Skip to content

Basic Map Features

kobewi edited this page Feb 14, 2024 · 6 revisions

In-game map is an important feature of most metroidvania games. MetSys comes with various methods for drawing and manipulating the map.

Drawing the Map

Usually the in-game map is drawn in form of minimap and a full map on a separate screen. The drawing methods are basic enough to cover any use-case. The basic way to draw map is using MetSys.draw_cell() method. It takes several arguments:

  • canvas_item: The CanvasItem that will do the drawing (e.g. your minimap panel).
  • offset: Drawing offset of the cell, in map coordinates. E.g. (1, 1) will offset cell the cell by MetSyS.CELL_SIZE (size of a map cell in pixels).
  • coords: Map coordinates to draw. Usually offset and coords need to be somewhat in sync.
  • skip_empty (optional): If true, empty cell texture as defined in Map Theme will not be drawn. Has no effect if empty texture is not set.
  • use_save_data (optional): If true, runtime save data will be used to draw the rooms (i.e. discovered cells, overrides etc.).

Example call for drawing a 3x3 minimap would be:

for x in range(-1, 2):
	for y in range(-1, 2):
		MetSys.draw_cell(self, Vector2i(x + 1, y + 1), Vector3i(current_cell.x + x, current_cell.y + y, MetSys.current_layer))

We iterate x on range (-1, 1) inclusive and y on (-1, 1). We use x and y for the offset, but since they can be negative, we need to add 1 (otherwise the offset would be negative and the cells would be drawn outside beyond the top-left corner of our CanvasItem). The coords are Vector3i, because it's base coords + layer. The current_cell can be obtained in multiple ways. The easiest one is using MetSys.get_current_coords(), which returns the whole Vector3i. Alternative is connecting to MetSys.cell_changed signal (see the section below). Then we add x and y to the vector to draw adjacent cells.

If your map theme has enabled shared borders, you need to call MetSys.draw_shared_borders() after you finish drawing your map. And if you use custom elements, you need to use MetSys.draw_custom_elements(), which also takes a few arguments:

  • canvas_item: The CanvasItem that will draw the elements (can be the same as before).
  • rect: The coordinate range to draw the elements. In case of the 3x3 minimap it would be Rect2i(current_coords - (1, 1), (3, 3)).
  • drawing_offset (optional): Offset of the elements, in map coordinates.
  • layer (optional): The layer of elements to draw. Defaults to the current layer.

Drawing map is a complex operation and might be tricky to use for the first time. But it will draw cells accurately, as defined in the theme, while taking in consideration all symbols and whether the cells are discovered or not. You can draw any cells of your world anywhere on the screen, as the system is very flexible. Note however that it's expensive, so make sure that your map does not redraw unnecessarily (especially when using shared borders). To redraw efficiently, take advantage of MetSys.map_updated signal and, in case of a minimap, also MetSys.cell_changed.

Tracking Player Position

Tracking player position allows for automatically discovering cells and changing scenes when player goes out of bounds. Tracking position is performed by using MetSys.set_player_position(). The method takes a position relative to the current scene's origin (i.e. top-left corner of the room). Unless your map's root node is offsetted, you can just use player's global_position. The method has to be called each frame, after the player has done moving. Example:

... some input
move_and_slide()
MetSys.set_player_position(global_position)

MetSys will automatically detect if the current map cell is different and emit cell_changed signal, which you can use to update your minimap. When the position is in a different room, MetSys will emit room_changed signal that also contains the name of the new scene that you should load. The scene name is not a full path; you can get the full path by using MetSys.get_full_room_path() on its name.

You can display player's location on your map by using MetSys.add_player_location(). This method should be called once per map CanvasItem. It will add your player location scene defined in the map theme as child of that node and automatically move it when visible, based on your supplied player position. The method also optionally takes an offset, in case your map CanvasItem has some margins.

Markers

A cell may have assigned any number of markers, out of the markers defined in map theme (each marker can be assigned only once). Markers assigned at runtime will override the symbol assigned in the Map Editor. They are stored as a bitmask, which means you can define and assign only 63 markers. If multiple markers are assigned, the one being last on the theme list will be used. Markers are usually assigned based on events, manually by players on map screen, or automatically via storable objects. Assign a marker using MetSys.add_custom_marker(), remove it using MetSys.remove_custom_marker().

Discovering

Cells on the map can be in 3 states: undiscovered, mapped and explored. Undiscovered rooms don't draw at all, explored rooms draw with the default style. Mapped rooms draw with an alternate style and rules that can be specified in the map theme. You can manually discover cells using MetSys.discover_cell() or you can discover a group of cells (e.g. when picking up a map item) by using MetSys.discover_cell_group(). Cell groups can be defined in the editor. Discovering a cell emits MetSys.map_updated signal.

You can use is_cell_discovered() to check if a cell is discovered and get_explored_ratio() to get ratio of explored cells vs all cells.