A Tiled JSON map renderer for pyglet
These classes use the JSON format as generated by Tiled JSON plugin to render the maps with pyglet (1.2+).
The Map class will use internally a Batch to render the map in the most efficient way, although the Map.draw() method has to be used as it will apply the required transforms to the batch.
A map can be loaded with Map.load_json(fileobj). Before rendering the map a viewport has to be specified with Map.set_viewport(x, y, width, height).
pyglet.resource framework is used to load all the elements of the map, so any path information must be removed from the tileset.
Loading a Map
A map can be loaded using Map.load_json():
# use pyglet's resource framework fd = pyglet.resource.file("map.json") m = Map.load_json(fd) # set the viewport m.set_vieport(0, 0, 320, 200)
The viewport has to be set at least once so the Map class knows which area is available to draw the map.
Drawing the Map
The map will be drawn using a batch that is exposed in Map.batch, but because some transformations need to be applied to the batch, the method Map.draw() must be used:
# in your draw code m.draw()
See example/basic_render.py for a complete example.
Changing the focus
If the map is larger than the area available to draw it, it is possible to specify which part of the map will be draw by using the Map.set_focus(x, y) method.
Invalidating the batch
The map will update the batch only then is required (eg, the focus changed and the part of the map to be drawn is different). If you change part of the map data and the batch information is not up to date, Map.invalidate() can be used to force a regeneration of the batch.
Calling Map.invalidate() frequently can affect the application performance.
Translating coordinates
World coordinates can be translated to screen coordinates with Map.world_to_screen(x, y). This can be useful to determine if an element is visible on screen or not.
Accessing to Map data
Almost every element of the map has a data property that contains a dictionary with the actual JSON data, but that information can be accessed using a pythonic interface.
In the case of object groups, the actual objects returned by the interface are dictionaries with no data property.
Also the Map class provides access to the layers with:
- Map.layers: list with all the layers.
- Map.tilelayers: dictionary with the tile layers indexed by name.
- Map.objectgroups: dictionary with the object group layers indexed by name.
The tile layer interface provides access to the tiles, for example:
# list all the tiles for tile in m.tilelayers["Walls"]: print tile # get one specific tile print m.tilelayers["Walls"][10, 10]
The object group layer interface provides access to the objects, for example:
# list all the objects for obj in m.objectgroups["Objects"]: print obj # is there a "Door1" object? print "Door1" in m.objectgroups["Objects"] # is there aan object in coords 10, 10? print (10, 10) in m.objectgroups["Objects"] # get the object named "Door1" print m.objectgroups["Objects"]["Door1"] # get the object in coords (5, 3) print m.objectgroups["Objects"][5, 3] # list all the objects with type "Door": for obj in m.objectgroups["Objects"].get_by_type("Door"): print obj
Adding unmanaged sprites to the Map
In order to get the best performance any Sprite can be added to the Map's batch even it won't be managed by the Map class.
There are some things to take into account:
- The sprite should be added to the Map's batch using an OrderedGroup with number >= Map.last_group (or it may not be visible).
- The sprite coordinates are world based (instead of screen based). Also the y coordinate has to be converted from pyglet's coordinate system to Tiled coordinate system (basically: y = SCREEN_HEIGHT-real_y-1).
The sprite can be moved using its x and y properties in world coordinates.
Author
Juan J. Martinez <jjm@usebox.net>
The rendering code was inspired by Cocos2D TMX support.