Tile Maps ========= .. versionadded:: 1.4.0 Many games use a rectangular grid of tile images to represent static parts of a level, such as floors, platforms, obstacles, background scenery, traps and more: .. figure:: _static/tilemap/example-tilemap.png A platform game level made in Wasabi2D with a `tile pack from Craftpix `_. You could use sprite primitives to do this, but because large, scrolling levels can consist of tens of thousands of tiles, this is relatively expensive both in terms of memory usage and rendering. Instead Wasabi2D provides a GPU accelerated tile map that is fast to render even over vast tile maps. Some of the properties of this tile map: * You do not need to declare the bounds of the map. Maps do not have to have rectangular bounds or be contiguous. * You can update tiles at any coordinate at any time. * However the map can use at most 255 tile images. (To use more tiles, consider using multiple maps.) Quickstart ---------- Create a tile map in a layer by calling ``add_tile_map()``. The easiest form of this requires no arguments. Setting tiles into the map is done by assigning the name of an image (from the ``images/`` directory) to a coordinate pair: .. code-block:: python tiles = scene.layers[3].add_tile_map() tiles[3, 5] = 'tile_sand' .. method:: Layer.add_tile_map(*, tile_size: Tuple[int, int] = None, any_size_tile: bool = False) -> TileMap Create a tile map, initially blank. :param tile_size: The dimensions of each tile. If omitted this will be inferred from the first tile you insert into the map. :param any_size_tile: If True, allow setting images of any size into the map and resize them; otherwise, all tiles must match `tile_size`. If this is given then `tile_size` is a required parameter. As well as setting tiles, there are a range of operations to treat the map as a mapping of coordinate to image name. For example, you can retrieve the tile values you have already set: .. code-block:: python print(tiles[3, 5]) # prints tile_sand There are also a number of :ref:`tile-drawing` operations to update the map in bulk. Tile Get/Set Operations ----------------------- .. currentmodule:: wasabi2d.primitives.tile_map .. automethod:: TileMap.__setitem__ .. automethod:: TileMap.__getitem__ .. automethod:: TileMap.get .. automethod:: TileMap.setdefault .. automethod:: TileMap.__delitem__ .. automethod:: TileMap.clear .. _tile-drawing: Tile Drawing Operations ----------------------- All these methods accept a tile parameter given as a string to set in the tile map. They also accept a list of tile image names. In this case each tile that is drawn randomly picks from the list:: tiles = scene.layers[0].add_tile_map() tiles.line( ['fence1', 'fence2'], # randomly pick fence tile images start=(0, 0), end=(20, 0), ) You can also pass ``value=None`` in order to clear affected tiles. .. automethod:: wasabi2d.primitives.tile_map.TileMap.fill_rect .. automethod:: wasabi2d.primitives.tile_map.TileMap.line .. automethod:: wasabi2d.primitives.tile_map.TileMap.flood_fill .. _tiled-tip: Tips: TilEd ----------- To design large tile maps it is useful to have an editor. TilEd_ is a great cross-platform editor that allows you to create tile maps with powerful editing tools. It also allows you to annotate your tile map with any other data your game might need, such as the positions of decorations and interactions, and custom field for each tile. As Wasabi2D focuses purely on the rendering of tile maps, consider using one of these libraries to load the data you want from TMX files: * `tmx `_ * `PyTMX `_ .. _TilEd: https://www.mapeditor.org/ Tips: Decorations ----------------- Tile maps can be repetitive. Consider adding additional decorative objects both behind and in front of your tile map to make areas of a level seem more individual: .. image:: _static/tilemap/tilemap-decorated.png Tips: Shortcut functions ------------------------ The :class:`TileMap` doesn't have built-in features to understand the tile images you have; it doesn't know which images are edge tiles or connectors. If you use :ref:`TilEd `, it can manage this for you. If you are building tile maps procedurally it is useful to build abstractions to help you generate the tile map. For example, here are some functions I wrote to build platforms or ladders in a game: .. code-block:: python tm = scene.layers[0].add_tile_map() def platform(xs, y): """Insert a platform into the tile map.""" x1, x2 = sorted(xs) tm[x1, y] = 'platform_left' for x in range(x1 + 1, x2): tm[x, y] = 'platform_mid' tm[x2, y] = 'platform_right' def ladder(x, ys): """Insert a ladder into the tile map.""" y1, y2 = sorted(ys) tm[x, y1] = 'ladder_top' for y in range(y1 + 1, y2): tm[x, y] = 'ladder_mid' tm[x, y2] = 'ladder_btm' platform((1, 4), 7) platform((8, 11), 10) platform((13, 17), 6) ladder(7, (9, 13)) ladder(18, (5, 12))