Skip to main content

Movement System Architecture

Comparative analysis of movement patterns for game entities.

Executive Summary

After evaluating four movement prototypes, we selected a hybrid multi-system architecture combining:

  1. Linear Movement - Critical (bullets, simple enemies)
  2. Sine Wave Movement - High priority (classic R-Type patterns)
  3. Scripted Movement - High priority (bosses, complex behaviors)
  4. Bezier Curves - Optional (cinematics, polish)

Movement Systems Comparison

SystemPriorityComplexityPerformanceUse Case
LinearCriticalLowExcellentProjectiles
Sine WaveHighLowExcellentEnemy patterns
ScriptedHighMediumGoodBoss behaviors
BezierOptionalHighModerateCinematics

System 1: Linear Movement

Priority: Critical

Timeline: Sprint 1

Description

Foundation for all projectile-based gameplay. Simple velocity-based movement.

Use Cases

  • All bullet types
  • Projectiles
  • Particle effects
  • Simple enemy charges
  • Background elements

Implementation

struct Velocity {
float dx, dy; // Direction * Speed combined
};

void LinearMovementSystem::update(Registry& registry, float dt) {
for (auto [entity, pos, vel] : registry.view<Position, Velocity>()) {
pos.x += vel.dx * dt;
pos.y += vel.dy * dt;
}
}

Performance

MetricValue
ComplexityO(n)
Operations per entity4 multiplications, 2 additions
Cache efficiencyExcellent
Memory per entity8 bytes (2 floats)

Rationale

  • Highest performance characteristics
  • Simple, predictable, reliable
  • Essential for core gameplay

System 2: Sine Wave Movement

Priority: High

Timeline: Sprint 1

Description

Creates the classic R-Type aesthetic with oscillating movement patterns.

Use Cases

  • Wave formation enemies
  • Floating power-ups
  • Background animations
  • Enemy patrol patterns
  • Classic shooter feel

Implementation

struct SineWave {
float centerY;
float frequency;
float amplitude;
float phase;
float time;
};

void SineWaveMovementSystem::update(Registry& registry, float dt) {
for (auto [entity, pos, wave] : registry.view<Position, SineWave>()) {
wave.time += dt;
pos.y = wave.centerY + wave.amplitude * std::sin(wave.frequency * wave.time + wave.phase);
}
}

Configuration Examples

PatternFrequencyAmplitudeEffect
Gentle float1.020Power-ups
Standard wave2.040Normal enemies
Aggressive4.060Fast enemies
Tight wobble6.010Nervous movement

Performance

MetricValue
ComplexityO(n)
Operations1 sin(), 3 multiplications, 2 additions
Cache efficiencyGood
Memory per entity20 bytes (5 floats)

Rationale

  • Classic R-Type aesthetic
  • Excellent performance-to-visual ratio
  • Simple to tune and adjust

System 3: Scripted Movement

Priority: High

Timeline: Sprint 2

Description

Data-driven movement system using command sequences for complex patterns.

Use Cases

  • Boss movement patterns
  • Multi-phase enemy behaviors
  • Cutscene movements
  • Tutorial sequences
  • Complex attack choreography

Implementation

struct MovementScript {
std::vector<std::unique_ptr<ICommand>> commands;
size_t currentIndex;
};

// Command interface
class ICommand {
public:
virtual bool execute(Position& pos, float dt) = 0; // Returns true when done
virtual void reset() = 0;
};

// Example: Move to point
class MoveToCommand : public ICommand {
Vec2 target;
float speed;
public:
bool execute(Position& pos, float dt) override {
Vec2 direction = normalize(target - Vec2{pos.x, pos.y});
pos.x += direction.x * speed * dt;
pos.y += direction.y * speed * dt;
return distanceTo(target) < EPSILON;
}
};

Script Example (JSON)

{
"name": "boss_phase1",
"commands": [
{ "type": "moveTo", "x": 800, "y": 300, "speed": 200 },
{ "type": "wait", "duration": 1.0 },
{ "type": "moveTo", "x": 600, "y": 100, "speed": 300 },
{ "type": "wait", "duration": 0.5 },
{ "type": "loop", "count": 3, "commands": [
{ "type": "moveTo", "x": 700, "y": 200, "speed": 400 },
{ "type": "moveTo", "x": 500, "y": 400, "speed": 400 }
]}
]
}

Available Commands

CommandParametersDescription
moveTox, y, speedMove to position
waitdurationPause movement
loopcount, commandsRepeat sequence
setVelocitydx, dyDirect velocity
accelerateax, ay, durationGradual speed change

Rationale

  • Empowers designers to create content
  • Rapid prototyping and iteration
  • Data-driven for maintainability

System 4: Bezier Curves (Optional)

Priority: Medium

Timeline: Sprint 3-4 (Polish Phase)

Description

Smooth curved paths using cubic Bezier curves for cinematic movement.

Use Cases

  • Boss entrance/exit cinematics
  • Dramatic enemy entrances
  • Key story moments
  • Visual polish

Implementation

struct BezierPath {
Vec2 p0, p1, p2, p3; // Control points
float t; // Progress [0, 1]
float duration;
};

Vec2 cubicBezier(const BezierPath& path, float t) {
float u = 1.0f - t;
float tt = t * t;
float uu = u * u;
float uuu = uu * u;
float ttt = tt * t;

return uuu * path.p0 +
3 * uu * t * path.p1 +
3 * u * tt * path.p2 +
ttt * path.p3;
}

Decision Criteria

Implement only if:

  • Performance budget allows
  • Visual editor tool available
  • Art direction requires curves
  • Core gameplay is complete

Performance Consideration

MetricBezierLinear
Operations~20~4
ComplexityMediumSimple
AuthoringComplexEasy

System Execution Order

void GameLoop::updateMovement(float deltaTime) {
// 1. Base movement (Linear)
linearMovementSystem.update(registry, deltaTime);

// 2. Modifiers (Sine Wave)
sineWaveMovementSystem.update(registry, deltaTime);

// 3. Scripted overrides
scriptedMovementSystem.update(registry, deltaTime);

// 4. Optional (Bezier) - Only if implemented
// bezierMovementSystem.update(registry, deltaTime);
}

Component Design

namespace RType::Movement {
// Shared base
struct Position {
float x, y;
};

// Linear movement
struct Velocity {
float dx, dy;
};

// Sine wave movement
struct SineWave {
float centerY;
float frequency;
float amplitude;
float phase;
float time;
};

// Scripted movement
struct MovementScript {
std::vector<std::unique_ptr<ICommand>> commands;
size_t currentIndex;
};
}

Final Decision Summary

SystemStatusSprintRationale
LinearApproved1Foundation for projectiles
Sine WaveApproved1Classic R-Type aesthetic
ScriptedApproved2Designer empowerment
BezierDeferred3-4Polish phase only

Implementation Order

  1. Sprint 1: Linear + Sine Wave (core gameplay)
  2. Sprint 2: Scripted (boss patterns)
  3. Sprint 3-4: Bezier (if time permits)

References

  • PoC implementations: /PoC/PoC_Movement/
  • Linear Movement: /PoC/PoC_Movement/LinearMovement/
  • Sine Wave: /PoC/PoC_Movement/SineWaveMovement/
  • Scripted: /PoC/PoC_Movement/ScriptedMovement/
  • Bezier: /PoC/PoC_Movement/BezierMovement/
  • Decision document: /PoC/PoC_Movement/movement_system_decision.md