Simulering med Pygame 2

Bevegelse med konstant fart

No skal vi sjå på korleis vi klarer å skapa illusjonen av at ein ball beveger seg i eit Pygame-vindu. Som med all animasjon, byggjer vi på det faktum at når vi viser enkeltbilder fort, så ser det ut som kontinuerlig bevegelse fordi augene våre ikkje klarer å skilja. Så prinsippet er rett og slett å tegna ballen igjen og igjen, mens vi stadig endrar ballens posisjon. Så vi innfører ein "fartsvektor" med komponentane vx og vy, og så oppdaterer vi posisjonen (x,y) med disse:

In [ ]:
x = 50
y = 50
vx = 1
vy = 1
x += vx
y += vy

Effekten av dette er at vi har flytta ballen frå punktet (50,50) til punktet (51,51). Det einaste vi treng å gjera no er å laga ei løkke der vi stadig endrar posisjonen, og så tegnar vi ballen påny for kvar gong. For at den gamle tegningen skal bli borte, må vi også tegna bakgrunnen omigjen. Koden for game_loop kan sjå slik ut:

In [ ]:
def game_loop():   

    RADIUS = 15 # Ballens radius

    vx = 1 # Fart i x-retning
    vy = 1 # Fart i y-retning
    
    x = 50 # Ballens startverdi i x-retning.
    y = 50 # Ballens startverdi i y-retning.

    running = True # Boolsk variabel. Løkka vil løpa så lenge denne er SANN
    while running:

        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                exit("Ferdig!")
                #quit()
                
        x += vx
        y += vy  

        # Tegnar bakgrunn og ball:
        vindu.fill(BAKGRUNN) 
        pygame.draw.circle(vindu, BALL, (int(x), int(y)), RADIUS, 0)
        pygame.display.update()
        
        clock.tick(FPS)

game_loop()

Kollisjonshåndtering

Hvis du kunne kjørt denne koden, ville du sett ein ball som bevega seg på skrå nedover til høgre og forsvann ut av vinduet. For vi har ikkje lagt inn kode som bestemmer kva som skal skje når ballen kjem utanfor. Avgengig av kva vi skal bruka dette til, så kan vi enten legga inn ein såkalt "wrap" rundt kantane (det betyr at ballen vil komma tilbake på venstre side når den går ut til høgre osv.) eller vi kan leggja inn kollisjonshåndtering slik at ballen sprett tilbake. Vi skal gjera det siste. Prinsippet er som følgjer:

  • Hvis ballen har truffe ein sidevegg, så snur vi den i x-retning, dvs vi set vx = -vx
  • Hvis den har truffe golvet eller taket, så snur vi den i y-retning ved å setja vy = -vy

Det einaste som gjenstår å løysa då er å finna ut om ballen har kollidert. Då må ta hensyn til at posisjonen til ballen er koordinatane til sentrum av ballen. Det betyr at vi må leggja til ballens radius for å sjekka om vi har kollisjon:

In [ ]:
        # Hvis kollisjon med venstre eller høgre kant, reverserer vi farten i x-retning
        if x + RADIUS > VINDUB or x < RADIUS:
            vx = -vx
        # Hvis kollisjon med øvre eller nedre kant, reverserer vi farten i y-retning
        if y + RADIUS > VINDUH or y < RADIUS:
            vy = -vy

Streng talt, er denne koden litt unøyaktig. Vi burde også tatt hensyn til farten, og reposisjonert ballen etter kollisjon. Men så lenge vi bruker ein lav fart og animasjonen er for eit spill, som for eksempel pong, så vil den duga. Her er heile koden:

In [ ]:
import pygame 

pygame.init() # Startar pygame

VINDUB = 400 # Bredden på vinduet
VINDUH = 300 # Høyden på vinduet

# Definerer RGB-fargar
BAKGRUNN =  (100, 200, 255)
BALL = (255, 80, 0)

FPS = 200 #Frames Per Second, dvs kor fort animasjonen skal gå.

vindu = pygame.display.set_mode((VINDUB, VINDUH)) # Definerer vindu / overflate
pygame.display.set_caption("Ein bevegelig ball: ") # Overskrift for vinduet
clock = pygame.time.Clock()

def game_loop():

    RADIUS = 15 # Ballens radius

    vx = 1 # Fart i x-retning
    vy = 1 # Fart i y-retning
    
    x = 50 # Ballens startverdi i x-retning.
    y = 50 # Ballens startverdi i y-retning.

    running = True # Boolsk variabel. Løkka vil løpa så lenge denne er SANN

    while running:
        
        # Lukker vindu og stoppar programmet:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                return pygame.quit() 

        # Hvis kollisjon med venstre eller høgre kant, reverserer vi farten i x-retning
        if x + RADIUS > VINDUB or x < RADIUS:
            vx = -vx
        # Hvis kollisjon med øvre eller nedre kant, reverserer vi farten i y-retning
        if y + RADIUS > VINDUH or y < RADIUS:
            vy = -vy
            
        x += vx # Oppdaterer x-koordinat
        y += vy # Oppdaterer y-koordinat

        # Tegnar bakgrunn og ball:
        vindu.fill(BAKGRUNN) 
        pygame.draw.circle(vindu, BALL, (int(x), int(y)), RADIUS, 0)
        pygame.display.update()
        
        clock.tick(FPS)

game_loop()