/ #mechanics 

Movement: Grid-based

Grid-based movement is a very simple movement system that is often used in strategy games, board-games and roguelikes.

At its most basic a grid based movement system consists of a world defined as a grid and a unit defined as an (x, y) coordinate that can be moved in 8 directions, as shown in the figure below. Diagonal movement

As the greyed out diagonal arrows suggest, not all games allow diagonal movement so technically there are a few variants of this system:

  • Orthogonal: up, down, left and right only.
  • Diagonal: This term usually refers to orthogonal plus the diagonal directions being allowed.
  • Diagonal only: Only allow movement on diagonals, not on orthogonals - I don’t think I’ve ever seen this used.

Games with large empty grids to move around in are not very interesting, so most games also define some of the grid tiles to be impassable as shown below.

Map with obstacles

Most games don’t actually show the grid, for example here’s a pretty screenshot from Heroes of Might and Magic showing the orthogonal movement system with the grid nearly invisible thanks to good tile design. Pretty Heroes of Might and Magic map hides the grid!

As the screenshot above hints, there’s another factor that might be come into play, which is limiting the amount of movements a player can make in a turn - for more information about that see related mechanics.

ECS Design

Components

Position

  • x: Integer
  • y: Integer

Tile

  • passable: Boolean

MoveCommand

  • delta_x: Integer (-1, 0, 1)
  • delta_y: Integer (-1, 0, 1)

SelectedUnit

Marker component for marking the selected unit.

Entities

One entity per map tile in the grid with Tile and Position components.

Map data structure containing all the tiles in a grid for easy lookup.

One Position entity per unit in the game with Position component. Add a MoveCommand to a unit to move it.

Map data structure for units to allow easy lookup of units by position.

Systems

InputSystem

The InputSystem gathers input from the user and turns it into MoveCommand components on the unit being moved.

key = get_keypresses()
selected_entity = world.get_entity_with_component(SelectedUnit)

while not valid_input(key):
    # Only orthogonal movement implemented in this example
    if key == LEFT:
        world.add_component(selected_entity, MoveCommand(-1, 0))
    elif key == RIGHT:
        world.add_component(selected_entity, MoveCommand(1, 0))
    elif key == UP:
        world.add_component(selected_entity, MoveCommand(0, 1))
    elif key == DOWN:
        world.add_component(selected_entity, MoveCommand(0, 1))

    key = get_keypresses()

MovementSystem

The MovementSystem applies movement commands to the entity (if the terrain allows).

for entity in world.get_entities_with_components(Position, MoveCommand):
    target_x = entity.position.x + entity.move_command.delta_x
    target_y = entity.position.y + entity.move_command.delta_y

    target_tile = world.get_tile_at(target_x, target_y)
    # Practically speaking to do the above line efficiently
    # you actually need some map data structure in addition
    # to the ECS entities for the tiles...

    if not target_tile.passable:
        message("You walk into the wall.")
    else:
        entity.position.x = target_x
        entity.position.y = target_y
    
    world.remove_component(entity, MoveCommand))
Author

Matt Van Der Westhuizen

Back-end service developer at Ubisoft Blue Byte by day - wannabe game designer & developer by night.