
REFACTORED
Roles
Designer
Programmer
Team Size
Solo
Tools
Unreal Engine 5
UE C++
Blueprints
Visual Studio
GitHub
Production Time
June 2025 - Ongoing
Genre
First Person Shooter
Platform
Windows
Asset Packs
Battleseries: Refactored takes the original Battleseries and refactors it into a far more optimal, modular, and flexible system for expansion into a far more comprehensive product.
​
My main objectives have been taking the original mechanics and systems of Battleseries and making them more data-driven, modular, and optimized using new means, methods, and skillsets I've picked up such as C++, Data-Oriented Design, and Entity-Component Systems. Whereas the first Battleseries focused on individual systems and mechanics, this iteration focuses on putting it all together in a well-structured architecture as well as introduce new systems and mechanics that were not included in the original Battleseries that take advantage of such things.
The hopeful end result will be refined vehicle mechanics and additions such as on-foot combat to make for a true combined arms experience.
​
Data-Driven Vehicles
Vision
In the original Battleseries, vehicles were objects that relied on deep vehicle inheritance chains, with logic split across multiple subclasses and components. While this architecture did prove successful to a certain extent, it was clear that over-abstraction and managing of state on different vehicles was becoming harder to maintaining as updating one vehicle often meant updating several and any new system to be added to vehicles would only add to the increasingly fragile hierarchy.
For the refactor, I wanted a data-oriented, composition-driven approach where a single base class constructs the vehicle at runtime.
Implementation
Every vehicle is now defined entirely in a struct container, which defines the many different characteristics of a vehicle such as name, movement type, role, mesh, physics, animation, seats, turrets, and much more.
Result
This removed subclass bloat, centralized state, and made adding new vehicles as simple as adding a new data row. The architecture is now far more scalable, predictable, and easy to extend.
Data in Action
Architecture Layers
At a high level, The vehicle framework is built around a centralized, data-driven architecture where all systems, gameplay, UI, and editor tools, operate on the same authoritative data sources. Every vehicle is defined in data tables through structured rows that describe seats, attachment slots, movement settings, and other metadata. Runtime systems don’t hardcode anything; they “read” their behavior from these rows.
At the core are two Global API Subsystems:
-
DataSubsystem - serves all static, read-only configuration (vehicle definitions, seat layouts, weapon options, attachment rules).
-
SaveSubsystem - stores player-specific, instance-level state (loadout selections, current attachments per seat, etc.).
Vehicle Attachment Customization
Vision
With the advent of data-oriented design on multi-seated vehicles and other things such as weapons came the want to have extremely modular vehicles with the ability to swap out different attachments such as weapons, countermeasures, optics, upgrades anc camoflauges. Each seat could have its own set of options, and any number of attachments could be made available per vehicle type. The system also needed to support a robust, intuitive customization UI that allows players to configure vehicle loadouts easily.
Challenge
The main challenges arose from combining extreme configurability with vehicle-specific constraints:
1. How to define attachment slots in a way that clearly conveys context for both gameplay and UI.
2. How to ensure each attachment fits and functions correctly on a given vehicle, respecting seat-specific and type-specific rules.
​
Solution
To solve this, I designed a data-driven architecture that incorporates vehicle definitions and individual vehicle item instances. Vehicles define all possible attachment slots per seat and what options are available, allowing the UI to dynamically display valid choices. Each vehicle stores the actual equipped attachments, including mesh, camera, and firing behavior, in a way that supports per-seat and per-item slot customization. This approach ensures every vehicle can be uniquely configured while maintaining modularity and scalability without hardcoding behavior.
Vehicle Attachment Customization In Action
The Data Architecture Layer
The system is built around a clean architecture of high-level vehicle definitions and instance-specific data. Vehicles are defined by their type (tank, jet, attack helicopter, etc), what seats exist, and the roles of each seat (driver, gunner, hybrid of the 2 passenger). Since the vehicles are multiseated and the weapons and other items used are predominantly locked to an individual seat, available items are defined on a per seat basis. Each seat defines the the types of items available to it.
The instance-level layer for each item type answers the question "how does this item fit on this vehicle?". It stores an item's properties for a given vehicle, specifying mesh overrides, camera behavior, firing method, projectile mounting behavior, and decorative meshes. This separation allows multiple vehicles of the same type to exist with unique loadouts while still referencing a single type definition. attachment customization is.
The Customization UI Layer
The Customization UI is a thin, data-driven layer that sits on top of the vehicle system. Instead of containing gameplay logic, it pulls structured data from subsystems, reacts to user input, and pushes validated changes back through the proper systems.
Weapon Architecture
Vision
The objective was to create a Unified Weapon Framework that treats "Firepower" as a data-driven commodity, independent of the entity wielding it. I envisioned a system where a 120mm Tank Cannon and a 9mm Sidearm share the same DNA, defined by the same base data structure, while allowing their unique physical implementations to remain entirely decoupled.
Challenge
The system needed to support numerous types of settings such as fire modes (bolt-action, semi-auto, burst, and full-auto), different kinds of projectiles (hitscan, simulated ballistic projectiles, and pooled projectile actors). The 2 main contexts of weapons would be vehicle weapons and on-foot weapons. Logic-wise from a loadout, attachment, context, and data perspective, each had their own specific needs that required a different approach for each. However, both use the same underlying data.
Solution
To bridge the gap between shared data and specialized logic, I implemented a Hybrid Entity-Component-System that separates "What a weapon is" from "How a weapon behaves."
Weapon Architecture in Action
Vehicle Weapon ECS Architecture
Entites – Vehicles --> Seats --> Weapons
For vehicle weapons, the main entity in question is vehicles that can hold multiple weapons. Weapons are managed by the vehicle's seats which act as the sub-entities.
-
Each Seat Index acts acts as a unique identifier for a functional unit that can own weapons, turrets, and cameras.
-
The Vehicle Weapon System map (mapping Seat Index to a Weapon System) effectively treats each seat as an entity that possesses weapon-related components.
Component – Vehicle Weapon Logic Component
The Vehicle Weapon Logic Component serves as the bridge between the vehicle's physical existence and its combative capabilities.
-
What it Stores (Data): It holds the state of all weapon systems on the vehicle, including the Weapon System map, turret states, and static weapon data pointers for quick reference.
-
What it Provides: It provides the "Weapon Identity" to the vehicle, allowing it to have meshes, muzzle sockets, special cameras, and more, all defined by the vehicle weapon instance-level data.
Systems
The component acts as a system that provides the logic that operates on the data. The key systems include:
-
The Fire System: Processes the logic of turning a user input into a spawned or simulated projectile.
-
The Turret/Aim System: Operates on Turret States, it takes raw inputs applies constraints (min/max pitch/yaw) defined by the static vehicle data and updates the visual representation.
-
The Initialization/Assembly System: This is the "factory" logic. It looks at the different pieces of data (VehicleID, StartingData, saved loadout data, etc) and dynamically spawns the necessary sub-components to self-initialize and update accordingly to reflect an entering user's saved loadout choices.
-
The Rangefinder System: A per-tick system that performs raycasts from the active camera to determine distance and aim convergence for the UI and weapon alignment.
Projectile Architecture
Vision
With the many different weapons that could be fired at once, the goal was to build an optimized projectile framework capable of supporting hundreds (possibly thousands) of projectiles all in-flight at once to simulate real-time warfare. To support modern large-scale FPS mechanics, projectile flight needed to be efficient enough for hundreds of active rounds while still supporting complex behaviors like mounting, lock-on and other complex flight behavior, ballistics, and penetration behavior
Challenge
Traditional OOP actor-driven projectiles are expensive in large quantities, and rapidly become difficult to scale across many weapon types and vehicle seats. At the same time, relying purely on simulation structs makes advanced behaviors (attaching preloaded rockets, homing projectiles, camera-guided rounds, etc.) harder to manage. The system needed to unify multiple projectile behaviors under one architecture, cleanly separate data from runtime state, and avoid duplication across weapons, vehicles, and UI.
Solution
By separating configuration from behavior and state from execution, the system allows all weapons, infantry or vehicle, to share the same firing pipeline while still supporting highly specialized projectiles. The ECS-style simulated path is lightweight enough to support high-rate automatic weapons, while the pooled actor path handles complex projectiles with no GC pressure or spawn cost.
This unified approach results in:
-
Scalable projectile simulation (hundreds+ active rounds)
-
Minimal spawning overhead during gameplay
-
Consistent data access across UI, weapons, and vehicles
-
Support for mounted and preloaded projectiles
-
Clean extensibility driven entirely by data rows
In practice, adding a new projectile type is purely a data operation, and the architecture handles the rest.
Projectile Architecture in Action
Simulated Projectile ECS Architecture (High-Level)
Entity – Projectile
Each simulated projectile is represented as a lightweight entity ID rather than a full actor. The entity itself has no logic, it's simply an entry in the projectile subsystem’s simulation list.
Component – Runtime Projectile Data
All projectile properties that change during flight are stored in a dedicated runtime component struct (e.g., velocity, position, lifetime, gravity scale, drag, damage, penetration state, etc.).
This runtime data is stored in contiguous memory, allowing fast iteration and cache-friendly updates for hundreds of projectiles.
System – ProjectileSubsystem
A centralized Global API subsystem acts as the system in ECS terms. It owns all active entities and components. Every tick it runs the ballistic update pipeline (movement, gravity, traces, impact resolution, cleanup). Weapons simply request a new projectile entity and populate its runtime component; from that point on, the system fully controls simulation until impact or expiration.
Gameplay HUD Architecture
Vision
Born out of lessons and headaches of the first Battleseries, the goal in this iteration was to develop a Unified HUD Subsystem capable of handling diverse, context-specific interfaces, ranging from complex vehicle multi-functional displays (MFDs/HMDs) to minimal on-foot reticles, using a single, centralized logic controller. The vision was to create a "Stateless UI" where widgets act as pure visual observers of the underlying game state, ensuring that the UI remains decoupled from the physical actors it represents.
Challenge
The diverse range of HUD elements that would display any number of states in real-time as well as the modularity of layouts I wanted led to 3 main challenges:
-
Architecture: Bridging the gap between a Data-Oriented backend (ECS/DOD) and an inheritance-based, modularity-focused frontend (UMG).
-
Scalability: Handling unique contexts that require different elements while sharing the same data.
-
Retaining robust, event-based updates for high-frequency data like speed, ammo counts, and rangefinding.
Solution
I implemented a Hybrid Subsystem Architecture that uses a ULocalPlayerSubsystem to act as the traffic controller between game data and visual widgets relevant to that player.
-
Automated Component Binding (The "Factory" Pattern)​​ Instead of manually dragging and dropping references in the editor, the Self-Assembling HUD scans itself at runtime, automatically finding and binding pointers to specialized components like Rangefinder, Compass, or Speedometer. This allows designers to create new HUD layouts simply by adding widgets with specific names/types.
-
Event-Driven "Observer" Pattern To maximize performance, the HUD Subsystem treats the UI as an observer and binds directly to delegates in relevant classes, which allows for:
-
Reactive Updates: The UI only updates when the backend broadcasts a change.​
-
Data Routing: The Subsystem acts as a translator, taking raw entity data and mapping it to visual transforms (eg. compass).​
-

