The Game Loop
Relevant source files
The following files were used as context for generating this wiki page:
The Game Loop is the core engine of the Kilo Man platformer, responsible for synchronizing input handling, physics calculations, entity AI, and visual rendering. It operates as a continuous cycle driven by the browser's animation frames, ensuring a smooth 60 FPS experience by separating mutable state logic from the React rendering cycle through the use of useRef.
The loop is primarily encapsulated within the GameCanvas component, which manages the lifecycle of a single game session. It orchestrates the flow of data between the player's inputs, the static level environment defined in levelData.ts, and dynamic entities like monsters.
Sources: app/components/Game/GameCanvas.tsx:431-447, architecture_plan.md:7-15
Architecture and Execution Flow
The game loop follows a standard Update-Draw pattern. The loop function is initiated via requestAnimationFrame and recursively schedules itself to maintain execution.
The diagram above illustrates the sequential execution of logic and rendering within a single frame of the game loop.
Sources: app/components/Game/GameCanvas.tsx:431-447
State Management
To avoid React's re-render overhead during high-frequency updates, the game loop utilizes useRef for mutable game state. This includes player coordinates, velocity, and monster positions. React state (useState) is only used for high-level status changes such as switching between 'playing', 'won', or 'lost' states.
| Data Structure | Source | Purpose |
|---|---|---|
playerRef | PlayerState | Stores x/y position, velocity (vx/vy), and orientation. |
monstersRef | MonsterState[] | Manages an array of active monster entities and their patrol bounds. |
cameraRef | {x, y} | Stores the current viewport offset for scrolling logic. |
keysRef | { [key: string]: boolean } | Tracks the current state of keyboard inputs. |
Sources: app/components/Game/GameCanvas.tsx:43-70, app/components/Game/types.ts:16-43
Physics and Update Logic
The update function is called at the start of every frame. It processes three primary logic branches: kinematics, AI, and collision.
Player Kinematics
The loop applies horizontal acceleration based on keyboard input, followed by a friction coefficient to simulate momentum. Vertical movement is governed by a gravity constant applied every frame.
- Gravity:
0.6 - Friction:
0.85 - Move Speed:
0.8 - Max Speed:
8.0
Sources: app/components/Game/GameCanvas.tsx:34-41, app/components/Game/GameCanvas.tsx:298-315
Monster AI and Patrols
Monsters update their positions independently of the player. They move at a constant speed and reverse direction when they reach their patrolStart or patrolEnd boundaries defined in the level data.
Collision Detection
The loop performs AABB (Axis-Aligned Bounding Box) collision detection between the player and level entities.
- Hazards/Monsters: Trigger a state change to
lost. - Goal: Trigger a state change to
won. - Platforms: Calculate the overlap on X and Y axes. The loop resolves the collision by snapping the player to the edge of the platform and zeroing the relevant velocity component.
Sources: app/components/Game/GameCanvas.tsx:356-388
Rendering Engine
The draw function clears the viewport and renders layers using a camera-relative coordinate system: screenX = worldX - cameraX.
Camera System
The camera follows the player with a "trailing" effect. It centers on the player's X position while clamping to the levelWidth (7000px). The Y-axis follows the player only when they exceed a specific "deadzone," preventing jitter during small jumps.
The camera uses linear interpolation (Lerp) to provide smooth motion as the player moves through the stage.
Sources: app/components/Game/GameCanvas.tsx:399-430
Parallax Layers
The loop renders multiple background layers at varying speeds relative to the camera to simulate depth:
- Sky/Stars: Moves at
cameraX * 0.1 - Far Mountains: Moves at
cameraX * 0.2 - Near Hills: Moves at
cameraX * 0.5
Sources: app/components/Game/GameCanvas.tsx:135-165, architecture_plan.md:53-57
Game Lifecycle Management
The GameContainer component wraps the loop and manages the transition between game states.
Completion Scoring
When the loop exits due to a "won" state, the system calculates a score based on elapsed time. This is tracked using startTimeRef within the GameContainer.
- Base Points: 10,000
- Penalty: 100 points per second
- Minimum: 1,000 points
Sources: app/components/Game/GameContainer.tsx:16-24
Summary
The Game Loop in Kilo Man effectively decouples high-frequency physics and rendering from React's stateful nature. By utilizing useRef for frame-by-frame calculations and requestAnimationFrame for timing, it achieves a performant environment for complex collision handling, smooth camera movement, and multi-layered parallax effects.