Part 2 - Moving ‘@’ around the screen¶
Explanation¶
In this part we will be able to move the @ around the screen. Like Roguelike Tutorial in Python 3 and TCOD we will also add an Action class.
Add this to the actions.py file:
class Action:
pass
class EscapeAction(Action):
pass
class MovementAction(Action):
def __init__(self, dx: int, dy: int):
super().__init__()
self.dx = dx
self.dy = dy
Here we have different classes depending on the action that we need to perform. All classes are inherited from Action.
Now let’s change the main.py file to allow moving of the @ character:
+import sdl2
import sdl2.ext
from sdl2 import SDL_Color
+from actions import Action, EscapeAction, MovementAction
We need the sdl2 library to later import the key codes to identify specific keys. We also added the actions from the actions.py file.
+def run():
sdl2.ext.init()
screen_width = 640
screen_height = 480
+BLACK = SDL_Color(0, 0, 0)
+tile_size = 20
+player_font_size = 20
+player_x = screen_width // 2
+player_y = screen_height // 2
window = sdl2.ext.Window(
"PySDL2 Roguelike Tutorial", size=(screen_width, screen_height)
)
Here we wrapped the code into a run() function to make the code more structured. We also added a definition of the black color. We need the tile_size to calculate the jumps for the @ character. In addition we also fixed the starting position of the @ that will later change depending on the keyboard input.
In the following code we change the size of the font to be equal to a variable
font_manager = sdl2.ext.FontManager(
font_path="C:\\Windows\\Fonts\\arial.ttf",
-size=16,
+size=player_font_size,
color=fg,
bg_color=bg,
)
factory = sdl2.ext.SpriteFactory(renderer=renderer)
Now we insert the player coordinates into the calculation of the @ sprite rectangle.
-x_offset = screen_width // 2 - text.size[0] // 2
-y_offset = screen_height // 2 - text.size[1] // 2
+x_offset = player_x - text.size[0] // 2
+y_offset = player_y - text.size[1] // 2
We also add a loop to process events. In our case it is the keyboard events that move the @. The renderer.clear is needed to clear everything that was drawn in the previous iteration.
+while True:
+ renderer.clear(BLACK)
renderer.copy(text, dstrect=(x_offset, y_offset, text.size[0], text.size[1]))
renderer.present()
Now we add the keyboard event processing loop:
+events = sdl2.ext.get_events()
+for event in events:
+ action = None
+ if event.type == sdl2.SDL_QUIT:
+ action = EscapeAction()
+ elif event.type == sdl2.SDL_KEYDOWN:
+ if event.key.keysym.sym == sdl2.SDLK_UP:
+ action = MovementAction(dx=0, dy=-1)
+ elif event.key.keysym.sym == sdl2.SDLK_DOWN:
+ action = MovementAction(dx=0, dy=1)
+ elif event.key.keysym.sym == sdl2.SDLK_LEFT:
+ action = MovementAction(dx=-1, dy=0)
+ elif event.key.keysym.sym == sdl2.SDLK_RIGHT:
+ action = MovementAction(dx=1, dy=0)
+ elif event.key.keysym.sym == sdl2.SDLK_ESCAPE:
+ action = EscapeAction()
+ if action is None:
+ continue
+ if isinstance(action, MovementAction):
+ player_x += action.dx * tile_size
+ player_y += action.dy * tile_size
+ x_offset = player_x - text.size[0] // 2
+ y_offset = player_y - text.size[1] // 2
+ elif isinstance(action, EscapeAction):
+ sdl2.ext.quit()
+ raise SystemExit()
Let’s have a closer look at the lines.
events = sdl2.ext.get_events()
Reads all the events that were accumulated by SDL2 from the previous call of this line.
for event in events:
action = None
Loops over all accumulated events and sets an empty action at the beginning of the loop
if event.type == sdl2.SDL_QUIT:
action = EscapeAction()
If a quit signal was sent to the program (for example the close window button was clicked), then it is an EscapeAction
elif event.type == sdl2.SDL_KEYDOWN:
if event.key.keysym.sym == sdl2.SDLK_UP:
action = MovementAction(dx=0, dy=-1)
elif event.key.keysym.sym == sdl2.SDLK_DOWN:
action = MovementAction(dx=0, dy=1)
elif event.key.keysym.sym == sdl2.SDLK_LEFT:
action = MovementAction(dx=-1, dy=0)
elif event.key.keysym.sym == sdl2.SDLK_RIGHT:
action = MovementAction(dx=1, dy=0)
Here if the event was keydown event then we check what key was pressed. If the key is an arrow then it is a MovementAction
elif event.key.keysym.sym == sdl2.SDLK_ESCAPE:
action = EscapeAction()
If the key was Esc then it is an EscapeAction.
Now we process the actions depending on what action we have:
if action is None:
continue
If action is empty then check the next event
if isinstance(action, MovementAction):
player_x += action.dx * tile_size
player_y += action.dy * tile_size
x_offset = player_x - text.size[0] // 2
y_offset = player_y - text.size[1] // 2
Here if the action was a movement then we need to change the coordinates of the @
elif isinstance(action, EscapeAction):
sdl2.ext.quit()
raise SystemExit()
If the action was EscapeAction, then shut down SDL2 and finish the program.
Taking into account that we moved everything into run(), we need to execute run() when the file is executed with python main.py:
if __name__ == "__main__":
run()
After running this code with python main.py we see the same picture as in Part 1 but we can move the @ with the keyboard keys
Full code of all files¶
actions.py¶
class Action:
pass
class EscapeAction(Action):
pass
class MovementAction(Action):
def __init__(self, dx: int, dy: int):
super().__init__()
self.dx = dx
self.dy = dy
main.py¶
import sdl2
import sdl2.ext
from sdl2 import SDL_Color
from actions import Action, EscapeAction, MovementAction
def run():
sdl2.ext.init()
screen_width = 640
screen_height = 480
BLACK = SDL_Color(0, 0, 0)
tile_size = 20
player_font_size = 20
player_x = screen_width // 2
player_y = screen_height // 2
window = sdl2.ext.Window(
"PySDL2 Roguelike Tutorial", size=(screen_width, screen_height)
)
window.show()
renderer = sdl2.ext.Renderer(window)
fg = SDL_Color(255, 255, 255)
bg = SDL_Color(0, 0, 0)
font_manager = sdl2.ext.FontManager(
font_path="C:\\Windows\\Fonts\\arial.ttf",
size=player_font_size,
color=fg,
bg_color=bg,
)
factory = sdl2.ext.SpriteFactory(renderer=renderer)
text = factory.from_text("@", fontmanager=font_manager)
x_offset = player_x - text.size[0] // 2
y_offset = player_y - text.size[1] // 2
while True:
renderer.clear(BLACK)
renderer.copy(text, dstrect=(x_offset, y_offset, text.size[0], text.size[1]))
renderer.present()
events = sdl2.ext.get_events()
for event in events:
action = None
if event.type == sdl2.SDL_QUIT:
action = EscapeAction()
elif event.type == sdl2.SDL_KEYDOWN:
if event.key.keysym.sym == sdl2.SDLK_UP:
action = MovementAction(dx=0, dy=-1)
elif event.key.keysym.sym == sdl2.SDLK_DOWN:
action = MovementAction(dx=0, dy=1)
elif event.key.keysym.sym == sdl2.SDLK_LEFT:
action = MovementAction(dx=-1, dy=0)
elif event.key.keysym.sym == sdl2.SDLK_RIGHT:
action = MovementAction(dx=1, dy=0)
elif event.key.keysym.sym == sdl2.SDLK_ESCAPE:
action = EscapeAction()
if action is None:
continue
if isinstance(action, MovementAction):
player_x += action.dx * tile_size
player_y += action.dy * tile_size
x_offset = player_x - text.size[0] // 2
y_offset = player_y - text.size[1] // 2
elif isinstance(action, EscapeAction):
sdl2.ext.quit()
raise SystemExit()
if __name__ == "__main__":
run()