Mobile Unit Movement in OpenRA - guidebee/OpenRA GitHub Wiki
This document explains how different unit types (ground, naval, and air) move within the game world and utilize the 3D coordinate system in OpenRA.
OpenRA uses several coordinate systems to represent positions in the game world:
WPos
is the most fundamental 3D coordinate system, representing positions in the game world with:
-
X
andY
define the horizontal position -
Z
defines the vertical position (height above terrain)
public readonly struct WPos
{
public readonly int X, Y, Z;
// ...
}
CPos
represents discrete grid cells in the map:
-
X
andY
are the grid coordinates -
Layer
represents different movement layers (0 for ground, others for tunnels, etc.)
public readonly struct CPos
{
public readonly int X, Y;
public readonly byte Layer;
// ...
}
These are intermediate coordinate systems used for map representation and projections. They help handle terrain height and isometric perspectives.
The Locomotor system is central to unit movement in OpenRA. It handles:
- Determining terrain passability
- Managing movement speeds on different terrain types
- Handling collisions and obstacles
- Supporting different movement layers (ground, bridges, tunnels, etc.)
Ground units use the Mobile
trait with a specific Locomotor
to move across the terrain.
public class Mobile : PausableConditionalTrait<MobileInfo>, IIssueOrder, IResolveOrder, /* ... */
{
// ...
}
Key characteristics:
-
Terrain Interaction: Ground units move along the terrain surface, affected by slopes and terrain types.
-
Z-Coordinate Handling: The Z coordinate is automatically calculated based on the terrain height.
public void SetPosition(Actor self, CPos cell, SubCell subCell = SubCell.Any) { // ... var position = cell.Layer == 0 ? self.World.Map.CenterOfCell(cell) : self.World.GetCustomMovementLayers()[cell.Layer].CenterOfCell(cell); position += self.World.Map.Grid.OffsetOfSubCell(subCell); position -= new WVec(0, 0, self.World.Map.DistanceAboveTerrain(position).Length); // ... }
-
Movement Types: Ground units have different movement types (tracked, wheeled, etc.) that affect their speed on various terrains.
-
Custom Layers: Ground units can also move in special layers like tunnels and bridges using
CustomMovementLayerType
:public static class CustomMovementLayerType { public const byte Tunnel = 1; public const byte Subterranean = 2; public const byte Jumpjet = 3; public const byte ElevatedBridge = 4; }
Aircraft use the Aircraft
trait which implements specialized movement in 3D space:
public class Aircraft : PausableConditionalTrait<AircraftInfo>, /* ... */
{
// ...
}
Key characteristics:
-
Full 3D Movement: Aircraft explicitly manage all three dimensions (X, Y, Z).
-
Cruise Altitude: Aircraft typically maintain a specific altitude defined by
CruiseAltitude
:public readonly WDist CruiseAltitude = new(1280);
-
Flight Behaviors:
- Taking off: Vertical movement to reach cruise altitude
- Landing: Controlled descent to land on airfields or terrain
- Cruising: Maintaining altitude while moving horizontally
- Hovering (for VTOL aircraft): Maintaining position in the air
-
Aircraft Motion: Aircraft move using specialized flight vectors that consider speed, facing, and altitude:
public WVec FlyStep(int speed, WAngle facing) { var dir = new WVec(0, -1024, 0).Rotate(WRot.FromYaw(facing)); return speed * dir / 1024; }
-
Terrain Interaction: Aircraft calculate their height above terrain using
DistanceAboveTerrain
:public WDist DistanceAboveTerrain(WPos pos) { // Apply ramp offset var cell = CellContaining(pos); var offset = pos - CenterOfCell(cell); // ... }
Units move by executing various activities that handle movement logic:
-
Ground Units:
-
Move
: Basic movement to a cell -
MoveWithinRange
: Move to within a specified range of a target -
MoveTo
: Move to a specific location -
LocalMove
: Fine-grained movement within a cell
-
-
Aircraft:
-
Fly
: Basic flight movement -
Land
: Controlled descent to landing sites -
TakeOff
: Vertical ascent from ground -
FlyFollow
: Following a moving target -
FlyOffMap
: Exit the map bounds
-
When units move, several coordinate transformations take place:
-
Cell to World Position:
public WPos CenterOfCell(CPos cell) { if (Grid.Type == MapGridType.Rectangular) return new WPos(1024 * cell.X + 512, 1024 * cell.Y + 512, 0); // For isometric maps var z = Height.TryGetValue(cell, out var height) ? 724 * height + Grid.Ramps[Ramp[cell]].CenterHeightOffset : 0; return new WPos(724 * (cell.X - cell.Y + 1), 724 * (cell.X + cell.Y + 1), z); }
-
World Position to Cell:
public CPos CellContaining(WPos pos) { if (Grid.Type == MapGridType.Rectangular) return new CPos(pos.X / 1024, pos.Y / 1024); // For isometric maps var u = (pos.Y + pos.X - 724) / 1448; var v = (pos.Y - pos.X + (pos.Y > pos.X ? 724 : -724)) / 1448; return new CPos(u, v); }
Terrain elevation affects unit movement through:
-
Ramps: Sloped terrain represented by
CellRamp
structures:public readonly struct CellRamp { public readonly int CenterHeightOffset; public readonly WVec[] Corners; public readonly WVec[][] Polygons; public readonly WRot Orientation; // ... }
-
Height Map: A height value for each cell:
public CellLayer<byte> Height { get; private set; }
-
TerrainInfo: Properties defining how units interact with terrain types
Movement calculation includes:
-
Speed Calculations:
// For ground units public int MovementSpeedForCell(CPos cell) { var terrainSpeed = Locomotor.MovementSpeedForCell(cell); var modifiers = speedModifiers.Value.Append(terrainSpeed); return Util.ApplyPercentageModifiers(Info.Speed, modifiers); } // For aircraft public int MovementSpeed => !IsTraitDisabled && !IsTraitPaused ? Util.ApplyPercentageModifiers(Info.Speed, speedModifiers) : 0;
-
Direction and Facing: Units maintain facing (direction) during movement:
public WAngle Facing { get => orientation.Yaw; set => orientation = orientation.WithYaw(value); }
-
Vertical Movement: For aircraft, vertical movement is calculated with:
public static bool VerticalTakeOffOrLandTick(Actor self, Aircraft aircraft, WAngle desiredFacing, WDist desiredAltitude) { // ... var maxDelta = aircraft.Info.AltitudeVelocity.Length; var deltaZ = (desiredAltitude.Length - dat.Length).Clamp(-maxDelta, maxDelta); aircraft.SetPosition(self, aircraft.CenterPosition + new WVec(0, 0, deltaZ)); // ... }
Pathfinding uses the coordinate systems to navigate units through the world:
- Units calculate paths through cells considering terrain, obstacles, and passability
- Aircraft can navigate directly through 3D space, subject to altitude constraints
- Each locomotor type has its own set of movement rules for different terrain types
OpenRA's movement system is a sophisticated framework that:
- Handles multiple coordinate systems for different purposes
- Supports different unit types with specialized movement behavior
- Incorporates terrain elevation and features into movement calculations
- Provides realistic 3D movement for aircraft and other flying units
- Allows custom movement layers for specialized unit abilities
This system creates a realistic and varied gameplay experience where unit movement feels appropriate to the unit type and the terrain they're traversing.