This is a tutorial for using the maps module in kivent to load Tiled maps. It covers the steps required to load and display a map on the kivent canvas, but it doesn't cover how to make a map in Tiled.
Make sure you have kivy and its dependencies properly set up and working using these installation instructions.
Clone the kivent repository to obtain the source. The module is currently in a separate branch 'tiled_gsoc_2016' so you can clone that branch only.
git clone -b tiled_gsoc_2016 https://github.com/kivy/kivent.git
You can skip the -b tiled_gsoc_2016
if you want the whole repository.
Install kivent_core
first. Assuming you cloned it in a dir named 'kivent'
$ cd kivent/modules/core
$ python setup.py build_ext install
Then install the maps module similarly
$ cd kivent/modules/maps
$ python setup.py build_ext install
It is best to set up kivy and kivent in a virtual environment. Just make sure you use the correct python for the above commands. The module works best with python3, but it works with python2 too.
We need a basic setup of the gameworld and a gameview where we will add the
renderers to be displayed. We also need to add systems which the tiles depend
on like PositionSystem2D
, ColorSystem
and MapSystem
.
TestGame:
<TestGame>:
gameworld: gameworld
camera1: camera1
GameWorld:
id: gameworld
gamescreenmanager: gamescreenmanager
size_of_gameworld: 250*1024
system_count: 4
zones: {'general': 100000}
PositionSystem2D:
system_id: 'position'
gameworld: gameworld
zones: ['general']
ColorSystem:
system_id: 'color'
gameworld: gameworld
zones: ['general']
MapSystem:
system_id: 'tile_map'
id: tile_map
gameworld: gameworld
zones: ['general']
GameView:
system_id: 'camera1'
gameworld: gameworld
size: root.size
window_size: root.size
pos: root.pos
do_scroll_lock: False
id: camera1
updateable: True
PositionSystem2D
is necessary for any map because it it responsible for the
tile positions. And MapSystem
holds the relevant data for the map hence that
is necessary too, obviously. ColorSystem
is required if there are shapes in
your map which require coloring. And GameView
is the canvas where we will
render the map's layers.
This is the basic boilerplate KV necessary for rendering the map.
I will start with the basic game app structure of main.py
.
def get_asset_path(asset, asset_loc):
return join(dirname(dirname(abspath(__file__))), asset_loc, asset)
class TestGame(Widget):
def __init__(self, **kwargs):
super(TestGame, self).__init__(**kwargs)
# Init gameworld with all the systems
self.gameworld.init_gameworld(
['position', 'color', 'camera1', 'tile_map'],
callback=self.init_game)
def init_game(self):
self.setup_states()
self.set_state()
def setup_states(self):
# TODO we need to add the state of the systems to this gameworld state
self.gameworld.add_state(state_name='main')
def set_state(self):
self.gameworld.state = 'main'
class YourAppNameApp(App):
pass
if __name__ == '__main__':
YourAppNameApp().run()
We now need to load the systems required for each layer. We will have to
specify parameters for them the same way we fo it in KV files. We will make 3
dicts, one each for Renderer, PolyRenderer and AnimationSystem and pass them
to load_map_systems
util function to create 4 layers.
def __init__(self, **kwargs):
super(TestGame, self).__init__(**kwargs)
# Args required for Renderer init
map_render_args = {
'zones': ['general'],
'frame_count': 2,
'gameview': 'camera1',
'shader_source': get_asset_path('positionshader.glsl', 'assets/glsl')
}
# Args for AnimationSystem init
map_anim_args = {
'zones': ['general'],
}
# Args for PolyRenderer init
map_poly_args = {
'zones': ['general'],
'frame_count': 2,
'gameview': 'camera1',
'shader_source': 'poscolorshader.glsl'
}
# Initialise systems for 4 map layers and get the renderer and
# animator names
self.map_layers, self.map_layer_animators = \
map_utils.load_map_systems(4, self.gameworld,
map_render_args, map_anim_args, map_poly_args)
We will be returned a list of renderers and animators. This list can be added to the gameworld init sequence like so. Renderers and animators require specific states to be set so we have to add these lists while setting states. Modify the corresponding lines with these.
# Init gameworld with all the systems
self.gameworld.init_gameworld(
['position', 'color', 'camera1', 'tile_map']
+ self.map_layers
+ self.map_layer_animators,
callback=self.init_game)
and
def setup_states(self):
# We want renderers to be added and unpaused
# and animators to be unpaused
self.gameworld.add_state(state_name='main',
systems_added=self.map_layers,
systems_unpaused=self.map_layer_animators + self.map_layers)
These systems need to be rendered from bottom to top to preserve the
layer order. And the gameview camera handles rendering of these systems. So we
will set the render order for that camera to match layer index. Add this line
to __init__
.
# Set the camera1 render order to render lower layers first
self.camera1.render_system_order = reversed(self.map_layers)
Next up, we need to populate our systems with entities and for that we need a TileMap loaded with tile data. This data will be obtained from the TMX file. The util module has a function for loading TMX files and registering them with the map manager
def setup_tile_map(self):
# The map file to load
# Change to hexagonal/isometric/isometric_staggered.tmx for other maps
filename = get_asset_path('orthogonal.tmx','assets/maps')
map_manager = self.gameworld.managers['map_manager']
# Load TMX data and create a TileMap from it
map_name = map_utils.parse_tmx(filename, self.gameworld)
setup_tile_map()
should be added to init_gameworld()
so that it is called
after gameworld init.
parse_tmx
takes the filename of the TMX, loads it to a TileMap
, registers it
in the map_manager
with name as the filename without the extension, and
returns that same name.
All that is left to do is to create entities from this TileMap
.
The function for that is init_entities_from_map
. It requires the TileMap
instance and an instance of the gameworld's init_entity
function. It is used
like this:
# Initialise each tile as an entity in the gameworld
map_utils.init_entities_from_map(map_manager.maps[map_name],
self.gameworld.init_entity)
You can add this to setup_tile_map
after parse_tmx
is called and we have
the TileMap
.
This is all we require to load a Tiled map in KivEnt.
Download the source files from here!
Thank you and happy tiling!
EDIT (2016-08-24): Added installation instructions.