Skip to main content

Scoring Demo

This example demonstrates the scoring system in helios, including score pools, score observers, UI text rendering, state management, and integration with the combat/health/spawn mechanics.

Features

  • Score Pool Management - Centralized score tracking with ScorePoolManager
  • Score Attribution - Kill scoring via LastAttackerComponent and ScorePoolComponent
  • UI Text Rendering - Dynamic score display with OpenGLGlyphTextRenderer and TextMesh
  • Score Observers - Data binding between score pools and UI components
  • Health & Damage System - Enemy health tracking with HealthComponent
  • Game / Match State Management - Hierarchical state machine with GameState and MatchState
  • Menu System - Title screen, pause menu, and game over menu with gamepad navigation
  • SpawnSystemFactory DSL - Declarative spawn configuration with fluent builder API
  • Multiple Viewports - Separate viewports for game world, HUD, title screen, and menus
  • Complete Twin-Stick Gameplay - Player movement, aiming, shooting, and scoring

Building

cmake -S . -B build
cmake --build build --target scoring_demomain

Running

./build/examples/scoring_demo/main

Controls

InputAction
Left StickMove spaceship
Right StickAim and fire projectiles
D-Pad Up/DownNavigate menus
A ButtonConfirm menu selection
ESCExit application
~ (Tilde)Toggle ImGui overlay

Architecture

Scoring System Overview

┌─────────────────────────────────────────────────────────────────────┐
│ SCORING ARCHITECTURE │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ ENEMY DEFEATED │
│ ┌─────────────────┐ │
│ │ HealthComponent │ → health <= 0 │
│ │ ScoreValueComp. │ → points = 100 │
│ │ LastAttackerCmp │ → attacker = player │
│ └────────┬────────┘ │
│ │ │
│ ▼ │
│ ATTACKER LOOKUP │
│ ┌─────────────────┐ │
│ │ Player Entity │ │
│ │ ScorePoolComp. │ → poolId = "playerOneScorePool" │
│ └────────┬────────┘ │
│ │ │
│ ▼ │
│ SCORE POOL UPDATE │
│ ┌─────────────────┐ │
│ │ ScorePoolManager│ → addScore(poolId, 100) │
│ │ ScorePool │ → totalScore += 100, revision++ │
│ └────────┬────────┘ │
│ │ │
│ ▼ │
│ UI UPDATE │
│ ┌─────────────────┐ │
│ │ScoreObserverCmp │ → detects revision change │
│ │TextMeshComponent│ → updates displayed text │
│ └─────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘

Code Structure

FilePurpose
main.cppApplication entry point with game loop and phase configuration
_module.ixxModule aggregation
ArenaConfig.ixxArena constants (grid size, coordinates, cell length)
IdConfig.ixxCentralized typed identifiers (pool, prefab, spawn, collision IDs)
CollisionId.ixxCollision layer identifiers
EnemyPrefabs.ixxEnemy prefab definitions (purple, orange, blue)
SpawnConfiguration.ixxSpawnSystemFactory DSL configuration for all spawn pools
MenuConfiguration.ixxTitle, pause, and game over menu setup
GameStateListener.ixxGame state transition listeners
MatchStateListener.ixxMatch state transition listeners
UiActionCommandPolicy.ixxUI action command handling
SpaceshipWidget.ixxImGui widget for physics and fire rate tuning

Key Components

ComponentPurpose
ScorePoolComponentAssociates player with a score pool
ScoreValueComponentPoints awarded when entity is defeated
ScoreObserverComponentObserves a score pool for UI updates
LastAttackerComponentTracks who dealt the killing blow
HealthComponentEntity health and damage tracking
MatchStateComponentPer-entity match state tracking
MenuComponentMenu state and item configuration
UiTextComponentText rendering in UI context

Key Systems

SystemPurpose
HealthDepletedSystemDetects entity death, triggers score award
ScoreObserverSystemUpdates observers when pools change
DamageApplicationSystemApplies collision damage
GameFlowSystemManages game state transitions
MatchFlowSystemManages match state transitions
MenuDisplaySystemState-driven menu visibility
MenuNavigationSystemGamepad-based menu navigation
StateToViewportPolicyUpdateSystemActivates viewports based on state
SceneRenderingSystemRenders scenes to their associated viewports

Spawn System Configuration (DSL)

SpawnSystemFactory::configure(poolManager, spawnManager)
.pool(ProjectilePoolId, ProjectilePrefabId, 50)
.profile(ProjectileSpawnProfileId)
.emitterPlacement()
.done()
.commit()

.pool(PurpleEnemyPoolId, PurpleEnemyPrefabId, 50)
.profile(RandomSpawnProfileId)
.randomPlacement()
.randomDirectionInitializer()
.scheduledBy(PurpleSpawnRuleId)
.timerCondition(5.0f)
.fixedAmount(1)
.done()
.done()
.commit()

.pool(OrangeEnemyPoolId, OrangeEnemyPrefabId, OBJECT_AMOUNT_X)
.profile(LeftColumnProfileId)
.axisPlacement(vec3f(0, -1, 0).normalize(), TOP_LEFT)
.moveInitializer(X_AXISf)
.scheduledBy(LeftColumnRuleId)
.timerWithAvailabilityCondition(15.0f)
.fixedAmount(OBJECT_AMOUNT_Y)
.done()
.done()
// ... additional profiles for other edges
.commitCyclic<4>();

GameLoop Phase Configuration

// Pre-Phase: Input, Spawning, Physics
gameLoop.phase(PhaseType::Pre)
.addPass<GameState>(GameState::Running)
.addSystem<TwinStickInputSystem>(shipGameObject)
.addCommitPoint(CommitPoint::Structural)
.addPass<GameState>(GameState::Running)
.addSystem<GameObjectSpawnSystem>(spawnManager)
.addSystem<ProjectileSpawnSystem>(ProjectileSpawnProfileId)
.addCommitPoint(CommitPoint::Structural);

// Main-Phase: Collision Detection and Response
gameLoop.phase(PhaseType::Main)
.addPass<GameState>(GameState::Running)
.addSystem<GridCollisionDetectionSystem>(bounds, cellSize)
.addCommitPoint();

// Post-Phase: Transform, Rendering, UI
gameLoop.phase(PhaseType::Post)
.addPass<GameState>(GameState::Any)
.addSystem<ComposeTransformSystem>()
.addSystem<StateToViewportPolicyUpdateSystem<GameState, MatchState>>(stateToViewportMap)
.addSystem<SceneRenderingSystem>(renderingDevice, sceneToViewportMap)
.addSystem<TransformClearSystem>();

State Management

The demo uses a hierarchical state machine:

  • GameState: UndefinedStartRunningPaused / GameOver
  • MatchState: UndefinedWaitingForPlayersPlayingGameOver

State transitions drive viewport visibility, system execution, and menu display.

See Also