Tiled maps ========== The :mod:`cocos.tiles` module provides a simple method of managing tiled displays in 2D games. Tiled maps are made of three components: images Images are from individual files for from image atlases in a single file. tiles Tiles may be stand-alone or part of a TileSet. maps MapLayers are made of either rectangular or hexagonal Cells which reference the tiles used to draw the cell. tmx objects Entities that do not align with a grid, see `Support for TMX object layers`_. Currently only available when loading from a .tmx file. cocos.tiles supports two file formats: 1. The TMX file format as generated by the Tiled map editor from . 2. An internal cocos2d XML format that it can both read and write described below in `The XML File Specification`_. Both file formats are loaded with the same API call:: #This loads a TMX format map cocos.tiles.load('filename.tmx') #This loads a cocos2d XML format map cocos.tiles.load('filename.xml') Once the map is loaded the API for accessing the map information is the same regardless of the on-disk format. Fairly simple examples of using the cocos.tiles module is provided in test/test_tiles.py, test/test_tmx.py, test_platformer.py A basic tile map editor is provided in tools/editor.py; use tools\gentileset.py to generate separate .xml for each tileset used. Maps ---- The :class:`.RectMapLayer` class extends the regular Cocos layer to handle a regular grid of tile images, or Cells. The map layer may be manipulated like any other Cocos layer. It also provides methods for interrogating the map contents to find cells (eg. under the mouse) or neighboring cells (up, down, left, right.) Maps may be defined in Python code, but it is a little easier to do so in XML. To support map editing the maps support round-tripping to XML. The XML File Specification -------------------------- The intent is that you may have a tile set defined in one XML file which is shared amongst many map files (or indeed within a single XML file). Images may be shared amongst multiple tiles with the tiles adding different meta-data in each case. These may be constructed manually or loaded from XML resource files which are used to store the specifications of tile sets and tile maps. A given resource XML file may define multiple tile sets and maps and may even reference other external XML resource files. This would allow a single tile set to be used by multiple tile maps. Assuming the following XML file called "example.xml":: You may load that resource and examine it:: >>> r = load('example.xml') >>> map = r['level1'] and then, assuming that level1 is a map:: >>> scene = cocos.scene.Scene(map) and then either manually select the tiles to display: >>> map.set_view(0, 0, window_width, window_height) or if you wish for the level to scroll, you use the ScrollingManager:: >>> from cocos import layer >>> manager = layer.ScrollingManager() >>> manager.add(map) and later set the focus with:: >>> manager.set_focus(focus_x, focus_y) See the section on `controlling map scrolling`_ below for more detail. XML file contents ----------------- XML resource files must contain a document-level tag :: ... You may draw in other resource files by using the tag:: This will load "road-tiles.xml" into the resource's namespace. If you wish to avoid id clashes you may supply a namespace:: If a namespace is given then the element ids from the "road-tiles.xml" will be prefixed by the namespace and a colon, e.g. "road:bitumen". Other tags within are:: Loads file with pyglet.image.load and gives it the id which is used by tiles to reference the image. :: Sets up an image atlas for child tags to use. Child tags are of the form:: If the tag does not provide a size attribute then all child tags must provide one. Image atlas ids are optional as they are currently not referenced directly. :: Sets up a TileSet object. Child tags are of the form:: [] The tag is optional; these tiles may have only properties (or be completely empty). The id is used by maps to reference the tile. .. Warning:: Currently tools/editor.py fails to save if a tileset is inlined in the .xml Workaround: use tools/gentileset.py to generate a .xml for each tileset used, and then include in the map using a:: element for each tileset. :: Sets up a RectMap object. Child tags are of the form:: .. _cell_properties_label: Map, Cell and Tile Properties ----------------------------- Most tags may additionally have properties specified as:: Where type is one of "unicode", "int", "float" or "bool". The property will be a unicode string by default if no type is specified. Properties are accessed on the map, cell or tile using common dict operations with some extensions. Accessing a property on a **cell** will fall back to look on the **tile** in case it's not found on the cell. If a cell has a property ``player-spawn`` (boolean) and the tile that the cell uses has a property ``move-cost=1`` (int) then the following are true:: 'player-spawn' in cell == True cell.get('player-spawn') == True cell['player-spawn'] == True 'player-spawn' in tile == False tile.get('player-spawn') == None tile['player-spawn'] --> raises KeyError cell['move-cost'] == 1 You may additionally set properties on cells and tiles:: cell['player-position'] = True cell['burnt-out'] = True and when the map is exported to XML these properties will also be exported. Controlling Map Scrolling ------------------------- You have three options for map scrolling: 1. Never automatically scroll the map. 2. Automatically scroll the map but stop at the map edges. 3. Scroll the map an allow the edge of the map to be displayed. The first is possibly the easiest since you don't need to use a :class:`.ScrollingManager` layer. You simple call ``map.set_view(x, y, w, h)`` on your map layer giving the bottom-left corner coordinates and the dimensions to display. This could be as simple as:: map.set_view(0, 0, map.px_width, map.px_height) If you wish to have the map scroll around in response to player movement the :class:`.ScrollingManager` from the :mod:`.cocos.layer.scrolling` module may be used. Map Queries ----------- Maps have a ``map.get_at_pixel(x, y)`` query which will return the Cell at that position in the Map. Given a Cell object you may also use ``map.get_neighbor(cell, direction)`` to get the Cell which is in the direction (one of ``map.UP``, ``map.DOWN``, ``map.LEFT`` or ``map.RIGHT``.) Handling collisions between actors and tiles -------------------------------------------- See :doc:`mapcoliders` Rectangular Maps ---------------- Cells are stored in column-major order with y increasing up, allowing [i][j] addressing:: +---+---+---+ | d | e | f | +---+---+---+ | a | b | c | +---+---+---+ The cells property is list of lists: ``[['a', 'd'], ['b', 'e'], ['c', 'f']]`` Thus the cell at (0, 0) is 'a' and (0, 1) is 'd'. Support for .TMX maps with hexagonal tiles ------------------------------------------ To have the same visualization in cocos than in Tiled Editor, you should set in Tiled the following map properties: - 'orientation': 'hexagonal'. - 'staggeraxis': 'x' (top and bottom hexagon sides parallel to the x axis). - 'staggerindex': 'even' (lower columns are the even ones). Draw order is not guaranteed by cocos, so tilesets which expands outside the hexagon will not look good. Support for TMX object layers ------------------------------ The TMX map format allows to specify 'object layers' also know as 'object groups'. An 'object layer' is a collection of entities freely placed in the world, that is, they don't need to align with a tile map grid. Objects can represent abstract 'ellipse', 'polygon', 'polyline', 'rect' or 'tile'. When a TMX file is loaded each 'object layer' produces an instance of :class:`.TmxObjectLayer`, the member '.objects' holds a list of the objects in the group, each one translated as an instance of :class:`.TmxObject`. Cocos does not intend to provide a real visualization of the :class:`.TmxObject`, for debug purposes if you add :class:`.TmxObjectLayer` to the scene the axis aligned bounding box of each object will be drawn. Usually after loading the map, your app code iterates over '.objects' and takes appropriate actions, like: - build a specific :class:`.CocosNode` for visualization (particle system, animated sprite,...) based in the ``TmxObject.usertype`` and other attributes, then add it to the scene. - translate to some model class, like a zone trigger or a patrol path and store in your level info. Other than the debug view, :class:`.TmxObjectLayer` provides some methods to select objects with certain properties or into a specified area.