Program Arcade Games
With Python And PygameChapter 8: Inleiding tot animatie
8.1 De botsende rechthoek
Om te beginnen met de eerste animatie vertrekken we van het basis pygame programme van hoofdstuk 5 dat een blanko scherm toont. De code van pygame_base_template.py kan je hier vinden :
ProgramArcadeGames.com/python_examples/f.php?file=pygame_base_template.py
We gaan een programma maken dat een witte rechthoek laat botsen op het scherm met een zwarte achtergrond. Je mag zelf je kleuren kiezen, zorg er alleen voor dat de achtergrond en de rechthoek een verschillende kleur hebben!
Eerst start je met de basis template en wijzig je de achtergrond van wit naar zwart. Dit moet je doen in lijn 46.
screen.fill(black)
Daarna teken je een rechthoek die we willen laten bewegen. Een simpele rechthoek is genoeg. Deze code hoort thuis nadat het scherm wordt gewist en voor het nieuwe scherm wordt getoont (geflipt).
pygame.draw.rect(screen, white, [50, 50, 50, 50])
Elke keer dat we door de lus gaan tekenen we de rechthoek met coordinaten (x,y) of dezelfde locatie (50,50). Het zijn de eerste twee 50'en in de list die de positie bepalen. Zolang deze twee getallen niet wijzigen zal de rechthoek niet bewegen.
De rechthoek is 50 pixels breed en 50 pixels hoog. De grootte van de rechthoek wordt bepaalt door de laatste twee getallen van de list. We kunnen deze rechthoek eigenlijk een vierkant noemen aangezien de breedte en de hoogte gelijk zijn. Toch blijf ik het een rechthoek noemen aangezien elk vierkant een rechthoek is en afhankelijk van de resolutie van het scherm niet alle pixels vierkantig zijn. Indien je meer hierover wil weten zoek je maar wat meer info op over Pixel Aspect Ratio.
Hoe kunnen we de positie nu wijzigen in plaats van de rechthoek altijd te tekenen op (50, 50)? Door een variabele te gebruiken natuurlijk! De code hieronder zet alvast de eerste stap :
rect_x = 50 pygame.draw.rect(screen, white, [rect_x, 50, 50, 50])
Om de rechthoek naar rechts te bewegen moet je x verhogen met 1 voor elk frame. De code is bijna juist, maar we zijn er nog niet helemaal :
rect_x = 50 pygame.draw.rect(screen, white, [rect_x, 50, 50, 50]) rect_x += 1
Het probleem is de bovenste code is dat rect_x elke keer opnieuw op 50 gezet wordt in de lus? Om dit op te lossen moeten we de initialisatie buiten de lus houden. De volgende code zal de rechthoek mooi laten schuiven naar rechts.
# Startpositie van x voor de rechthoek # Dit moet buiten de lus. rect_x = 50 # -------- Main Program Loop ----------- while not done: for event in pygame.event.get(): # De user heeft iets gedaan if event.type == pygame.QUIT: # De user wil stoppen done = True # Flag dat we willen stoppen # Zet de achtergrond screen.fill(black) pygame.draw.rect(screen, white, [rect_x, 50, 50, 50]) rect_x += 1
Om de rechthoek sneller te verschuiven kunnen we rect_x verhogen met 5 in plaats van met 1 :
rect_x += 5
We kunnen de code nu uitbreiden voor zowel x als y, zodanig dat de rechthoek naar rechtsonder gaat :
# Startpositie van de rechthoek rect_x = 50 rect_y = 50 # -------- Main Program Loop ----------- while not done: for event in pygame.event.get(): if event.type == pygame.QUIT: done = True # Zet de achtergrond screen.fill(black) # Teken de rechthoek pygame.draw.rect(screen, white, [rect_x, rect_y, 50, 50]) # Verplaats de rechthoek rect_x += 5 rect_y += 5
De richting en de snelheid van de rechthoek kunnen we bewaren in een vector. Dit maakt met makkelijk om de richting en de snelheid van een verplaatsend object aan te passen. Het volgende stukje coding toont het gebruik van variabelen voor de snelheid en bewaart de gegevens in x en y in plaats van (5,5).
# Startpositie van de rechthoek rect_x = 50 rect_y = 50 # snelheid en richting van de rechthoek rect_change_x = 5 rect_change_y = 5 # -------- Main Program Loop ----------- while done == False: for event in pygame.event.get(): # user geeft iets in if event.type == pygame.QUIT: # user wil stoppen done = True # Flag dat we uit de lus willen # zet de achtergrond screen.fill(black) # tekent de rechthoek pygame.draw.rect(screen, white, [rect_x, rect_y, 50, 50]) # verplaatst het startpunt van de rechthoek rect_x += rect_change_x rect_y += rect_change_y
Eenmaal dat de rechthoek tegen de rand van het scherm, blijft hij verder gaan en verdwijnt hij van het scherm. Niets dat ervoor zorgt dat de rechthoek terug wordt gebotst. Om er voor te zorgen de rechthoek verder naar rechts gaat eenmaal het tegen de bodem komt, moeten we rect_change_y wijzigen van 5 naar -5 van zodra de rechthoek de bodem raakt. De rechthoek is op de bodem van zodra rect_ygroter is dan de hoogte van het scherm. De code hieronder controleert daarop en verandert dan de richting :
# Botsen van de rechthoek indien nodig. if rect_y > 450: rect_change_y = rect_change_y * -1
Waarom moeten we rect_y controleren op 450 als het scherm 500 pixels hoog is? Omdat de startpunt linksboven is en de hoogte van de rechthoek ook dient meegerekent te worden natuurlijk. Wannner anders de rechthoek op 500 komt wordt deze getekent van 500 tot 550 en is die volledig buiten het scherm. Zie figuur 8.1.
Rekening houdend dat de rechthoek 50 pixels hoog is, is de correcte botspositie
:
$500-50=450$.
De code hieronder zorgt ervoor dat de rechthoek kan botsen tegen alle kanten voor een window van 700x400.
# Bots de rechthoek indien nodig if rect_y > 450 or rect_y < 0: rect_change_y = rect_change_y * -1 if rect_x > 650 or rect_x < 0: rect_change_x = rect_change_x * -1
Geintresseerd om moeilijkere vormen te tekenen dan een rechthoek? Verschillende teken commando's kunnen gebruikt worden op basis van rect_x en rect_y. De code hieronder tekent een rode rechthoek binnen een witte rechthoek. De rode rechthoek heeft een verschuiving van 10 pixels in de x,y richting van de linkerbovenhoek van de witte rechthoek. Het is eveneens 20 pixels kleiner dan de omtrek van de witte rechthoek. Zie figuur 8.2.
# Teken een rode rechthoek met een witte rechthoek rond pygame.draw.rect(screen, white, [rect_x, rect_y, 50, 50]) pygame.draw.rect(screen, red, [rect_x + 10, rect_y + 10 ,30, 30])
8.2 Sneeuw animatie
Is animatie van 1 item niet genoeg? Wat met honderden objecten in 1 keer? Hier is een voorbeeld gebruikmakend van de techniek van hoofdstuk 8.1 om sneeuwvlokjes te laten vallen.
8.2.1 Uitleg van de code
Om te beginnen met dit programma, starten we met onze basis pygame template dat een blanko scherm toont. Opnieuw, de source pygame_base_template.py kan je hier vinden :
ProgramArcadeGames.com/python_examples/show_file.php?file=pygame_base_template.py
Het is mogelijk om de x en y positie van dingen zoals sterre, sneeuw, regen te creeren door willekeurige getallen. De simpelste manier om deze te genereren is door gebruik te maken van een for lus en cirkels te tekenen met een wilekeurige x en y positie. Probeer deze code op de juiste plaats te zetten binnen de while lus.
for i in range(50): x = random.randrange(0, 400) y = random.randrange(0, 400) pygame.draw.circle(screen, white, [x, y], 2)
Probeer het en je zal zien dat deze code een vreemd probleem heeft! 20 keer per seconde. Elke keer dat we door de lus gaan, wordt de sneeuw getekent op een nieuwe willekeurige positie. Probeer het aantal sneeuwvlokjes aan te passen en kijk hoe het beeld wijzigt.
Het is duidelijk dat we de sneeuwvlokjes op dezelfde plaats willen houden en ze niet 20 keer per seconde op een nieuwe plaats willen tonen. We moeten dus een lijst bijhouden van waar de sneeuwvlokjes zich bevinden. Het programma kan hier gebruik maken van een python list. Dit moet wel gebeuren voor de hoofdlus (the main loop), anders gaat het programma 50 nieuwe sneeuwvlokjes aanmaken elke 1/20ste van een seconde.
for i in range(50): x = random.randrange(0, 400) y = random.randrange(0, 400) snow_list.append([x, y])
Eenmaal de locatie is toegevoegd van de sneeuwvlokjes, kunnen deze benaderd worden zoals een gewone list. The volgende code zal zowel de x als de y coordinaat printen van de eerste positie, bewaard in positie nul :
print(snow_list[0])
Wat als we de x en de y coordinaten willen bewaren? We hebben een lists binnenin een list. De buitenste lijst (main list) bevat de coordinaten. Binnenin die list, is er telkens een andere list met de x (positie 0), en de y coordinaat (position 1). Bijvoorbeeld, we hebben drie coordinaten :
[[34, 10], [10, 50], [20, 18]]
Om de y coordinaat op positie 0 te printen selecteren we eerst coordinaat 0, en dan de y waarde die staat op positie 1. De code ziet er als volgt uit :
print(snow_list[0][1])
Om de x waarde van de 21ste coordinaat (positie 20)te printen, selecteer je eerst coordinaat 20 en dan de waarde op positie 0 :
print(snow_list[20][0])
Binnenin de (main) while lus, wordt er een for lus gebruikt om telkens de items te tekenen van de sneeuw list. Denk eraan, len(snow_list) geeft je terug hoeveel elementen er in de sneeuwvlokkenlijst zitten.
# Behandel elke sneeuwvlok for i in range(len(snow_list)): # Tekenen van de sneeuwvlok pygame.draw.circle(screen, white, snow_list[i], 2)
Denk eraan, er zijn twee types van for lussen. Het andere type lus kan gebruikt worden en dan ziet die er als volgt uit :
# Neem een COPY van elke positie van een sneeuwvlok uit de lijst for xy_coord in snow_list: # Teken de sneeuwvlok pygame.draw.circle(screen, white, xy_coord, 2)
Omdat we echter het plan hebben om de locatie van de sneeuwvlokken te wijzigen, kunnen we dit type van for lus niet gebruiken.
Het doel is nu om al die objecten (sneeuwvlokjes) naar omlaag te doen vallen en daarom moeten we de for lus uitbreiden en de y coordinaat telkens verhogen :
# Behandel elke sneeuwvlok in de list for i in range(len(snow_list)): # Teken de sneeuwvlok pygame.draw.circle(screen, white, snow_list[i], 2) # Verplaats de sneeuwvlok met 1 pixel naar beneden snow_list[i][1] += 1
Dit zorgt ervoor dat de sneeuw naar beneden valt, maar wanneer het sneeuwvlokje van het scherm valt komt er geen nieuw te voorschijn. Door de onderstaande code komt er telkens een nieuw sneeuwvlokje bij op een willekeurige positie, wanneer er eentje onderaan verdwijnt :
# Wanneer het sneeuwvlokje verdwenen is van het scherm. if snow_list[i][1] > 400: # breng het sneeuwvlokje opnieuw naar boven. y = random.randrange(-50, -10) snow_list[i][1] = y # Geef een nieuwe x positie x = random.randrange(0, 400) snow_list[i][0] = x
8.2.2 De volledige listing
""" Animating multiple objects using a list. Sample Python/Pygame Programs Simpson College Computer Science http://programarcadegames.com/ http://simpson.edu/computer-science/ Explanation video: http://youtu.be/Gkhz3FuhGoI """ # Import a library of functions called 'pygame' import pygame import random # Initialize the game engine pygame.init() BLACK = [0, 0, 0] WHITE = [255, 255, 255] # Set the height and width of the screen SIZE = [400, 400] screen = pygame.display.set_mode(SIZE) pygame.display.set_caption("Snow Animation") # Create an empty array snow_list = [] # Loop 50 times and add a snow flake in a random x,y position for i in range(50): x = random.randrange(0, 400) y = random.randrange(0, 400) snow_list.append([x, y]) clock = pygame.time.Clock() # Loop until the user clicks the close button. done = False while not done: for event in pygame.event.get(): # User did something if event.type == pygame.QUIT: # If user clicked close done = True # Flag that we are done so we exit this loop # Set the screen background screen.fill(BLACK) # Process each snow flake in the list for i in range(len(snow_list)): # Draw the snow flake pygame.draw.circle(screen, WHITE, snow_list[i], 2) # Move the snow flake down one pixel snow_list[i][1] += 1 # If the snow flake has moved off the bottom of the screen if snow_list[i][1] > 400: # Reset it just above the top y = random.randrange(-50, -10) snow_list[i][1] = y # Give it a new x position x = random.randrange(0, 400) snow_list[i][0] = x # Go ahead and update the screen with what we've drawn. pygame.display.flip() clock.tick(20) # Be IDLE friendly. If you forget this line, the program will 'hang' # on exit. pygame.quit()
In dit voorbeeld bewegen de sneeuwvlokjes zich allemaal in dezelfde richting. Wat als elk element zich moet bewegen op een individuele manier, en in zijn eigen richting? Als je dat nodig heby moet je wachten tot hoofdstuk 13, over hoe je te werk gaat met classen. Oefening 8 toont je hoe je te werk moet gaan met honderden verschillende elementen die elk hun eigen richting uitgaan.
8.3 3D Animatie
Een uitbreiding van een 2D omgeving naar een 3D omgeving, compleet met spelbewegingen is niet zo moeilijk als het er uit ziet. Alhoewel het buiten dit boek gaat, is het toch de moeite om het even aan te raken.
Er is een gratis beschikbaar 3D programma Blender welke beschikt over een “game engine” dat de programmeurs toelaat om 3D games te maken. De 3D objecten in het spel kunnen via Pyton code bestuurt worden om de nodige acties te doen in het spel.
Kijk naar figuur 8.3. Dit toont een groen dienblad met verschillende objecten op. Het blauwe object wordt gecontrolleerd door een Python script. Het object kan je besturen en tegen de andere items laten botsen. Het script , dat je hieronder kan vinden heeft veel gelijkenissen met een 2D programma. Er is een hoofdlus, er zijn x en y coordinaten en de variabelen controleren de vectors.
De hoofdlus is onder controle van Blender. De python code die getoont wordt in de listing wordt elke keer opgeroepen door Blender bij het renderen van een frame. Dit is de reden waarom de Python code geen hoofdlus bevat. Ze bestaat reeds ergens anders.
Het blauwe object heeft de positie bewaard in x,y,z formaat en kan benaderd worden via wijzigingen van de blueobject.position variabele. De array positie 0 bewaart x, positie 1 bewaart y, en positie 2 bewaart de z locatie.
Naast de change_x en change_y variabelen die gebruikt
worden in het 2D voorbeeld, gebruikt dit Blender voorbeeld ook assosiatieve array posities :
blueObject["x_change"]
blueObject["y_change"]
De if statements checken wanneer de randen worden geraakt of wanneer de richting van het object dient verandert te worden. Hoewel we in een 2D spel gebruik maken van pixels, kunnen de posities van objecten ook bepaald worden door reele getallen. Omd e positie van een object te tonen tussen 5 en 6 is het toegestaan om deze op 5,5 te zetten.
# Importeer de Blender Game Engine import bge # Vraag de referentie van het blauwe object cont = bge.logic.getCurrentController() blueObject = cont.owner # Print de x,y coordinaten van het blauwe object print(blueObject.position[0], blueObject.position[1] ) # Wijzig de x,y coordinates volgens de x_change en de # y_change. x_change en y_change zijn spel eigenschappen # die geassocieert zijn met het blauwe object. blueObject.position[0] += blueObject["x_change"] blueObject.position[1] += blueObject["y_change"] # Check of het object aan de rand komt. # Indien dit zo is keer je de richting om en dit voor alle zijden. if blueObject.position[0] > 6 and blueObject["x_change"] > 0: blueObject["x_change"] *= -1 if blueObject.position[0] < -6 and blueObject["x_change"] < 0: blueObject["x_change"] *= -1 if blueObject.position[1] > 6 and blueObject["y_change"] > 0: blueObject["y_change"] *= -1 if blueObject.position[1] < -6 and blueObject["y_change"] < 0: blueObject["y_change"] *= -1
Blender kan je downloaden van :
http://www.blender.org/
Hier kan je een volledig Blender voorbeeld vinden :
ProgramArcadeGames.com/chapters/08_intro_to_animation/simple_block_move.blend.
8.4 Herhaling
8.4.1 Multiple Choice kwis
Click here for a multiple-choice quiz.
8.4.2 Korte inhoud werkblad
Click here for the chapter worksheet.
8.4.3 Oefening
Click here for the chapter lab.
You are not logged in. Log in here and track your progress.
English version by Paul Vincent Craven
Spanish version by Antonio Rodríguez Verdugo
Russian version by Vladimir Slav
Turkish version by Güray Yildirim
Portuguese version by Armando Marques Sobrinho and Tati Carvalho
Dutch version by Frank Waegeman
Hungarian version by Nagy Attila
Finnish version by Jouko Järvenpää
French version by Franco Rossi
Korean version by Kim Zeung-Il
Chinese version by Kai Lin