This is a small 2D arcade game with a top-down view, designed for network play with other players. Each player controls a tank. The objective is to destroy the tanks of other players and survive. To assist players, special bonus boxes periodically appear on the map: three types of boxes for changing tank equipment and one repair kit box.
Equipment-changing boxes allow players to acquire a new hull, weapon, or type of projectile. This system creates a large number of combinations, each of which can be strategically utilized depending on the situation.
- W/S - Move forward/backward
- A/D - Rotate the tank around its axis
- Left Mouse Button - Fire the tank's weapon in the direction of the cursor
- 1/2/3/4 - Block pickup of boxes (hull/weapon/projectile/repair kit)
- F2 - Display tank information
- F3 - Display debug information
- N - Commit suicide
- T/G/B - Change hull, weapon, or projectile type, respectively
- H/F - Restore 40% or 100% health, respectively
- V - Maximize vampirism effect
This project is developed in Java 17 using the LWJGL3 library for OpenGL 3.3 functionality and LeGUI for UI management.
We use a custom engine, which was maintained in a separate repository until version v4.2.0. The project combines component-oriented programming for game object development and service-oriented programming for engine functionality.
The engine employs the PicoContainers library for dependency injection and inversion of control.
At startup, the engine scans specified packages for annotated classes (EngineService
, GameService
, TestService
) and adds them to the context.
The application context is divided into groups based on the parent thread, with each group having unique service visibility, simplifying parallelized testing of multiple game clients.
Using PicoContainers and a custom annotation system, test services can replace standard ones, such as rendering or texture services. This enables headless testing, ideal for environments like Jenkins. Services can also be configured dynamically using ProfilesService
with environment variables.
Game world updates (logic) and rendering are executed sequentially. Object interpolation for rendering without updating the game state is avoided. Each world update passes the elapsed nanoseconds since the previous update to objects, ensuring consistent state synchronization across clients with varying frame rates.
Vertical synchronization (VSync) is used to cap the frame rate, adjustable via a VSync divisor in the settings (0 = uncapped, 1 = monitor refresh rate, 2 = half refresh rate, etc.). On systems lacking VSync support, frame rate limits can be enforced by putting the game thread to sleep.
The game world is divided into locations, with only one active at a time. Active locations are further subdivided into chunks, optimizing updates and rendering. Chunks outside the screen are updated only when active objects are present.
Configuration files are categorized as internal (packaged in the jar) and external (stored in the root directory). External configurations override internal defaults, allowing user-specific settings like graphics and audio. All configurations are in JSON format.
Game objects inherit from a common base class with update
and render
methods. Since version 2.0.0, a component-oriented architecture has replaced a complex inheritance hierarchy. Components implement interfaces like Updatable
, Drawable
, and Collidable
, enabling modular behavior.
The server processes client messages containing an ID and corresponding data. Clients establish both TCP and UDP connections. TCP is optimized for low latency, while UDP handles non-critical but time-sensitive data (e.g., player positions).
The engine functions as a framework with inversion of control. To initialize, implement required interfaces and pass them to OrchEngine#start
. Game logic is customized via GameInterface
methods (init
, update
, render
), game object behavior, or annotated GameService
classes.
Equipment (armor, weapons, projectiles) is configured via JSON files. Mechanics and properties (e.g., speed, damage, fragmentation) are defined within these files. The system dynamically associates mechanics with classes using reflection, streamlining the addition of new equipment.
The effects system manages changes to player attributes from equipment or events. Effects are additive or percentage-based and are calculated sequentially for clarity. This modular design decouples attribute calculations from specific mechanics.
Integration tests validate game functionality using multiple simultaneous game clients. The PicoContainers library ensures isolated service contexts for each client, enabling robust testing of multiplayer mechanics.
Each commit triggers Jenkins to build the project for multiple platforms (Windows/Linux/MacOS, including ARM variants), run tests, and publish builds on our website.