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 :class`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
9SCREEN_WIDTH = 800
10SCREEN_HEIGHT = 600
11SCREEN_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 MyGame(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__(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_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(":resources:tiled_maps/map.json", scaling=TILE_SCALING, layer_options=layer_options)
71
72 # Create our Scene Based on the TileMap
73 self.scene = arcade.Scene.from_tilemap(self.tile_map)
74
75 self.player_texture = arcade.load_texture(":resources:images/animated_characters/female_adventurer/femaleAdventurer_idle.png")
76
77 self.player_sprite = arcade.Sprite(self.player_texture)
78 self.player_sprite.center_x = 128
79 self.player_sprite.center_y = 128
80 self.scene.add_sprite("Player", self.player_sprite)
81
82 # Create a Platformer Physics Engine, this will handle moving our
83 # player as well as collisions between the player sprite and
84 # whatever SpriteList we specify for the walls.
85 # It is important to supply static to the walls parameter. There is a
86 # platforms parameter that is intended for moving platforms.
87 # If a platform is supposed to move, and is added to the walls list,
88 # it will not be moved.
89 self.physics_engine = arcade.PhysicsEnginePlatformer(
90 self.player_sprite, walls=self.scene["Platforms"], gravity_constant=GRAVITY
91 )
92
93 # Initialize our camera, setting a viewport the size of our window.
94 self.camera = arcade.camera.Camera2D()
95
96 # Initialize our gui camera, initial settings are the same as our world camera.
97 self.gui_camera = arcade.camera.Camera2D()
98
99 # Reset our score to 0
100 self.score = 0
101
102 # Initialize our arcade.Text object for score
103 self.score_text = arcade.Text(f"Score: {self.score}", x=0, y=5)
104
105 self.background_color = arcade.csscolor.CORNFLOWER_BLUE
106
107 def on_draw(self):
108 """Render the screen."""
109
110 # Clear the screen to the background color
111 self.clear()
112
113 # Activate our camera before drawing
114 self.camera.use()
115
116 # Draw our Scene
117 self.scene.draw()
118
119 # Activate our GUI camera
120 self.gui_camera.use()
121
122 # Draw our Score
123 self.score_text.draw()
124
125 def on_update(self, delta_time):
126 """Movement and Game Logic"""
127
128 # Move the player using our physics engine
129 self.physics_engine.update()
130
131 # See if we hit any coins
132 coin_hit_list = arcade.check_for_collision_with_list(
133 self.player_sprite, self.scene["Coins"]
134 )
135
136 # Loop through each coin we hit (if any) and remove it
137 for coin in coin_hit_list:
138 # Remove the coin
139 coin.remove_from_sprite_lists()
140 arcade.play_sound(self.collect_coin_sound)
141 self.score += 75
142 self.score_text.text = f"Score: {self.score}"
143
144 # Center our camera on the player
145 self.camera.position = self.player_sprite.position
146
147 def on_key_press(self, key, modifiers):
148 """Called whenever a key is pressed."""
149
150 if key == arcade.key.ESCAPE:
151 self.setup()
152
153 if key == arcade.key.UP or key == arcade.key.W:
154 if self.physics_engine.can_jump():
155 self.player_sprite.change_y = PLAYER_JUMP_SPEED
156 arcade.play_sound(self.jump_sound)
157
158 if key == arcade.key.LEFT or key == arcade.key.A:
159 self.player_sprite.change_x = -PLAYER_MOVEMENT_SPEED
160 elif key == arcade.key.RIGHT or key == arcade.key.D:
161 self.player_sprite.change_x = PLAYER_MOVEMENT_SPEED
162
163 def on_key_release(self, key, modifiers):
164 """Called whenever a key is released."""
165
166 if key == arcade.key.LEFT or key == arcade.key.A:
167 self.player_sprite.change_x = 0
168 elif key == arcade.key.RIGHT or key == arcade.key.D:
169 self.player_sprite.change_x = 0
170
171
172def main():
173 """Main function"""
174 window = MyGame()
175 window.setup()
176 arcade.run()
177
178
179if __name__ == "__main__":
180 main()