3. Animatie#

In het vorige deel hebben we de achtergrond van het spel getekend. Nu gaan we de speler tekenen. Net als in eerdere games gebruiken we hiervoor weer een Actor variabele. In die games gaven we die Actor één sprite afbeelding mee. Daardoor konden we de speler wel door het venster laten bewegen, maar in de afbeelding van de speler zat géén beweging. In Endless Runner willen we de speler animeren, zodat het lijkt of hij echt loopt.

../_images/dino.gif

Om dit voor elkaar te krijgen, moeten in plaats van één sprite voor de Actor meerdere sprites gebruiken en die afwisselen, zodat het lijkt of de sprite beweegt.

Actor image veranderen#

In Pygame Zero kun je de afbeelding van een Actor veranderen via de .image variabele van de Actor. Probeer het volgende eens uit:

endlessrunner.py#
 1# Vensterinstellingen
 2WIDTH = 800
 3HEIGHT = 600
 4TITLE = 'Endless Runner'
 5
 6# Constanten
 7HORIZON = 400
 8
 9# Actors
10player = Actor('walk00')
11
12# Functie draw_background()
13def draw_background():
14   sky_rect = Rect(0, 0, WIDTH, HORIZON)
15   screen.draw.filled_rect(sky_rect, 'deepskyblue')
16   ground_rect = Rect(0, HORIZON, WIDTH, HEIGHT - HORIZON)
17   screen.draw.filled_rect(ground_rect, 'darkolivegreen4')
18
19# Functie draw()
20def draw():
21   draw_background()
22   player.draw()
23
24# Functie update()
25def update():
26   if player.image == 'walk00':
27      player.image = 'walk01'
28   elif player.image == 'walk01':
29      player.image = 'walk02'
30   elif player.image == 'walk02':
31      player.image = 'walk03'
32   elif player.image == 'walk03':
33      player.image = 'walk00'

Wanneer je deze code runt, zie je een hyperactieve dinosaurus in de linkerbovenhoek van het venster.

../_images/animation01.gif

Zoals je weet, wordt de update() functie 60 keer per seconde automatisch aangeroepen. De frame rate van de animatie is nu dus 60 frames per seconde (fps). Voor onze game is dat veel te snel. Bovendien is deze manier van animeren met een if statement in de update() functie niet heel mooi. Stel je voor dat je in plaats van 4 afbeeldingen 16 afbeeldingen hebt! Dan zou je een heel lang, lelijk if statement krijgen. Om dit probleem op te lossen, gaan we gebruik maken van de Pygame Zero Helper module die je al hebt gedownload en in je endlessrunner map hebt geplaatst. Deze pgzhelper module bevat handige functies voor Pygame Zero, waaronder een functie die het animeren van een Actor een stuk eenvoudiger maakt.

Pygame Zero Helper module#

Om de pgzhelper module te kunnen gebruiken, moet het bestand pgzhelper.py in dezelfde map staan als je codebestand endlessrunner.py. Vervolgens kun je de module importeren in je code en de functies gebruiken. Voeg de volgende regel toe, helemaal aan het begin van je bestand:

endlessrunner.py#
1from pgzhelper import *
2
3# Vensterinstellingen
4WIDTH = 800
5HEIGHT = 600
6TITLE = 'Endless Runner'
7
8# overige code weggelaten

Voor de animatie van onze Actor maken we een lijst van vier sprites. Vervolgens gebruiken we de pgzhelper functie next_image() om telkens de volgende sprite in de lijst te selecteren. Pas je code als volgt aan:

endlessrunner.py#
 1from pgzhelper import *
 2
 3# Vensterinstellingen
 4WIDTH = 800
 5HEIGHT = 600
 6TITLE = 'Endless Runner'
 7
 8# Constanten
 9HORIZON = 400
10
11# Actors
12player = Actor('walk00')
13walk_images = ['walk00', 'walk01', 'walk02', 'walk03']
14player.images = walk_images
15
16# Functie draw_background()
17def draw_background():
18   sky_rect = Rect(0, 0, WIDTH, HORIZON)
19   screen.draw.filled_rect(sky_rect, 'deepskyblue')
20   ground_rect = Rect(0, HORIZON, WIDTH, HEIGHT - HORIZON)
21   screen.draw.filled_rect(ground_rect, 'darkolivegreen4')
22
23# Functie draw()
24def draw():
25   draw_background()
26   player.draw()
27
28# Functie update()
29def update():
30   player.next_image()

In regel 13 maken we een lijst met de namen van de vier sprites die de animatie van de speler vormen. In regel 14 koppelen we de lijst aan de player Actor. In de update() functie is het if statement vervangen door één regel: player.next_image(). Deze regel zorgt ervoor dat de volgende sprite in de lijst wordt geselecteerd. Als de laatste sprite is bereikt, begint de animatie weer bij het begin.

Wanneer je deze code runt, zie je dat de dino nog steeds hyperactief is! Dat komt natuurlijk doordat player.next_image() nog stees 60 keer per seconde wordt aangeroepen. Gelukkig heeft pgzhelper ook hier een oplossing voor. Als we in plaats van de functie next_image() de functie animate() gebruiken, kunnen we de snelheid van de animatie instellen. Wijzig je code als volgt:

endlessrunner.py#
 1from pgzhelper import *
 2
 3# Vensterinstellingen
 4WIDTH = 800
 5HEIGHT = 600
 6TITLE = 'Endless Runner'
 7
 8# Constanten
 9HORIZON = 400
10
11# Actors
12player = Actor('walk00')
13walk_images = ['walk00', 'walk01', 'walk02', 'walk03']
14player.images = walk_images
15player.fps = 10
16
17# Functie draw_background()
18def draw_background():
19   sky_rect = Rect(0, 0, WIDTH, HORIZON)
20   screen.draw.filled_rect(sky_rect, 'deepskyblue')
21   ground_rect = Rect(0, HORIZON, WIDTH, HEIGHT - HORIZON)
22   screen.draw.filled_rect(ground_rect, 'darkolivegreen4')
23
24# Functie draw()
25def draw():
26   draw_background()
27   player.draw()
28
29# Functie update()
30def update():
31   player.animate()

Met een snelheid van 10 frames per seconde ziet de animatie er een stuk beter uit.

../_images/animation02.gif
Functies in de pgzhelper module

Met de pgzhelper module is het animeren van een Actor een fluitje van een cent. In de module zitten nog veel meer handige functies die je kunt gebruiken in je games. Kijk eens op de website van A Posteriori voor meer informatie.

Positioneren#

Ter afsluiting van dit deel gaan we de dino nog even op de goede plek in het venster zetten. Uiteraard is het de bedoeling dat hij op de grond staat. We kunnen hierbij mooi gebruik maken van de bottom eigenschap van de Actor en onze HORIZON constante.

endlessrunner.py#
11# Actors
12player = Actor('walk00')
13walk_images = ['walk00', 'walk01', 'walk02', 'walk03']
14player.images = walk_images
15player.fps = 10
16player.left = 10
17player.bottom = HORIZON

Het resultaat ziet er zo uit:

../_images/position01.gif

Nu loopt onze dinosaurus precies op de horizon, maar eigenlijk is het mooier om hem iets onder de horizon te plaatsen. Pas regel 17 aan als volgt:

endlessrunner.py#
17player.bottom = HORIZON + 45

Uiteraard is het een kwestie van smaak hoe ver je de dino onder de horizon plaatst. Experimenteer gerust met de waarde 45 om te zien wat het effect is.

../_images/position02.gif

Nu hebben we een mooie basis voor onze Endless Runner game. In het volgende deel gaan we de dino laten springen.