Step 12 - Loading a Map From a Map Editor
In this chapter we will start using a map editor called Tiled. Tiled is a popular 2D map editor, it can be used with any game engine, but Arcade has specific integrations for working with Tiled.
We’ll explore how to load maps from Tiled in this tutorial using Arcade’s built-in arcade.TileMap
class
using some maps from the built-in resources that Arcade comes with. We won’t cover actually building a map
in Tiled this tutorial, but if you want to learn more about Tiled check out the resources below:
Download Tiled: https://www.mapeditor.org/
Tiled’s Documentation: https://doc.mapeditor.org/en/stable/
You won’t actually need Tiled to continue following this tutorial. We will be using all pre-built maps included with Arcade. However if you want to experiment with your own maps or changing things, I recommend getting Tiled and getting familiar with it, it is a really useful tool for 2D Game Development.
To start off with, we’re going to remove a bunch of code. Namely we’ll remove the creation of our ground, boxes,
and coin sprites(We’ll leave the player one). Go ahead and remove the following blocks of code from the setup
function.
self.scene.add_sprite_list("Walls", use_spatial_hash=True)
self.scene.add_sprite_list("Coins", use_spatial_hash=True)
for x in range(0, 1250, 64):
wall = arcade.Sprite(":resources:images/tiles/grassMid.png", scale=TILE_SCALING)
wall.center_x = x
wall.center_y = 32
self.scene.add_sprite("Walls", wall)
coordinate_list = [[512, 96], [256, 96], [768, 96]]
for coordinate in coordinate_list:
wall = arcade.Sprite(
":resources:images/tiles/boxCrate_double.png", scale=TILE_SCALING
)
wall.position = coordinate
self.scene.add_sprite("Walls", wall)
for x in range(128, 1250, 256):
coin = arcade.Sprite(":resources:images/items/coinGold.png", scale=COIN_SCALING)
coin.center_x = x
coin.center_y = 96
self.scene.add_sprite("Coins", coin)
These things will now be handled by our map file automatically once we start loading it.
In order to load our map, we will first create a variable for it in __init__
:
self.tile_map = None
Next we will load our map in our setup
function, and then create a Scene from it using
a built-in function Arcade provides. This will give us a drawable scene completely based off
of the map file automatically. This code will all go at the top of the setup
function.
Make sure to replace the line that sets self.scene
with the new one below.
layer_options = {
"Platforms": {
"use_spatial_hash": True
}
}
self.tile_map = arcade.load_tilemap(
":resources:tiled_maps/map.json",
scaling=TILE_SCALING,
layer_options=layer_options
)
self.scene = arcade.Scene.from_tilemap(self.tile_map)
This code will load in our built-in Tiled Map and automatically build a Scene from it. The Scene at this stage is ready for drawing and we don’t need to do anything else to it(other than add our player).
Note
What is layer_options
and where are those values in it coming from?
layer_options
is a special dictionary that can be provided to the load_tilemap
function. This will
send special options for each layer into the map loader. In this example our map has a layer called
Platforms
, and we want to enable spatial hashing on it. Much like we did for our wall
SpriteList
before. For more info on the layer options dictionary and the available keys, check out arcade.TileMap
At this point we only have one piece of code left to change. In switching to our new map, you may have noticed by
the layer_options
dictionary that we now have a layer named Platforms
. Previously in our Scene we were calling
this layer Walls
. We’ll need to go update that reference when we create our Physics Engine.
In the setup
function update the Physics Engine creation to use the the new Platforms
layer:
self.physics_engine = arcade.PhysicsEnginePlatformer(
self.player_sprite, walls=self.scene["Platforms"], gravity_constant=GRAVITY
)
Source Code
1"""
2Platformer Game
3
4python -m arcade.examples.platform_tutorial.12_tiled
5"""
6import arcade
7
8# Constants
9WINDOW_WIDTH = 1280
10WINDOW_HEIGHT = 720
11WINDOW_TITLE = "Platformer"
12
13# Constants used to scale our sprites from their original size
14TILE_SCALING = 0.5
15COIN_SCALING = 0.5
16
17# Movement speed of player, in pixels per frame
18PLAYER_MOVEMENT_SPEED = 5
19GRAVITY = 1
20PLAYER_JUMP_SPEED = 20
21
22
23class GameView(arcade.Window):
24 """
25 Main application class.
26 """
27
28 def __init__(self):
29
30 # Call the parent class and set up the window
31 super().__init__(WINDOW_WIDTH, WINDOW_HEIGHT, WINDOW_TITLE)
32
33 # Variable to hold our texture for our player
34 self.player_texture = None
35
36 # Separate variable that holds the player sprite
37 self.player_sprite = None
38
39 # Variable to hold our Tiled Map
40 self.tile_map = None
41
42 # Replacing all of our SpriteLists with a Scene variable
43 self.scene = None
44
45 # A variable to store our camera object
46 self.camera = None
47
48 # A variable to store our gui camera object
49 self.gui_camera = None
50
51 # This variable will store our score as an integer.
52 self.score = 0
53
54 # This variable will store the text for score that we will draw to the screen.
55 self.score_text = None
56
57 # Load sounds
58 self.collect_coin_sound = arcade.load_sound(":resources:sounds/coin1.wav")
59 self.jump_sound = arcade.load_sound(":resources:sounds/jump1.wav")
60
61 def setup(self):
62 """Set up the game here. Call this function to restart the game."""
63 layer_options = {
64 "Platforms": {
65 "use_spatial_hash": True
66 }
67 }
68
69 # Load our TileMap
70 self.tile_map = arcade.load_tilemap(
71 ":resources:tiled_maps/map.json",
72 scaling=TILE_SCALING,
73 layer_options=layer_options,
74 )
75
76 # Create our Scene Based on the TileMap
77 self.scene = arcade.Scene.from_tilemap(self.tile_map)
78
79 self.player_texture = arcade.load_texture(
80 ":resources:images/animated_characters/female_adventurer/femaleAdventurer_idle.png"
81 )
82
83 self.player_sprite = arcade.Sprite(self.player_texture)
84 self.player_sprite.center_x = 128
85 self.player_sprite.center_y = 128
86 self.scene.add_sprite("Player", self.player_sprite)
87
88 # Create a Platformer Physics Engine, this will handle moving our
89 # player as well as collisions between the player sprite and
90 # whatever SpriteList we specify for the walls.
91 # It is important to supply static to the walls parameter. There is a
92 # platforms parameter that is intended for moving platforms.
93 # If a platform is supposed to move, and is added to the walls list,
94 # it will not be moved.
95 self.physics_engine = arcade.PhysicsEnginePlatformer(
96 self.player_sprite, walls=self.scene["Platforms"], gravity_constant=GRAVITY
97 )
98
99 # Initialize our camera, setting a viewport the size of our window.
100 self.camera = arcade.camera.Camera2D()
101
102 # Initialize our gui camera, initial settings are the same as our world camera.
103 self.gui_camera = arcade.camera.Camera2D()
104
105 # Reset our score to 0
106 self.score = 0
107
108 # Initialize our arcade.Text object for score
109 self.score_text = arcade.Text(f"Score: {self.score}", x=0, y=5)
110
111 self.background_color = arcade.csscolor.CORNFLOWER_BLUE
112
113 def on_draw(self):
114 """Render the screen."""
115
116 # Clear the screen to the background color
117 self.clear()
118
119 # Activate our camera before drawing
120 self.camera.use()
121
122 # Draw our Scene
123 self.scene.draw()
124
125 # Activate our GUI camera
126 self.gui_camera.use()
127
128 # Draw our Score
129 self.score_text.draw()
130
131 def on_update(self, delta_time):
132 """Movement and Game Logic"""
133
134 # Move the player using our physics engine
135 self.physics_engine.update()
136
137 # See if we hit any coins
138 coin_hit_list = arcade.check_for_collision_with_list(
139 self.player_sprite, self.scene["Coins"]
140 )
141
142 # Loop through each coin we hit (if any) and remove it
143 for coin in coin_hit_list:
144 # Remove the coin
145 coin.remove_from_sprite_lists()
146 arcade.play_sound(self.collect_coin_sound)
147 self.score += 75
148 self.score_text.text = f"Score: {self.score}"
149
150 # Center our camera on the player
151 self.camera.position = self.player_sprite.position
152
153 def on_key_press(self, key, modifiers):
154 """Called whenever a key is pressed."""
155
156 if key == arcade.key.ESCAPE:
157 self.setup()
158
159 if key == arcade.key.UP or key == arcade.key.W:
160 if self.physics_engine.can_jump():
161 self.player_sprite.change_y = PLAYER_JUMP_SPEED
162 arcade.play_sound(self.jump_sound)
163
164 if key == arcade.key.LEFT or key == arcade.key.A:
165 self.player_sprite.change_x = -PLAYER_MOVEMENT_SPEED
166 elif key == arcade.key.RIGHT or key == arcade.key.D:
167 self.player_sprite.change_x = PLAYER_MOVEMENT_SPEED
168
169 def on_key_release(self, key, modifiers):
170 """Called whenever a key is released."""
171
172 if key == arcade.key.LEFT or key == arcade.key.A:
173 self.player_sprite.change_x = 0
174 elif key == arcade.key.RIGHT or key == arcade.key.D:
175 self.player_sprite.change_x = 0
176
177
178def main():
179 """Main function"""
180 window = GameView()
181 window.setup()
182 arcade.run()
183
184
185if __name__ == "__main__":
186 main()