/ #mechanics 

Exploration

Exploring the unknown is a common mechanic in many games. It is very easy to implement in turn-based strategy games, but coupled with randomly generated maps can make for a very exciting experience for the player.

The banner image from Heroes of Might and Magic I (above) illustrates this perfectly: the Knight is staring off into the distance, wondering what wonders are hidden out of sight to the east.

ECS Design

Components

Tile

  • x: Integer
  • y: Integer
  • graphic: String / BinaryBlob

Exploration

  • explored: Boolean (defaults to False initially)

Position

  • x: Integer
  • y: Integer

Explorer

  • range: Integer

Entities

Each map tile entity has Tile and Exploration components. Tile has information about what the tile looks like, which in this case has been abstracted to the graphic field for purposes of the example. In a real game there will probably be a variety of entities that need to be rendered instead.

The unit doing the exploration has a Position and Explorer component - the latter tells us how far the explorer can see.

Systems

ExplorationSystem

The exploration system is responsible for marking tiles as explored once they have been in range of an explorer.

explorers = world.get_entities_with_components(Position, Explorer)

for entity in explorers:
    # I avoid the sqrt for euclidean distance because multiply
    # is cheaper than sqrt in computers, result is the same
    max_distance = entity.explorer.range * entity.explorer.range
    min_y = entity.position.y - entity.explorer.range
    max_y = entity.position.y + entity.explorer.range + 1
    min_x = entity.position.x - entity.explorer.range
    max_x = entity.position.x + entity.explorer.range + 1

    for y in range(min_y, max_y):
        for x in range(min_x, max_x):
            delta_x = entity.position.x - x
            delta_y = entity.position.y - y
            distance = delta_x * delta_x + delta_y + delta_y

            if distance <= max_distance:
                tile = map.tiles[x][y]
                tile.exploration.explored = True

Notes:

  • This system can be made a great deal cheaper to run by keeping a map of explorer positions and only executing the inner loops an explorer’s position has changed.

RenderSystem

The render system is responsible for drawing the game map and entities on the screen. When an exploration system is in play, the render system needs to consider this to decide if something should be drawn or not.

tiles = world.get_entities_with_components(Tile, Exploration)

for entity in tiles:
    if entity.exploration.explored:
        draw(entity.tile.graphics)
    else:
        draw(unexplored_graphic)
  • TODO: Vision / Line of sight
  • TODO: Fog of war
Author

Matt Van Der Westhuizen

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