Program Arcade Games
With Python And Pygame

Chapter 5: Inleiding tot Graphics

Nu je met lussen kan werken, is de tijd gekomen om te leren werken met graphics. Dit hoofdstuk bevat :

5.1 Computer Coordinaten Systeem

Video: Het coordinaten systeem voor computer graphics

Het Cartesiaans coordinaten systeem, zoals getoont in Figuur 5.1 (Wikimedia Commons), is het systeem dat de meeste mensen gebruiken wanneer ze graphics plotten. Dit is het systeem dat in de school wordt aangeleerd. De computer gebruikt een gelijkaardig, maar toch iets verschillend coordinaten systeem. Om te begrijpen waarom er een verschil is hebben we een beetje computergeschiedenis nodig.

fig.Cartesian_coordinates_2D
Figure 5.1: Cartesiaans coordinaten systeem

Tijdens de vroege jaren 80, waren de meeste computers text-based en was er nog geen sprake van graphics. Figuur 5.2 (Wikimedia Commons) toont een oude rekenblad programma dat werkte onder Apple ][ computer die populair was in de jaren 80. Wanneer er tekst moest gezet worden op het scherm dan starte de programmeur op lijn 1. Het had zo 24 lijnen en elke lijn had 40 tekens.

fig.apple3screen
Figure 5.2: Vroegere tekst scherm van Apple

Zelfs met gewone tekst was het mogelijk om eenvoudige tekeningen te maken door gebruik te maken van de tekens op het toetsenbord. Kijk maar naar de poes in Figuur 5.3 en kijk hoe zorgvuldig ze is getekent. Met deze kunst werden de tekens gepositioneerd vertrekkende vanaf lijn 1 bovenaan.

fig.simpleASCII
Figure 5.3: Text screen

Later werden de tekens uitgebreid met primitieve tekeningen of vormen. Ook konden de tekens getoont worden in verschillende kleuren. Zoals je kan zien in Figuur 5.4 worden de graphics beter. Zoek maar op het web naar “ASCII art” en je zal nog vele andere voorbeelden vinden.

fig.spacewar
Figure 5.4: Spaceware tekst screen

Van zodra de computers ook de mogelijkheden hadden om individuele pixels te gebruiken voor graphics, raakte het text-based coordinaten systeem vast.

fig.Computer_coordinates_2D
Figure 5.5: Computer coordinaten systeem

De $x$ coordinaat werkt hetzelfde als in het Cartesiaans coordinaten systeem. Maar de $y$ coordinaat is omgekeerd. In plaats van de nul $y$ coordinaat onderaan de grafiek zoals in een Cartesiaanse grafiek, wordt de nul $y$ coordinaat bovenaan het scherm gezet met de computer. Als de $y$ waarde omhoog gaat, zal de computercoordinaat zijn positie verplaatsen naar onder, net zoals met de tekstlijnen en niet zoals bij de standard Cartesiaanse grafieken. See Figure 5.5.

Merk ook op dat het scherm de rechtsonderliggende kwadrant omvat, terwijl het Cartesiaanse coordinaten systeem het rechtsbovenliggende kwadrant omvat. Het is mogelijk om elementen te tekenen met negatieve coordinaten, maar ze zullen niet op het scherm staan. Dit is wel interessant wanneer er bijvoorbeeld een deel van een vorm niet volledig op het scherm moet staan. De computer weet dan wat er op het scherm moet komen en wat niet en de programmeur moet er niet van wakker liggen.

5.2 Pygame Library

Video: Het openen van een window

Om het werken met graphics een stuk makkelijker te maken, gaan we gebruik maken van Pygame. Pygame is een bibliotheek (library) van code dat geschreven is door andere mensen en dat het ons veel makkelijker maakt om :

Het eerste wat een Pygame programma moet doen is het laden en initialiseren van de Pygame library. Elk programma dat gebruik maakt van Pygame zou moeten starten met volgende lijnen code :

# Importeer de library 'pygame'
import pygame
# Initialiseer de game engine
pygame.init()

Wanneer je Pygame nog niet hebt geinstalleerd kan ik je verwijzen naar het hoofdstuk Voor we gaan starten. Als Pygame niet geinstalleerd is op je computer krijg je een foutmelding bij het statement : import pygame.

Noem geen enkel bestand “pygame.py”

Belangrijk : De import pygame kijkt naar een bibliotheek met de naam pygame. Wanneer een programmeur een nieuw programma maakt met de naam pygame.py, dan zal de computer dit bestand importeren. Dat zal ervoor zorgen dat de pygame programma's niet meer werken tot zolang het bestand pygame.py daar blijft staan.

5.3 Kleuren

De volgende stap is het toevoegen van varabelen om de kleuren van het programma te definieren. Kleuren worden gedefinieerd met een lijst (list) van 3 basiskleuren : rood, groen en blauw. Heb je ooit al gehoord van een RGB monitor. Vandaar komt de term : Red-Green-Blue. Met de oudere computerschermen kon je de individuele RGB kleuren zien, door heel dicht bij het scherm te komen (Tot je moeder vroeg om een beetje verder van de TV te gaan). Vandaag lukt dat zo niet meer met de huidige hoge resolutie computerschermen.

Elk element van het RGB trio is een getal van 0 tot 255. Nul betekent niets van die kleur en 255 vertelt het computerscherm dat hij zoveel als mogelijk moet tonen van die kleur. Door de drie kleuren dan te combineren, krijg je dan de kleur die je wil. Wanneer alle drie de kleuren op 255 staan krijg je wit. (Dit is wel verschillend met inkt of verf.)

Lijsten (lists) in Python worden omringd door vierkante haken of ronde haken. (In hoofdstuk 7 worden lijsten in detail uitgelegd en het verschil tussen de twee types.) Individuele getallen in de lijsten worden gescheiden door kommas.) Hieronder is er een voorbeeld die variabelen creert en ze toevoegt tot een lijst van 3 getallen. Deze lijsten zullen dan later gebruikt worden om een kleur te specifieren.

# Defieren van kleuren
zwart    = (   0,   0,   0)
wit    = ( 255, 255, 255)
groen    = (   0, 255,   0)
rood      = ( 255,   0,   0)

Gebruik de interactieve shell in IDLE en probeer om de variabelen te definieren en ze uit te printen. Wanneer de bovenstaande kleuren niet de kleuren zijn die je wil, kan je ze zelf definieren. Om een kleur te kiezen, zoek je een “color picker” zoals die in Figuur 5.6. Een voorbeeld van een kleurenzoeker is :
http://www.colorpicker.com/

fig.colorpicker
Figure 5.6: Color picker

Extra: Sommige kleurenzoekers tonen hun kleuren hexadecimaal. Je kan een hexadecimaal getal ingeven vanaf 0x. Bijvoorbeeld :

wit = (0xFF, 0xFF, 0xFF)

Het kan ook zijn dat het programma de waarde $\pi$ nodig hebben om bogen te tekenen, dus is het eigenlijk het ogenblik om de variabele te definieren die de waarde $\pi$ bevat. (Het is ook mogelijk om de wiskunde bibliotheek math.pi te importeren.)

pi = 3.141592653

5.4 Open een Window

Tot nu toe moesten de programma's die we hebben gemaakt enkel tekst op het scherm brengen. Deze programma's openden geen aparte window zoals de meeste moderne programma's dit doen. De code om een window te openen is niet moeilijk. Hieronder staat de code die een window maakt van 700 pixels breed en 500 hoog :

# Zet de breedte en de hoogte van het scherm
size = (700,500)
screen = pygame.display.set_mode(size)

Waarom set_mode? Waarom niet open_window? De reden is dat dit commando veel meer kan dan alleen maar een window kan openen. Het kan ook spelletjes starten in fullscreen mode. Dit zorgt ervoor dat de start menu en de statusbalken verdwijnen en dat de controle volledig naar het fullscreen spel gaat. Omdat dit nog iets te complex is, en omdat de meeste mensen een windowed game verkiezen gaan we de discussie uit de weg tussen windowed games en fullscreen games. Indien je toch meer info wil over fullscreen games, dan kan je die vinden in de documentatie bij pygames's display command.

Waarom size=(700,500) en niet size=700,500? Het is namelijk dezelfde reden als waarom we haakjes hebben gezet rond de kleurendefinities. Python kan geen 2 getallen (de breedte en de hoogte) bewaren in 1 variabele. De enige manier dat dit kan is door ze te stockeren in een lijst (list). Lists hebben ofwel ronde ofwel vierkante haakjes. (Technisch gezien worden ronde haakjes rond getallen een tuple of een immutable list (onveranderlijke lijst) genoemd. Lists met vierkante haken noemen we gewoon lists. Een ervaren Python gebruiker zal misschien ineenkrimpen wanneer we een lijst van getallen met haakjes rond toch een list noemen in plaats van een tuple.) Lists worden verder behandeld in hoofdstuk 7.

Om je window een titel te geven (verschijnt dan in de title bar) doe je dit als volgt :

pygame.display.set_caption("Professor Craven's Cool Game")

5.5 Interactie met de user

Met deze code wordt er een window gecreerd en het programma gaat hangen. De gebruiker of user heeft geen interactie met het scherm. Hij kan het zelfs niet sluiten. Al deze acties moeten geprogrammeerd worden. De code waar het programma wacht in een lus tot wanneer de user op “exit.” klikt moet nog worden toegevoegd.

Dit is het meest complexe deel van het programma, en je hoeft het nog niet meteen volledig te begrijpen. Maar het is nodig om een idee te hebben van wat het doet. Het is wel een goed idee om het al eens te bekijken en te bestuderen.

#Blijf in de lus tot de user op close klikt.
done = False
 
# Wordt gebruikt om de refresh snelheid van het scherm te beheren
clock = pygame.time.Clock()
 
# -------- Lus Hoofdprogramma -----------
while done == False:
    # ALLE EVENTS(ACTIES) WORDEN HIERONDER BEHANDELD
    for event in pygame.event.get(): # User heeft iets gedaan
        if event.type == pygame.QUIT: # Wanneer de user op close klikt
            done = True # Flag om uit de lus te kunnen springen
    # ALLE EVENTS (ACTIES) WORDEN HIERBOVEN BEHANDELD
 
 
    # ALLE SPEL LOGICA KOMT HIERONDER TE STAAN

    # ALLE SPEL LOGICA KOMT HIERBOVEN TE STAAN


    # ALLE CODE OM IETS OP HET SCHERM TE BRENGEN VOLGT HIERONDER
     
    # ALLE CODE OM IETS OP HET SCHERM TE BRENGEN STAAT HIERBOVEN     
    # De limiet is frames per seconde
    clock.tick(20)

Eventueel kunnen we nog de code toevoegen om het toetsenbord en de muis te beheren. Deze code moet terechtkomen tussen de commentaar van het beheren van de events. Code om te bekijken of er kogels worden afgevuurd en hoe objecten zich moeten bewegen horen thuis in de spel logica. Daar hebben we het later over. Code om objecten op het scherm te zetten horen thuis in de volgende commentaar.

5.5.1 Het Lus Event

Pas op!

Opgepast! Een van de meest frustrerende problemen die programmeurs mee te maken hebben is dat ze een zootje maken van de event processings lus. Deze “event processing” code behandelt alle toetsaanslagen, muiskliks, en sommige andere types van events. Bijvoorbeeld :

    for event in pygame.event.get(): 
        if event.type == pygame.QUIT:
            print("User vraagt om te stoppen.")
        elif event.type == pygame.KEYDOWN:
            print("User drukt een toets in.")
        elif event.type == pygame.KEYUP: 
            print("User laat de toets los.")
        elif event.type == pygame.MOUSEBUTTONDOWN: 
            print("User drukt een muisknop in")

De events (zoals het indrukken van toetsen) vormen samen een list. Het programma gebruikt een for lus om door elke event te gaan. Door gebruik te maken van een reeks if statements kunnen we te weten komen welk soort van event er zich voordoet, en de wanneer deze event zich voordoet wordt de code van het if statement uitgevoerd.

Al de if statements horen bij elkaar te staan, binnen 1 for loop. Een veelvoorkomende fout is door gebruik te maken van copy en paste van twee verschillende programmas en zo tot twee verschillende event lussen te komen.

    # Hier is 1 event lus
    for event in pygame.event.get(): 
        if event.type == pygame.QUIT:
            print("User vraagt om te stoppen.")
        elif event.type == pygame.KEYDOWN:
            print("User drukt een toets in.")
        elif event.type == pygame.KEYUP: 
            print("User laat een toets los.")

    # Hier heeft de programmeur een event lus van een ander programma in gecopieerd.
    # Dit is FOUT want de events werden reeds behandeld.
    # 
    for event in pygame.event.get(): 
        if event.type == pygame.QUIT:
            print("User vraagt om te stoppen.")
        elif event.type == pygame.MOUSEBUTTONDOWN: 
            print("User drukt op een muisknop")

De for lus van lijn 2 neemt alle user events. De for lus van lijn 13 kan geen events meer behandelen want deze werden reeds behandeld in de vorige lus.

Een ander typisch probleem is te beginnen met tekenen tijdens de event lus :

    for event in pygame.event.get(): 
        if event.type == pygame.QUIT:
            print("User vraagt om te stoppen.")
        elif event.type == pygame.KEYDOWN:
            print("User drukt een toets in.")
    
              
    pygame.rect.draw(screen,green,[50,50,100,100])
    
    # Deze code behandeld de events, maar zit niet meer in de  
    # 'for' lus van de te behandelen events. Het zal niet werken zoals voorzien.
    if event.type == pygame.KEYUP: 
        print("User laat een toets los.")
    elif event.type == pygame.MOUSEBUTTONDOWN: 
        print("User drukt een muisknop in")

Dergelijke situatie zal ervoor zorgen dat er sommige toetsaanslagen niet zullen werken. Waarom? De for lus behandeld alle events van de list. Dus wanneer er 2 toetsen worden ingedrukt zal de for lus ze beide behandelen. In dit voorbeeld zit het if statement niet in de for lus. Wanneer er meerdere events gebeuren, zal het if statement alleen maar werken voor het laatste event in plaats van voor alle events.

5.5.2 Verwerken van elk frame

De basis logica en de volgorde van elk frame van het spel :

Het maakt het programma veel gemakkelijker te lezen als deze stappen niet door elkaar gebruikt worden. Doe niet eerst wat berekeningen om dan wat op het scherm te zetten, om daarna weer wat berekeningen te gaan doen om dan de rest op het scherm te zetten. Het is een beetje gelijkaardig zoals met de oefening van het rekenmachine. Eerst de user input, dan de berekeningen en dan de output weergeven. Hier gebruiken we hetzelfde patroon.

De code om alles op het scherm te tekenen gebeurt binnen de while lus. Wanneer je de cock tick op 10 zet, wil dit zeggen dat alles 10 keer per seconde op scherm wordt gezet. Wanneer dit te veel gebeurt kan de computer vertragen omdat hij al zijn tijd moet steken in het telkens opnieuw updaten van het scherm. Wanneer dit niet in de lus gebeurt, dan zal het scherm niet correct opnieuw hertekent worden. Wanneer het hertekenen buiten de lus valt, zullen de graphics eenmaal worden getoont, maar zullen de graphics niet opnieuw verschijnen indien de window wordt verkleind of indien er een andere window voorgeplaatst wordt.

5.6 Einde van het programma

Tot nu toe zal de “close” button van een window tijdens het runnen van een programma in IDLE er nog steeds voor zorgen dat het programma crashed. Dit is een heel gedoe, want het vergt heel wat klikken om een gecrashed programma te sluiten.

Het probleem is dat, zelfs wanneer er uit de lus wordt gesprongen, we het programma nog niet hebben verteld om de window mooi af te sluiten. Door het onderstaande commando, kunnen we de openstaande windows afsluiten en het programma verlaten.

pygame.quit()

5.7 Wissen van het scherm

De volgende code wist alles wat er in het window staat met een witte achtergrond. Denk eraan dat de variabele white reeds vroeger werd gedefinieerd als een list van 3 RGB waarden.

# Wis het scherm en zet een achtergrond
screen.fill(white)
Video: Lijnen tekenen

Dit zou moeten gebeuren vooraleer er een teken commando wordt gebruikt. Het wissen van het scherm na het tekenen van het scherm resulteert alleen maar in het tonen van een blanko scherm.

Wanneer een window wordt gecreerd heeft het een zwart scherm. Het blijft belangrijk om telkens het scherm te wissen omdat er ondertussen andere dingen kunnen gebeuren. Een programma mag er nooit van uit gaan dat het scherm mooi gewist is om op te tekenen.

5.8 Flipping van het Scherm

Zeer belangrijk! Je moet het scherm steeds flippen nadat het getekend is. De computer zal niet meteen alles op het scherm tekenen zoals je vraagt, want dit zou ervoor zorgen dat het scherm begint te flikkeren. Pygame wacht tot je klaar bent met tekenen. Met het commando “flip” worden alle graphics op het scherm gezet.

Een fout met dit commando zal ervoor zorgen dat er alleen maar een wit scherm wordt getoont. Alle code ivm het tekenen dat na dit commando staat zal niet getoont worden.

# Breng alles wat je getekend hebt op het scherm.
pygame.display.flip()

5.9 Openen van een blanko Window

Laat ons nu alles wat we gezien hebben eens samenvoegen tot 1 programma. De code kan gebruikt worden als een basis template van een Pygame programma. Het opent een wit scherm en wacht tot de user de close button drukt.

"""
 Pygame base template for opening a window

 Sample Python/Pygame Programs
 Simpson College Computer Science
 http://programarcadegames.com/
 http://simpson.edu/computer-science/

 Explanation video: http://youtu.be/vRB_983kUMc
"""

import pygame

# Define some colors
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
GREEN = (0, 255, 0)
RED = (255, 0, 0)

pygame.init()

# Set the width and height of the screen [width, height]
size = (700, 500)
screen = pygame.display.set_mode(size)

pygame.display.set_caption("My Game")

# Loop until the user clicks the close button.
done = False

# Used to manage how fast the screen updates
clock = pygame.time.Clock()

# -------- Main Program Loop -----------
while not done:
    # --- Main event loop
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            done = True

    # --- Game logic should go here

    # --- Screen-clearing code goes here

    # Here, we clear the screen to white. Don't put other drawing commands
    # above this, or they will be erased with this command.

    # If you want a background image, replace this clear with blit'ing the
    # background image.
    screen.fill(WHITE)

    # --- Drawing code should go here

    # --- Go ahead and update the screen with what we've drawn.
    pygame.display.flip()

    # --- Limit to 60 frames per second
    clock.tick(60)

# Close the window and quit.
pygame.quit()

5.10 Inleiding tot het tekenen

Hier is een lijst van wat je allemaal kan tekenen :
http://www.pygame.org/docs/ref/draw.html
Een programma kan dingen tekenen zoals rechthoeken, veelhoeken, cirkels, eclipsen, bogen en lijnen. We gaan ook bekijken hoe je tekst kan tonen bij de tekeningen. Bitmapped graphics zoals afbeeldingen worden bekeken in hoofdstuk 12. Wanneer je gaat kijken in de pygame reference, kan je een definitie van een functie tegenkomen die er als volgt uitziet :

pygame.draw.rect(Surface, color, Rect, width=0): return Rect

Een vaakvoorkomende verwarring is de lijn waar staat width=0. Dit betekent dat wanneer je geen breedte meegeeft de standaard (default) waarde gelijk is aan 0. Dus de functie :

pygame.draw.rect(screen, red, [55,500,10,5])

is dezelfde functie als :

pygame.draw.rect(screen, red, [55,500,10,5], 0)

De : return Rect vertelt je dat de functie een rechthoek weergeeft. Je mag dit momenteel nog negeren.

Wat niet zal werken is een poging om de lijn te copieren en de width=0 binnen de aanhalingstekens te zetten.

# Dit mislukt en de fout die de computer teruggeeft is  
# heel erg moeilijk om te begrijpen.
pygame.draw.rect(screen, red, [55,500,10,5], width=0)

5.11 Lijnen tekenen

De code hieronder toont hoe je lijnen moet tekenen op een scherm. Hier wordt een groene lijn op het scherm gezet van (0,0) tot (100,100) met een breedte van 5 pixels. Onthoud dat green een variabele is dat reeds vroeger gedefinieerd werd als een list van drie RGB waarden.

# Teken op het scherm een groene lijn van (0,0) tot (100,100) 
# met een breedte van 5 pixels.
pygame.draw.line(screen,green,[0,0],[100,100],5)

Gebruik het basis template van het vorige voorbeeld en voeg de code toe voor het tekenen van lijnen. Lees de commentaar om te weten waar juist de code thuishoort. Probeer lijnen te tekenen met verschillende lengte, dikte, kleur en locatie. Teken verschillende lijnen.

5.12 Het tekenen van lijnen met een lus en een verschuiving (Offset)

Een programma kan dingen herhalen en herhalen. De volgende code is een voorbeeld om lijnen te trekken door gebruik te maken van een lus. Programma's gebruiken dergelijke techniek om meerdere lijnen te tekenen of om zelfs een volledige auto te tekenen.

Wanneer je een commando om een lijn te tekenen binnen een lus brengt, dan heb je meteen een heleboel lijnen. Op een voorwaarde natuurlijk. Wanneer je telkens hetzelfde startpunt en eindpunt kiest, dan worden alle lijnen over elkaar gezet en zie je op het einde slechts 1 lijn staan.

Om dit te verhelpen is het noodzakelijk dat je telkens een van de coordinaten wat verschuift (Offset). Hieronder zie je dat de eerste keer dat je door de lus gaat de variabele y_offset nul heeft als verschuiving. De lijn in de code wordt getekend van (0,10) tot (100,110). De volgende keer dat we door de lus gaan is de y_offset verhoogd met 10. Dit zorgt ervoor dat de volgende lijn getekend wordt met de neiuwe coordinaten van (0,20) en (100,120). Elke keer dat er door de lus wordt gegaan verschuiven de coordinaten met 10 pixels.

# Teken op het scherm verschillende groene lijnen van (0,10) tot (100,110) 
# met 5 pixels breedte door gebruik te maken van een lus.
y_offset = 0
while y_offset < 100:
    pygame.draw.line(screen,red,[0,10+y_offset],[100,110+y_offset],5)
    y_offset = y_offset+10

Dezelfde code kan ook met een eenvoudigere for lus :

# Teken op het scherm verschillende groene lijnen van (0,10) tot (100,110) 
# met 5 pixels breedte door gebruik te maken van een lus.
for y_offset in range(0,100,10):
    pygame.draw.line(screen,red,[0,10+y_offset],[100,110+y_offset],5)

Laat deze code runnen en probeer verschillende wijzigen te maken in de Offset. Probeer verschillende waardes te gebruiken voor de verschuiving, tot je helemaal begrijpt hoe het werkt.

In het volgende voorbeeld zie je een lus dat gebruik maakt van sinus en cosinus om een ingewikkeldere verschuiving te creeren, wat resulteert in het creeren van een beeld dat je kan zien in Figuur 5.7.

for i in range(200):
	
	radians_x = i/20
	radians_y = i/6
	
	x=int( 75 * math.sin(radians_x)) + 200
	y=int( 75 * math.cos(radians_y)) + 200
	
	pygame.draw.line(screen,black,[x,y],[x+5,y], 5)
fig.complex_offsets
Figure 5.7: Complexe Offsets

Meerdere elementen kunnen getekend worden binnen een for lus, zoals je kan zien in de code om meerdere X'en te tonen in Figuur 5.8.

    for x_offset in range(30,300,30):
        pygame.draw.line(screen,black,[x_offset,100],[x_offset-10,90], 2 )
        pygame.draw.line(screen,black,[x_offset,90],[x_offset-10,100], 2 )
fig.multiple_x
Figure 5.8: Multiple X's

5.13 Het tekenen van een rechthoek

Video: Hoe teken je rechthoeken en eclipsen

Wanneer je een rechthoek moet tekenen heeft de computer de coordinaten nodig van de linkerbovenhoek van de rechthoek (the origin) en de hoogte en de breedte.

Figuur 5.9 toont een rechthoek (en een eclipse, welke straks wordt uitgelegd) met een origin van (20,20), en een breedte van 250 en een hoogte van 100. Wanneer we dit aan de computer moeten meegeven dan heeft deze een list van 4 getallen nodig in de volgorde van (x, y, width, height).

fig.ellipse
Figure 5.9: Het tekenen van een eclipse

De volgende code is een voorbeeld van het tekenen van een rechthoek. De eerste 2 getallen in de list zijn voor het definieren van de linkerbovenhoek. De tweevolgende getallen geven de breedte van 250 pixels en de hoogte van 100 pixels weer.

De 2 op het einde specifieert de breedte van de lijn mee (2 pixels). Hoe groter het getal, hoe dikker de lijn rond de rechthoek. Wanneer het getal gelijk is aan 0, dan wordt er geen boord getekend rond de rechthoek, maar wordt alleen het vlak gevuld met de gevraagde kleur.

# Teken een rechthoek
pygame.draw.rect(screen,black,[20,20,250,100],2)

5.14 Tekenen van een ovaal (eclipse)

Een ovaal wordt net zoals een rechthoek getekend. De grenzen van de rechthoek worden meegegeven en de computer tekent een eclipse binnen deze grenzen.

De meestvoorkomende fout bij het tekenen van een eclipse is om te denken dat het vertrekpunt het centrum van de eclipse is. In de praktijk wordt er niets getekend bij het startpunt, maar is het startpunt de linkerbovenhoek van de rechthoek die de eclipse bevat.

Wanneer je terugkijkt naar Figuur 5.9 dan zie je een eclipse met een breedte van 250 pixels en 100 pixels lang. Aan de linkerbovenhoek van de 250x100 rechthoek die start op (20,20) wordt niet getekend. Wanneer we zowel de rechthoek als de eclipse tekenen kunnen we makkelijker zien hoe de eclipse binnen de rechthoek zit.

# Teken een eclipse door gebruik te maken van de grenzen van de rechthoek
pygame.draw.ellipse(screen,black,[20,20,250,100],2)	

5.15 Tekenen van een boog (Arc)

Video: Hoe teken je een boog

Wanneer het programma alleen maar nood heeft aan het tekenen van een deel van een eclipse kan je dit doen door het gebruik van het arc commando. Dit commando is gelijkaardig aan het eclipse commando, maar je moet de start en eind hoeken meegeven van de boog die moet getekend worden. De hoeken zijn in radialen.

fig.arc
Figure 5.10: Bogen

De code hieronder tekent 4 bogen, die elk een verschillend kwadrant vormen van een cirkel. Elk kwadrant is getekend in een verschillende kleur om de verschillende delen gemakkelijker te laten zien. Het resultaat van de code kan je zien in Figuur 5.10.

# Teken een boog als deel van een ovaal. Gebruik de radialen om te
# bepalen welke hoek er getekend moet worden.
pygame.draw.arc(screen,green,[100,100,250,200],  pi/2,     pi, 2)
pygame.draw.arc(screen,black,[100,100,250,200],     0,   pi/2, 2)
pygame.draw.arc(screen,red,  [100,100,250,200],3*pi/2,   2*pi, 2)
pygame.draw.arc(screen,blue, [100,100,250,200],    pi, 3*pi/2, 2)

5.16 Tekenen van een veelhoek

Video: Hoe teken je veelhoeken

De volgende code tekent een veelhoek. De vorm van volgende driehoek wordt bepaald door drie punten op (100,100) (0,200) en (200,200). Je kan zoveel punten megeven als je wil. Merk wel op dat de punten in een list zitten en dat elk punt bestaat uit 2 getallen, die zelf in een list zitten. De code tekent wat je kan zien in Figuur 5.11.

fig.triangle
Figure 5.11: Veelhoek
# Het tekenen van een driehoek door gebruik te maken van het polygon commando
pygame.draw.polygon(screen,black,[[100,100],[0,200],[200,200]],5)

5.17 Tekenen van tekst

Video: Hoe teken je tekst

Tekst is iets complexer. Er zijn drie dingen dat je eerst moet doen. Eerst heb je een variabele nodig dat de informatie bewaard van het lettertype (font) dat wordt gebruikt.

Daarna maakt het programma een beeld van de tekst. Een beetje te vergelijken met het uitsnijden van een “stempel” die je nodig hebt om de letters te drukken zodanig dat die klaar is om in de inkt te worden gedoopt om de letters dan op papier te kunnen zetten.

Tenslotte moet het programma nog vertellen waar het beeld van de letters moet gedrukt (of “geblit'ed”) worden op het scherm.

Hier is een voorbeeld :

# Selecteer een lettertype om te gebruiken. Standaard lettertype, 25 pt grootte.
font = pygame.font.Font(None, 25)

# Render de tekst. "True" betekent anti-aliased tekst. 
# Zwart is de kleur. De variabele zwart was gedefinieerd
# in de lijst daarboven [0,0,0]
# Merkop: Deze lijn creert een beeld van letters, 
# maar er wordt nog niet op het scherm gezet.
text = font.render("Mijn tekst",True,black)

# Zet nu het beeld van de tekst op het scherm op positie 250x250
screen.blit(text, [250,250])

Wil je de score op het scherm printen? Dit is iets moeilijker want hetvolgende werkt niet :

text = font.render("Score: ",score,True,black)

Waarom? Een programma kan niet zomaar extra elementen toevoegen aan font.render zoals bij een print statement. Slechts 1 string kan gestuurd worden naar het commando. Daarom moet de waarde van de score aan de tekst van de“Score: ” string geplakt worden. Maar ook het volgende werkt niet :

text = font.render("Score: "+score,True,black)

Wanneer score een geheel getal variabele is, dan weet de computer niet hoe hij die moet toevoegen aan een string. Jij als programmeur moet de conversie doen van de score naar een string, om dan de twee strings aan elkaar te hangen zoals hier :

text = font.render("Score: "+str(score),True,black)

Nu weet je hoe je de score kan printen. Wanneer je nu een timer wil printen heb je print formatting nodig, maar dat wordt in een later hoofdstuk bekeken. Bekijk de voorbeeld code voor het onderdeel timer.py voorbeeld :
ProgramArcadeGames.com/python_examples/f.php?file=timer.py

5.18 Listing van het volleige programma

Dit is de volledige listing van het programma dat we hebben besproken in dit hoofdstuk. Dit programma kan je samen met andere programma's downloaden van :
http://ProgramArcadeGames.com/index.php?chapter=example_code

fig.program_result
Figure 5.12: Resultaat van het voorbeeld programma
"""
 Simple graphics demo

 Sample Python/Pygame Programs
 Simpson College Computer Science
 http://programarcadegames.com/
 http://simpson.edu/computer-science/

"""

# Import a library of functions called 'pygame'
import pygame

# Initialize the game engine
pygame.init()

# Define some colors
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
BLUE = (0, 0, 255)
GREEN = (0, 255, 0)
RED = (255, 0, 0)

PI = 3.141592653

# Set the height and width of the screen
size = (400, 500)
screen = pygame.display.set_mode(size)

pygame.display.set_caption("Professor Craven's Cool Game")

# Loop until the user clicks the close button.
done = False
clock = pygame.time.Clock()

# Loop as long as 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

    # All drawing code happens after the for loop and but
    # inside the main while not done loop.

    # Clear the screen and set the screen background
    screen.fill(WHITE)

    # Draw on the screen a line from (0,0) to (100,100)
    # 5 pixels wide.
    pygame.draw.line(screen, GREEN, [0, 0], [100, 100], 5)

    # Draw on the screen several lines from (0,10) to (100,110)
    # 5 pixels wide using a loop
    for y_offset in range(0, 100, 10):
        pygame.draw.line(screen, RED, [0, 10 + y_offset], [100, 110 + y_offset], 5)


    # Draw a rectangle
    pygame.draw.rect(screen, BLACK, [20, 20, 250, 100], 2)

    # Draw an ellipse, using a rectangle as the outside boundaries
    pygame.draw.ellipse(screen, BLACK, [20, 20, 250, 100], 2)

    # Draw an arc as part of an ellipse.
    # Use radians to determine what angle to draw.
    pygame.draw.arc(screen, BLACK, [20, 220, 250, 200], 0, PI / 2, 2)
    pygame.draw.arc(screen, GREEN, [20, 220, 250, 200], PI / 2, PI, 2)
    pygame.draw.arc(screen, BLUE, [20, 220, 250, 200], PI, 3 * PI / 2, 2)
    pygame.draw.arc(screen, RED, [20, 220, 250, 200], 3 * PI / 2, 2 * PI, 2)

    # This draws a triangle using the polygon command
    pygame.draw.polygon(screen, BLACK, [[100, 100], [0, 200], [200, 200]], 5)

    # Select the font to use, size, bold, italics
    font = pygame.font.SysFont('Calibri', 25, True, False)

    # Render the text. "True" means anti-aliased text.
    # Black is the color. This creates an image of the
    # letters, but does not put it on the screen
    text = font.render("My text", True, BLACK)

    # Put the image of the text on the screen at 250x250
    screen.blit(text, [250, 250])

    # Go ahead and update the screen with what we've drawn.
    # This MUST happen after all the other drawing commands.
    pygame.display.flip()

    # This limits the while loop to a max of 60 times per second.
    # Leave this out and we will use all CPU we can.
    clock.tick(60)

# Be IDLE friendly
pygame.quit()

5.19 Herhalingsvragen

Click here for a multiple-choice herhalingskwis.

Na het beantwoorden van de herhalingsvragen hieronder mag je proberen om een programma te schrijven dat je eigen creaties op het scherm zet. Voor details verwijs ik naar de Maak een tekening oefening.

  1. Voordat een programma een functie zoals pygame.display.set_mode() uitvoert, moeten er twee dingen gebeuren, welke?
  2. Wat doet de pygame.display.set_mode() functie?
  3. Voor wat wordt de pygame.time.Clock functie gebruikt?
  4. Wat doet de for event in pygame.event.get() lus?
  5. voor deze codelijn :
    pygame.draw.line(screen,green,[0,0],[100,100],5)
    
    • Waarvoor dient screen?
    • Waarvoor dient [0,0]? Waarvoor dient[100,100]?
    • Waarvoor dient 5?
  6. Verklaar waarin het coordinatensysteem van de computer verschilt met het Cartesiaans coordinaten systeem.
  7. Verklaar waarom en hoe white = ( 255, 255, 255) een kleur vertegenwoordigd.
  8. Wat gebeurt er wanneer we een driehoek tekenen met een lijndikte van 0?
  9. Schets de getekende ovaal zoals beschreven in de code hieronder en benoem de origin coordinaat :
    pygame.draw.ellipse(screen,black,[20,20,250,100],2) 
    
  10. Beschrijf welke de drie algemene stappen zijn om tekst te printen op het scherm door middel van graphics.
  11. Wat zijn de coordinaten van een veelhoek die de onderstaande code tekent?
    pygame.draw.polygon(screen,black,[[50,100],[0,200],[200,200],[100,50]],5)
    
  12. Wat doet pygame.display.flip()?
  13. Wat doet pygame.quit()?

5.20 Oefening

Vervolledig Oefening 3 “Maak een tekening” om je eigen tekening te maken en te tonen dat je het gebruik van lussen en graphics begrijpt.


You are not logged in. Log in here and track your progress.