Repository navigation
Gameplay Systems
SparkEngine includes a comprehensive set of gameplay systems built on top of the Entity Component System architecture. Beyond the FPS-focused player controller and weapon system, the engine provides: achievement tracking, ability/condition systems, event response system (data-driven When/If/Then rules), branching dialogue, destructible objects, replay record/playback, inventory and quests, day/night cycle and weather, tween/coroutine systems, and accessibility (5 colorblind modes, subtitles, reduced motion, one-handed input).
Source: SparkEngine/Source/Game/, SparkEngine/Source/Engine/Gameplay/, SparkEngine/Source/Engine/ECS/Components/
The engine quest implementation separates runtime progression and rewards
(QuestSystem.cpp) from save-state capture, validation, and transactional restore
(QuestPersistence.cpp). Both implement the same QuestSystem interface; the
split does not introduce a second quest registry or change the saved-state format.
For governance on when gameplay behavior must extend engine systems (Quest/Dialogue) versus when module forks are acceptable (specialized Loot/Skill/Inventory domains), see:
docs/architecture/gameplay-extension-policy.md
The FPS player controller provides standard first-person movement and interaction:
- Movement -- WASD movement with configurable speed
- Mouse look -- Pitch/yaw camera control with sensitivity settings
- Jump -- Space bar with configurable jump height and gravity
- Crouch -- Ctrl with height adjustment
- Sprint -- Shift for increased movement speed
Source: SparkEngine/Source/Engine/Gameplay/WeaponManager.h
The weapon system is a data-driven, component-based architecture consisting of:
-
WeaponDefinition-- Shared template data for a weapon type -
WeaponInstance-- Per-entity runtime state (ammo, recoil) -
WeaponInventoryComponent-- ECS component attached to weapon-carrying entities -
WeaponRegistry-- Singleton registry of all weapon definitions -
WeaponSystem-- ECS system that processes weapon behavior each frame
enum class FireMode : uint8_t
{
SemiAuto, // One shot per trigger pull
Burst, // Fixed burst (e.g., 3-round burst)
FullAuto // Continuous fire while trigger held
};enum class WeaponState : uint8_t
{
Idle, // Ready to fire
Firing, // Currently firing (cooldown active)
Reloading, // Magazine swap in progress
Switching, // Equip/holster transition
Empty, // Magazine empty, needs reload
Disabled // Cannot fire (ability disabled, etc.)
};enum class WeaponSlot : uint8_t
{
Primary = 0, // Assault rifles, SMGs
Secondary = 1, // Pistols
Melee = 2, // Knives, melee weapons
Grenade = 3, // Grenades, throwables
MaxSlots = 4
};The WeaponDefinition struct is the data template for a weapon type. It is registered once and shared across all instances:
struct WeaponDefinition
{
uint32_t definitionID; // Unique weapon type ID
std::string name; // Display name ("AK-47")
WeaponSlot slot; // Inventory slot
// Damage
float baseDamage = 25.0f; // Per-projectile damage
float headshotMultiplier = 2.0f; // Headshot bonus
float armorPenetration = 0.0f; // 0.0 = none, 1.0 = full
// Fire
FireMode fireMode = FireMode::FullAuto;
float fireRate = 600.0f; // Rounds per minute
int burstCount = 3; // Rounds per burst
float muzzleVelocity = 900.0f; // m/s (0 = hitscan)
bool isHitscan = true; // Raycast vs ballistic
// Ammo
int magazineSize = 30;
int maxReserveAmmo = 120;
float reloadTime = 2.5f; // Seconds
float tacticalReloadTime = 2.0f; // With round chambered
// Handling
float equipTime = 0.5f; // Draw time
float holsterTime = 0.3f; // Put-away time
float adsTime = 0.2f; // ADS transition
float adsFOV = 50.0f; // FOV when ADS
float moveSpeedMultiplier = 1.0f; // Speed penalty
// Recoil & Spread
RecoilPattern recoil;
SpreadConfig spread;
// Helpers
float GetShotInterval() const; // 60 / fireRate
float GetDPS() const; // baseDamage * (fireRate / 60)
};struct RecoilPattern
{
float verticalPerShot = 0.5f; // Degrees upward per shot
float horizontalPerShot = 0.1f; // Max degrees horizontal
float recoverySpeed = 5.0f; // Degrees/sec recovery
float maxVertical = 10.0f; // Max accumulated vertical
float maxHorizontal = 3.0f; // Max accumulated horizontal
float firstShotMultiplier = 1.5f; // First shot bonus
};struct SpreadConfig
{
float baseSpread = 1.0f; // Standing still (degrees)
float moveSpread = 3.0f; // Additional while moving
float crouchReduction = 0.5f; // Multiplier when crouching
float adsReduction = 0.3f; // Multiplier when ADS
float sprintPenalty = 2.0f; // Additional from sprinting
float maxSpread = 15.0f; // Absolute maximum
};auto& registry = Spark::Gameplay::WeaponRegistry::GetInstance();
// Register default built-in weapons
registry.RegisterDefaults();
// Register a custom weapon
Spark::Gameplay::WeaponDefinition rifle;
rifle.name = "Assault Rifle";
rifle.slot = Spark::Gameplay::WeaponSlot::Primary;
rifle.fireMode = Spark::Gameplay::FireMode::FullAuto;
rifle.fireRate = 700.0f;
rifle.baseDamage = 28.0f;
rifle.magazineSize = 30;
rifle.maxReserveAmmo = 150;
rifle.reloadTime = 2.2f;
rifle.isHitscan = true;
rifle.recoil.verticalPerShot = 0.6f;
rifle.spread.baseSpread = 1.2f;
uint32_t rifleID = registry.RegisterWeapon(rifle);auto& inv = world.AddComponent<Spark::Gameplay::WeaponInventoryComponent>(player);
auto& primary = inv.weapons[static_cast<size_t>(Spark::Gameplay::WeaponSlot::Primary)];
primary.definitionID = rifleID;
primary.currentAmmo = 30;
primary.reserveAmmo = 120;
primary.state = Spark::Gameplay::WeaponState::Idle;When a weapon fires, the WeaponSystem emits a WeaponFireEvent:
struct WeaponFireEvent
{
uint32_t ownerEntity; // Entity that fired
uint32_t weaponDefID; // Weapon definition ID
float damage; // Computed damage
float spreadAngle; // Computed spread (degrees)
bool isHitscan; // Raycast or ballistic
float muzzleVelocity; // Projectile speed
};Subscribe to fire events for projectile spawning, audio, and VFX:
weaponSystem.OnFire([&](const Spark::Gameplay::WeaponFireEvent& e) {
if (e.isHitscan)
SpawnHitscanTrace(e);
else
SpawnProjectile(e);
audio.PlaySound("gunshot");
SpawnMuzzleFlash(e.ownerEntity);
});Source: SparkEngine/Source/Engine/ECS/Components/FPSComponents.h
The ProjectileComponent tracks in-flight projectiles:
| Type | Description |
|---|---|
Hitscan |
Instant ray-cast; resolves on the frame it's fired |
Ballistic |
Physics-based arc with gravity |
| Behavior | Description |
|---|---|
Destroy |
Destroy on first impact |
Bounce |
Ricochet off surfaces |
Pierce |
Pass through targets |
Stick |
Attach to the hit surface/entity |
| Field | Type | Default | Description |
|---|---|---|---|
direction |
XMFLOAT3 |
{0,0,1} |
Travel direction (normalized) |
speed |
float |
100.0 |
Units per second |
gravityScale |
float |
1.0 |
Gravity multiplier |
damage |
float |
25.0 |
Base impact damage |
explosionRadius |
float |
0.0 |
Splash damage radius |
maxRange |
float |
500.0 |
Auto-destroy distance |
maxLifetime |
float |
10.0 |
Auto-destroy time |
bouncesRemaining |
int |
0 |
Ricochet count |
piercesRemaining |
int |
0 |
Penetration count |
ownerEntityId |
uint32_t |
0 |
Owner for friendly fire |
teamId |
int |
-1 |
Team for damage rules |
Source: SparkEngine/Source/Engine/ECS/Components/FPSComponents.h
The InteractionComponent marks entities as player-interactable:
| Type | Description |
|---|---|
Use |
Press 'E' to activate (doors, switches) |
Pickup |
Auto-collect or press to pick up (ammo, health) |
Hold |
Hold to interact (defuse, hack, revive) |
Toggle |
Toggle on/off (lights, generators) |
| State | Description |
|---|---|
Idle |
Ready for interaction |
Active |
Currently being held (Hold type) |
Cooldown |
Temporarily unavailable |
Disabled |
Cannot be interacted with |
Destroyed |
Permanently unusable |
| Field | Type | Default | Description |
|---|---|---|---|
displayName |
string |
"" |
Name shown in prompt |
actionVerb |
string |
"Use" |
Verb in prompt ("Open", "Pick up") |
interactionRadius |
float |
2.5 |
Max interaction distance |
holdDuration |
float |
0.0 |
Required hold time |
cooldownDuration |
float |
0.0 |
Post-use cooldown |
usesRemaining |
int |
-1 |
Uses left (-1 = unlimited) |
showHighlight |
bool |
true |
Show outline when in range |
requiredItemId |
string |
"" |
Required key/item |
onInteractEvent |
string |
"" |
Script event name |
The DecalComponent renders projected textures for bullet holes, blood, and scorch marks:
| Field | Type | Default | Description |
|---|---|---|---|
texturePath |
string |
"" |
Decal texture path |
category |
string |
"generic" |
Grouping category |
size |
XMFLOAT3 |
{0.1, 0.1, 0.05} |
Projection half-extents |
color |
XMFLOAT4 |
{1,1,1,1} |
Tint color (RGBA) |
lifetime |
float |
30.0 |
Seconds before removal (0 = permanent) |
fadeOutDuration |
float |
2.0 |
Fade-out time at end of life |
receiveLighting |
bool |
true |
Receive scene lighting |
sortOrder |
int |
0 |
Draw order for overlapping decals |
A player class system for multiplayer game modes with configurable:
- Starting weapons and equipment
- Health and armor values
- Movement speed modifiers
- Special abilities
Physics-driven vehicle mechanics:
- Enter/exit vehicles
- Steering and acceleration
- Suspension and physics simulation
Built-in HUD elements for FPS gameplay:
| Element | Description |
|---|---|
| Crosshair | Configurable weapon crosshair |
| Health bar | Player health display |
| Kill feed | Recent kill notifications |
| Minimap | Top-down area overview |
| Compass | Directional compass |
| Ammo counter | Current weapon ammunition |
The inventory system is compiled as part of the engine; there is no separate CMake toggle.
Item management system:
- Inventory slots with weight/capacity limits
- Item stacking
- Pickup and drop mechanics
- Equipment slots
auto& inv = world.AddComponent<InventoryComponent>(player);
inv.maxSlots = 20;
inv.maxWeight = 50.0f;
// Add items
inv.AddItem(ItemDef{ "healthpack", "Health Pack", 1.0f, true, 5 });
inv.AddItem(ItemDef{ "ammo_rifle", "Rifle Ammo", 0.1f, true, 60 });
// Check inventory
if (inv.HasItem("healthpack")) {
inv.RemoveItem("healthpack", 1);
player.health += 50.0f;
}
// Equipment slots
inv.Equip("primary_weapon", rifleItemId);
inv.Equip("armor", vestItemId);The quest system is compiled as part of the engine; there is no separate CMake toggle.
Objective tracking:
- Quest definitions with stages
- Objective tracking and completion
- Quest log and notifications
- Integration with
QuestCompletedEvent
QuestDefinition quest;
quest.id = 1;
quest.name = "Clear the Building";
quest.stages = {
{"Find the entrance", ObjectiveType::GoToLocation, {10.0f, 0.0f, 20.0f}},
{"Eliminate all enemies", ObjectiveType::KillCount, 5},
{"Collect the intel", ObjectiveType::CollectItem, "intel_docs"},
{"Return to base", ObjectiveType::GoToLocation, {0.0f, 0.0f, 0.0f}}
};
questSystem.RegisterQuest(quest);
questSystem.StartQuest(1);
// Update progress from event callbacks
bus.Subscribe<EntityKilledEvent>([&](const EntityKilledEvent& e) {
questSystem.UpdateKillCount(e.killerId);
});
if (questSystem.IsQuestComplete(1)) {
bus.Publish(QuestCompletedEvent{ playerId, 1, "Clear the Building" });
}Configurable game modes for multiplayer:
- Deathmatch
- Team Deathmatch
- Capture the Flag
- Domination
- Search and Destroy
- Free for All
- Custom modes
See Dedicated Server for GameModeType enum details.
Core gameplay component for damageable entities:
auto& hp = world.AddComponent<HealthComponent>(entity);
hp.maxHealth = 100.0f;
hp.health = 100.0f;
hp.isDead = false;
hp.isInvulnerable = false;Damage events are published through the event bus:
bus.Publish(EntityDamagedEvent{ entityId, damage, "Weapon" });Configurable gravity for physics-driven gameplay:
- Per-scene gravity settings (via
SceneMetadata, see Scene Management) - Gravity zones for localized effects
Objects that respond to player interaction:
- Doors (open/close)
- Buttons and switches
- Pickup items
auto& interact = world.AddComponent<InteractionComponent>(doorEntity);
interact.type = InteractionComponent::InteractionType::Use;
interact.displayName = "Door";
interact.actionVerb = "Open";
interact.interactionRadius = 2.5f;
interact.onInteractEvent = "OpenDoor";- Entity Component System -- Gameplay components
- Physics -- Physics-driven gameplay
- Event System -- Gameplay events
- AI and Navigation -- NPC behavior
- Networking -- Multiplayer game modes
- Dedicated Server -- Server game modes
- Scene Management -- Scene gravity and level loading
- Scripting with AngelScript -- Scripting gameplay logic
- Input System -- Player input handling
- Audio -- Gameplay sound effects
- Rendering and Graphics -- HUD and visual effects
- Animation -- Player and weapon animations
- Terrain and Procedural Generation -- Level environments
Published from 2b03dc797148. Edit the canonical source in wiki/.
- Documentation
- Docs route
- Wiki index
- Guides
- Tutorials
- Samples
- Examples
- API Reference
- API route
- Reference
- Build Guide
- Dependencies
- FAQ
- Changelog
- Roadmap
- Contributing
- Code of Conduct
- Home
- FAQ
- Getting Started
- Quick-Start Tutorial
- Making Your First Game
- Making Your First Multiplayer Game
- Artist Workflow Guide
- Editor Walkthrough
- Migration Guide
- How SparkEngine Works
- Architecture Overview
- Engine Architecture Flowchart
- Creating a Game Module
- Game Modules (catalog)
- Entity Component System
- Rendering and Graphics
- Physics
- Cloth Simulation
- Audio
- Input System
- Camera System
- Scripting with AngelScript
- Visual Scripting
- AI and Navigation
- Animation
- 2D Systems
- Networking
- Dedicated Server
- Multiplayer Quick Start
- Area Server Architecture
- Scene Management
- Large World Support
- Collaborative Editing
- Coroutine System
- Event System
- Event Response System
- Job System
- UI System
- UI Layout Extensions
- Localization
- Dialogue System
- Destruction System
- Replay System
- Achievement System
- Loading System
- Mod System
- Content Delivery
- Tween System
- Memory Integrity
- Gameplay Systems
- Terrain and Procedural Generation
- Save System
- Persistence System
- Day Night Cycle and Weather
- Cinematic Sequencer
- Runtime Prefabs
- SparkEditor
- Editor Tutorials
- SparkConsole
- SparkDaemon
- Shader Pipeline
- Asset Pipeline
- Asset Validation
- Asset Migration
- Game Packaging
- Online Services
- DataTable System
- Loot and Crafting System
- CSG System
- Font System
- Timer Manager
- Movie Render Pipeline
- HLOD and World Partition
- Remote Debug System
- Selection Manager
- Asset Dependency Graph
- Editor Automation
- File Watcher
- Project Templates
- System Requirements
- VR Support
- Mobile Platform
- Accessibility
- Platform Input
- Platform Certification
- Cross-Compilation: Wine Testing
- RHI Abstraction Layer
- D3D11 Backend
- D3D12 Backend
- Vulkan Backend
- OpenGL Backend
- Metal Backend
- DXR Raytracing
- Hybrid Ray Tracing
- Upscaling (DLSS/FSR)
- Render Graph
- Shader Graph
- GPU Particles
- GPU-Driven Rendering
- Volumetric Fog
- Volumetric Clouds
- Global Illumination
- Virtual Texturing
- Water Rendering
- Clustered Lighting
- Material System
- Post-Processing
- Shadow System
- Particle System
- Decal System
- Sky and Atmosphere
- Foliage System
- Mesh Shaders
- Neural Rendering
- Configuration Reference
- Performance Tips
- Benchmark Framework
- Threading Model
- Fuzz Policy and Parser Security
- Memory Safety
- Memory Management Patterns
- Build System and CMake Modules
- Profiler and Debugging
- Performance Profiling Guide
- Telemetry System
- Crash Reporting
- Golden Image Testing
- Utilities
- Testing
- Fuzz Policy and Parser Security
- Codebase Statistics
- Codebase Health
- Error Handling Patterns
- Hot Reload Overview
- Troubleshooting
- Contributing
- Workflow Patterns
- Build Optimizations
- CI Reproducible Builds
- GitHub API and PR Checks
- Git Rebase Conflicts
- Clang-Format
- Code Quality Violations
- AI Bloat Pattern
- MinGW + Wine Cross-Compilation
- Live Editor Testing
- Engine & Renderer Landscape
- DuetOS Portability Catalog
- Five-Engine Analysis
- Eleven-Engine Analysis
- ThorVG / Unity Graphics Analysis
- Advanced Techniques Catalog
- Third-Party Library Evaluation
- Engine Viability Evaluation
- Engine Feature Recommendations
- Project Recommendations
- Mac Compatibility Analysis
- Codebase Observations
- Codebase Bloat Audit
- Test Suite Audit
- Documentation Coverage Audit
- ThirdParty Dependencies Audit
- Load Test Baseline
- Gameplay Systems Status
- SparkGame Module Status
- Stub and Abandoned Features
- Memory Integrity System
- Memory Safety Evaluation
- Hardware Acceleration Systems
- Jolt Physics Integration
- GPU/CPU Separation Plan
- Daemon Services Architecture
- Reflection & Polymorphism Refactoring Plan
- SparkBuild In-Tree
- Wine No-JobSystem Breakthrough
- Wine Role and Fallback Tiers