| name | physics-and-movement |
| description | Physics and Movement in FlatRedBall2. Use when working with velocity, acceleration, drag, gravity, kinematic movement, projectiles, rotation-based thrust, or coordinate system (Y+ up). Also covers GameRandom helpers for randomized spawning. Trigger on any physics or movement question. |
Physics and Movement in FlatRedBall2
Coordinate System
Y+ is UP. This is the opposite of screen-space pixels.
- A value of
Y = 100 places an entity above center.
- Gravity is a negative AccelerationY.
- The camera applies a Y-flip when converting world → screen, so everything renders correctly.
Physics Properties on Entity
entity.X
entity.Y
entity.VelocityX
entity.VelocityY
entity.AccelerationX
entity.AccelerationY
entity.Drag
Gravity Pattern
Set a negative AccelerationY to simulate gravity. Spawn the entity with a positive Y so it has room to fall:
public class Ball : Entity
{
public override void CustomInitialize()
{
var circle = new Circle { Radius = 8f, IsVisible = true };
Add(circle);
AccelerationY = -200f;
}
}
var ball = _ballFactory.Create();
ball.X = 0f;
ball.Y = Engine.Random.Between(50f, 150f);
ball.VelocityX = Engine.Random.Between(-150f, 150f);
Kinematic Formula (Second-Order)
Each frame, PhysicsUpdate applies:
position += velocity * dt + acceleration * (dt² / 2)
velocity += acceleration * dt
velocity -= velocity * drag * dt
This is second-order (Verlet-style), so acceleration is smoothly integrated even at low frame rates.
Drag
Drag is a multiplier applied to velocity each frame:
entity.Drag = 1f;
entity.Drag = 0.5f;
entity.Drag = 0f;
Drag does not affect acceleration — only velocity. A falling entity with drag will reach terminal velocity when gravity equals drag deceleration.
Update Order Each Frame
See engine-overview for the full 8-step frame loop. The key point: Physics → Collision → CustomActivity — game logic sees already-corrected positions.
Common Patterns
Horizontal movement with deceleration
VelocityX = input.X * 200f;
AccelerationX = input.X * 500f;
Drag = 4f;
Jump
if (jumpPressed && IsOnGround)
VelocityY = 350f;
Rotation-based thrust (top-down ship)
Angle convention: Standard math — 0 = right (1, 0), 90° = up (0, 1), positive = counter-clockwise. Rotation.ToVector2() returns the unit vector the entity is facing. Use it directly for thrust.
Set acceleration in the forward direction each frame and use Drag to decelerate naturally when thrust stops:
Drag = 3f;
const float ThrustForce = 400f;
var forward = Rotation.ToVector2();
if (kb.IsKeyDown(Keys.Up))
{
AccelerationX = forward.X * ThrustForce;
AccelerationY = forward.Y * ThrustForce;
}
else
{
AccelerationX = 0f;
AccelerationY = 0f;
}
Projectile
var bullet = _bulletFactory.Create();
bullet.X = X;
bullet.Y = Y;
bullet.VelocityX = facingDirection * 600f;
Aiming at the cursor: cursor.WorldPosition is already in world space (Y+ up) — no coordinate conversion needed. Guard Vector2.Normalize against a zero-length vector when the cursor is exactly on the entity.
GameRandom — Randomized Spawning
FlatRedBallService.Random (type GameRandom, a subclass of System.Random) provides game-friendly helpers:
Engine.Random.Between(-150f, 150f)
Engine.Random.Between(50f, 100f)
Engine.Random.NextSign()
Engine.Random.NextBool()
Engine.Random.RadialVector2(50f, 100f)
Engine.Random.PointInCircle(80f)
Engine.Random.NextAngle()
Engine.Random.In(list)
In unit tests, create new GameRandom(seed: 42) for deterministic results.