motion

package
v0.1.6 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 5, 2026 License: MIT Imports: 7 Imported by: 0

Documentation

Overview

Package motion provides components and systems for entity movement.

Spatial holds an entity's current world position and facing direction. Movement stores an entity's movement target and speed. MovementResult carries the outcome of a movement tick: the entity ID, original position, new position, and whether the destination was reached. ProcessMovement calculates per-tick displacement given a duration and returns the new position and a completion flag indicating whether the entity has reached its destination.

System bundles the component handles and spatial indexes movement operates on. Tick advances every entity that has a Movement and satisfies the sim.TickSystem interface. MoveEntity starts a single move with tile occupancy checks, and MoveEntityTowards and MoveEntityTowardsArea follow A* paths one bounded step at a time. Game policy stays with the caller: each attempt returns a MoveStart describing what happened so the consuming game can update its own entity states, AI scheduling, and logs.

Index

Constants

View Source
const (
	SpeedWalk = 1.0 // Speed in tiles per second
	SpeedRun  = 2.0 // Speed in tiles per second
)

Variables

This section is empty.

Functions

func ProcessMovement

func ProcessMovement(currentPosition, destination geometry.Vector2, speed float64, duration time.Duration) (newPosition geometry.Vector2, completed bool)

ProcessMovement calculates the new position for a moving entity based on elapsed time. It returns a MovementResult indicating the new position and whether movement is complete.

Parameters:

  • currentPosition: The entity's current position
  • destination: The target position
  • speed: Movement speed in tiles per second
  • duration: Time elapsed since last update

Returns:

  • newPosition: The calculated new position
  • completed: true if the entity has reached or passed the destination

Types

type MoveOutcome added in v0.1.5

type MoveOutcome int

MoveOutcome classifies the result of trying to start a move.

const (
	// MoveOutcomeNone is the uninitialized value.
	MoveOutcomeNone MoveOutcome = iota

	// MoveOutcomeStarted means a Movement toward the destination was set.
	MoveOutcomeStarted

	// MoveOutcomeAtDestination means the entity already is where it was asked
	// to go (or, for area moves, already inside the target area).
	MoveOutcomeAtDestination

	// MoveOutcomeDestinationOccupied means another entity has reserved the
	// destination tile. Normal AI flow during dense crowds, not an error.
	MoveOutcomeDestinationOccupied

	// MoveOutcomeNoPath means no walkable route toward the destination was
	// found. Normal flow when crowds block the entity in.
	MoveOutcomeNoPath
)

type MoveStart added in v0.1.5

type MoveStart struct {
	// Outcome classifies what happened.
	Outcome MoveOutcome

	// Destination is the position the move targets (the actual waypoint for
	// path-following moves, which may differ from the requested target).
	Destination geometry.Vector2

	// Distance is the length of the started move. Zero unless Outcome is
	// MoveOutcomeStarted.
	Distance float64

	// Duration is the game time the started move will take at the requested
	// speed. Zero unless Outcome is MoveOutcomeStarted.
	Duration time.Duration
}

MoveStart reports the outcome of trying to start a move so the caller can update game state (entity states, AI scheduling) and log the attempt.

func (MoveStart) Started added in v0.1.5

func (m MoveStart) Started() bool

Started reports whether a move was actually set in motion.

type Movement added in v0.1.3

type Movement struct {
	// Destination is the target position the entity is moving towards.
	Destination geometry.Vector2

	// Speed is the movement speed in tiles per second.
	Speed float64
}

Movement holds an entity's in-progress move: where it is headed and how fast.

type MovementResult

type MovementResult struct {
	EntityId         ecs.EntityId
	NewPosition      geometry.Vector2
	Completed        bool
	OriginalPosition geometry.Vector2
}

MovementResult represents the result of processing a single entity's movement.

type Spatial added in v0.1.1

type Spatial struct {
	// Position is the current world coordinates of the entity in tile units.
	Position geometry.Vector2

	// Direction is the facing direction vector for orientation. It is not
	// necessarily normalized: MoveEntity stores the raw displacement toward
	// the destination.
	Direction geometry.Vector2
}

Spatial holds an entity's position and facing in the game world. It is an ECS component: a plain data struct read and written through the world's component handles.

type System added in v0.1.5

type System struct {
	// Spatials accesses each entity's position and facing.
	Spatials ecs.Accessor[Spatial]

	// Movements accesses each entity's in-progress move.
	Movements ecs.Accessor[Movement]

	// Grid, when non-nil, is kept in sync as entities move.
	Grid *tilemap.SpatialGrid

	// Occupancy, when non-nil, tracks tile reservations: MoveEntity refuses
	// destinations reserved by another entity and moves the reservation as
	// the entity departs.
	Occupancy *tilemap.TileOccupancyManager

	// Terrain provides walkability for CanReach and the pathfinding helpers.
	// FindTilePath, FindPathBetween, MoveEntityTowards and
	// MoveEntityTowardsArea require it and panic when it is nil; a nil
	// Terrain makes CanReach treat every tile as walkable.
	Terrain pathfinding.TerrainProvider

	// RecordPhase, when non-nil, receives wall time spent in instrumented
	// hot spots ("pathfinding", "move_towards_area") so games can feed their
	// benchmark reports.
	RecordPhase func(name string, elapsed time.Duration)

	// MaxMoveActionDistance caps how far a single MoveEntityTowards step may
	// reach, in tiles. Games use a value just above sqrt(2) for
	// one-tile-per-action movement including diagonals.
	// MoveEntityTowards and MoveEntityTowardsArea panic when it is not set
	// (<= 0).
	MaxMoveActionDistance float64

	// OnArrival, when non-nil, is called for each entity that reaches its
	// destination during a Tick, after its Movement has been removed.
	OnArrival func(MovementResult)
}

System advances moving entities and starts new moves. It bundles the component handles and spatial indexes movement operates on; the consuming game constructs one per world.

Tick satisfies the sim.TickSystem interface, so a System can be registered on a sim.Driver directly or wrapped by a game tick system that also advances the game clock and records metrics.

Game policy stays with the caller: System never touches entity states or AI scheduling. Move attempts report what happened through MoveStart so the game can update its own components and log with its own logger.

func (*System) CanReach added in v0.1.5

func (s *System) CanReach(entityId ecs.EntityId, destination geometry.Vector2) bool

CanReach reports whether entityId can move onto the tile containing destination. It checks only the destination tile, not the path to it; use FindPathBetween for a full path check.

func (*System) CanReachTile added in v0.1.5

func (s *System) CanReachTile(entityId ecs.EntityId, tile tilemap.TileCoord) bool

CanReachTile reports whether entityId can move onto tile: the tile must be in bounds and walkable (always true when Terrain is nil) and not reserved by another entity (always true when Occupancy is nil).

func (*System) FaceDirection added in v0.1.5

func (s *System) FaceDirection(id ecs.EntityId, direction geometry.Vector2)

FaceDirection sets an entity's facing direction without moving it. The entity must have a Spatial; FaceDirection panics otherwise.

func (*System) FindPathBetween added in v0.1.5

func (s *System) FindPathBetween(origin, destination geometry.Vector2) []geometry.Vector2

FindPathBetween returns a sequence of world positions to move through in order to reach destination from origin, based on FindTilePath. The origin tile is skipped when origin already sits at its center, and the final waypoint is constrained to the goal tile's center so entities stay grid-aligned. It returns an empty slice when no path exists. A request whose origin and destination fall in the same tile returns a single waypoint at that tile's center (empty when the origin already sits at the center).

func (*System) FindTilePath added in v0.1.5

func (s *System) FindTilePath(start, goal tilemap.TileCoord) []tilemap.TileCoord

FindTilePath finds a tile path from start to goal using A* over the System's Terrain, routing around tiles reserved in Occupancy. It returns nil when no path exists. Terrain must be set; FindTilePath panics otherwise.

func (*System) MoveEntity added in v0.1.5

func (s *System) MoveEntity(id ecs.EntityId, destination geometry.Vector2, speed float64) MoveStart

MoveEntity starts moving an entity toward destination at speed (in tiles per second). When the System has an Occupancy manager, the destination tile must be free (or reserved by this entity); the entity's reservation moves from its current tile to the destination tile as the move starts.

The entity's facing direction is set toward the destination. The entity must have a Spatial; MoveEntity panics otherwise. speed must be positive, otherwise the returned Duration is meaningless. MoveEntity is intended for entities settled on their reserved tile: redirecting an entity mid-move can strand its old destination reservation and clear a tile it no longer holds.

func (*System) MoveEntityTowards added in v0.1.5

func (s *System) MoveEntityTowards(entityId ecs.EntityId, destination geometry.Vector2, speed float64) MoveStart

MoveEntityTowards starts moving an entity one bounded step toward destination: it follows the A* path from FindPathBetween but only as far as MaxMoveActionDistance, respecting walkable terrain and tile reservations. When no waypoint along the path is directly reachable, it falls back to the reachable adjacent tile that gets closest to the path. A destination within the entity's current tile starts a move to that tile's center, unless the entity is already there.

The returned MoveStart reports whether a step was started; callers use it to update entity states or fall through to a wait directive. MoveOutcomeNoPath means no walkable route toward destination was found (the entity is boxed in), which also covers the case where the entity is already exactly at destination. The entity must have a Spatial; MoveEntityTowards panics otherwise. MaxMoveActionDistance must be configured (> 0); MoveEntityTowards panics otherwise.

func (*System) MoveEntityTowardsArea added in v0.1.5

func (s *System) MoveEntityTowardsArea(entityId ecs.EntityId, center geometry.Vector2, radius, speed float64) MoveStart

MoveEntityTowardsArea starts moving an entity one bounded step toward a circular area defined by center and radius (in tiles): it finds the reachable tile center inside the area that is closest to the entity and steps toward it via MoveEntityTowards.

The returned MoveStart reports MoveOutcomeAtDestination when the entity is already inside the area and MoveOutcomeNoPath when no tile in the area is reachable (normal flow when every tile around the target is occupied). The entity must have a Spatial; MoveEntityTowardsArea panics otherwise.

func (*System) Tick added in v0.1.5

func (s *System) Tick(elapsed time.Duration)

Tick moves every entity that has a Movement by elapsed game time. Entities that reach their destination have their Movement removed and are reported through OnArrival. Entities with a Movement but no Spatial are skipped.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL