VelocityComponent
Location: shared/include/components/VelocityComponent.hpp (planned)
The VelocityComponent represents an entity's linear velocity in the game world. It is used by the MovementSystem to update the entity's TransformComponent based on delta time.
Structure
struct VelocityComponent {
float vx = 0.0F; // Velocity on X-axis (units/second)
float vy = 0.0F; // Velocity on Y-axis (units/second)
};
Fields
| Field | Type | Default | Description |
|---|---|---|---|
vx |
float |
0.0F |
Horizontal velocity in units per second (positive = right) |
vy |
float |
0.0F |
Vertical velocity in units per second (positive = down) |
Invariants
- Finite values —
vxandvymust be finite (not NaN or infinite) - Units — Measured in world units per second
- Direction — Positive vx = right, positive vy = down (screen space)
Usage
Creating a Moving Entity
Registry registry;
EntityId bullet = registry.createEntity();
registry.emplace<TransformComponent>(bullet, TransformComponent{100.0F, 200.0F, 0.0F});
registry.emplace<VelocityComponent>(bullet, VelocityComponent{300.0F, 0.0F}); // 300 units/s to the right
Changing Velocity
auto& velocity = registry.get<VelocityComponent>(player);
// Accelerate right
velocity.vx += 50.0F;
// Stop vertical movement
velocity.vy = 0.0F;
Setting Velocity from Direction and Speed
auto& velocity = registry.get<VelocityComponent>(missile);
float speed = 400.0F; // units/second
float angle = M_PI / 4.0F; // 45 degrees
velocity.vx = speed * std::cos(angle);
velocity.vy = speed * std::sin(angle);
Integration with MovementSystem
The MovementSystem integrates velocity into position using Euler integration:
void MovementSystem::update(Registry& registry, float deltaTime) {
for (EntityId id : registry.view<TransformComponent, VelocityComponent>()) {
auto& transform = registry.get<TransformComponent>(id);
auto& velocity = registry.get<VelocityComponent>(id);
// Integrate position
transform.x += velocity.vx * deltaTime;
transform.y += velocity.vy * deltaTime;
}
}
Example:
* Velocity: (200, 0) units/s
* Delta time: 0.016s (60 FPS)
* Position change: (3.2, 0) units
Systems That Use Velocity
MovementSystem (Primary Consumer)
Updates position based on velocity and delta time.
PlayerSystem (Writer)
Sets velocity based on player input:
auto& velocity = registry.get<VelocityComponent>(playerId);
auto& input = registry.get<PlayerInputComponent>(playerId);
constexpr float PLAYER_SPEED = 200.0F;
velocity.vx = input.moveX * PLAYER_SPEED;
velocity.vy = input.moveY * PLAYER_SPEED;
MissileSystem (Writer)
Maintains constant velocity for projectiles:
auto& velocity = registry.get<VelocityComponent>(missileId);
auto& missile = registry.get<MissileComponent>(missileId);
velocity.vx = missile.speed * missile.dirX;
velocity.vy = missile.speed * missile.dirY;
MonsterSystem (Writer)
Applies AI movement patterns via MovementComponent (which may modify velocity).
Examples
Stationary Entity
Constant Velocity
// Enemy moving left at 100 units/s
registry.emplace<VelocityComponent>(enemy, VelocityComponent{-100.0F, 0.0F});
Diagonal Movement
// Missile moving at 45° downward-right at 300 units/s
float speed = 300.0F;
float angle = M_PI / 4.0F; // 45 degrees
registry.emplace<VelocityComponent>(missile, VelocityComponent{
speed * std::cos(angle), // vx = 212.1
speed * std::sin(angle) // vy = 212.1
});
Player WASD Movement
constexpr float PLAYER_SPEED = 250.0F;
// Input: W key pressed
velocity.vx = 0.0F;
velocity.vy = -PLAYER_SPEED; // Move up (negative Y)
// Input: D key pressed
velocity.vx = PLAYER_SPEED; // Move right
velocity.vy = 0.0F;
// Input: W + D keys pressed (diagonal)
velocity.vx = PLAYER_SPEED / std::sqrt(2.0F); // Normalize to maintain speed
velocity.vy = -PLAYER_SPEED / std::sqrt(2.0F);
Velocity Patterns
Stopping Gradually (Friction/Drag)
constexpr float DRAG = 0.95F; // 5% speed loss per frame
for (EntityId id : registry.view<VelocityComponent>()) {
auto& velocity = registry.get<VelocityComponent>(id);
velocity.vx *= DRAG;
velocity.vy *= DRAG;
// Stop completely when very slow
if (std::abs(velocity.vx) < 0.1F) velocity.vx = 0.0F;
if (std::abs(velocity.vy) < 0.1F) velocity.vy = 0.0F;
}
Clamping Max Speed
constexpr float MAX_SPEED = 500.0F;
auto& velocity = registry.get<VelocityComponent>(entity);
float speed = std::sqrt(velocity.vx * velocity.vx + velocity.vy * velocity.vy);
if (speed > MAX_SPEED) {
velocity.vx = (velocity.vx / speed) * MAX_SPEED;
velocity.vy = (velocity.vy / speed) * MAX_SPEED;
}
Bouncing Off Walls
// Hit left/right wall
if (transform.x <= 0 || transform.x >= WORLD_WIDTH) {
velocity.vx = -velocity.vx; // Reverse horizontal direction
}
// Hit top/bottom wall
if (transform.y <= 0 || transform.y >= WORLD_HEIGHT) {
velocity.vy = -velocity.vy; // Reverse vertical direction
}
Networking
Replication
- Not replicated by default — Clients can infer velocity from position deltas
- Optional — Can be sent for smoother client-side prediction
Client-Side Prediction
Client can store velocity locally:
// Client receives position update
clientTransform.x = serverTransform.x;
clientTransform.y = serverTransform.y;
// Estimate velocity from delta
float dt = timeSinceLastUpdate;
clientVelocity.vx = (serverTransform.x - lastTransform.x) / dt;
clientVelocity.vy = (serverTransform.y - lastTransform.y) / dt;
Performance Considerations
- Tiny memory — 8 bytes per component
- Cache-friendly — Simple POD struct
- No allocations — Stack-based operations
Validation
Related Components
- TransformComponent — Position updated by velocity
- MissileComponent — Stores direction and speed
- MovementComponent — AI patterns that affect velocity
Related Systems
- MovementSystem — Integrates velocity into position
- PlayerSystem — Sets velocity from input
- MissileSystem — Maintains constant missile velocity
- MonsterSystem — Applies AI movement patterns