Ügyességi játékok programozása
Pythonnal és Pygame-mel

Chapter 8: Bevezetés az animációba

8.1 A pattogó négyzet

Videó: Pattogó négyzet

Ahhoz, hogy elkezdjük az első animációnkat, nyissuk meg az alap pygame programunkat az 5-ös fejezetből, ami egy üres oldalt nyit meg nekünk. A forráskódja itt van pygame_base_template.py:
ProgramArcadeGames.com/python_examples/f.php?file=pygame_base_template.py

Össze fogjuk pakolni a programot, hogy egy fehér négyzet pattogjon a képernyőn körbe, egy fekete háttér előtt. Szabad kezet kapsz abban, hogy a színt megválaszd, csak bizonyosodj meg afelől, hogy a háttérszín és a négyzet színe különbözik egymástól!

fig.bouncing_rectangle

Első lépés, kezdjük az alap sablonunkkal, és változtassuk a háttérszínt fehérről feketére. Ez a kód a 46-dik sor körül van.

screen.fill(BLACK)

Azután, rajzoljuk ki a négyzetet, amit animálni szeretnénk. Egy egyszerű négyzet fog megjelenni. Ez a kód akkor jelenik meg, miután letisztáztuk a képernyőt, és mielőtt frissítettük.

pygame.draw.rect(screen, WHITE, [50, 50, 50, 50])

A ciklus, amivel a négyszöget kirajzoljuk mindig az (x,y) kezdő koordinátával indít, jelen esetben (50,50). Ez az, amit kontrollál az első két 50-s a listában. Amíg ezt a számot nem változtatjuk, a négyszögünk nem fog mozogni.

A négyzet 50 pixel magas és 50 pixel széles. A dimenziókat a lista utolsó két számával irányítjuk. Hívhatjuk ezt a négyszöget négyzetnek is, mivel ugyanaz a szélessége és magassága. Én négyszögnek fogom nevezni, mivel a négyzet is négyszög, és ez csak a monitoron, és a felbontáson alapszik, a pixelek nem mindig adnak ki egy négyzetet. Keress rá a Pixel Aspect Ratio-ra, ha a téma bővebben érdekel.

Hogyan leszünk képesek megváltoztatni a helyét, és nem csak az (50,50) koordinátára beszorulni? Hát persze, használj változót! Az alábbi kód az első lépés:

rect_x = 50
pygame.draw.rect(screen, WHITE, [rect_x, 50, 50, 50])

Ahhoz, hogy a négyszög jobbra mozogjon, az x-t kell megnövelni eggyel minden esetben. Ez a program már közel van, de még nem teljes:

rect_x = 50
pygame.draw.rect(screen, WHITE, [rect_x, 50, 50, 50])
rect_x += 1

A probléma a fenti kóddal az, hogy a rect_x visszaadja az 50-t értéknek, minden ciklus alatt. A problémát, ha orvosolni akarjuk, akkor mozgassuk a rect_x kezdő értékadását a cikluson kívülre. A következő kód már sikeresen tolja a négyszöget jobbra.

# A kezdő x értéke a négyszögünknek
# Figyeld meg, hogy ez a fő ciklusunkon kívül van.
rect_x = 50

# -------- Fő Program Ciklus -----------
while not done:
	for event in pygame.event.get(): # User did something
		if event.type == pygame.QUIT: # Ha a felhasználó a quit-re kattint
			done = True # Jelöljük, hogy kész vagyunk és megszakíthatjuk a ciklust

	# Adjuk meg a hátteret
	screen.fill(BLACK)

	pygame.draw.rect(screen, WHITE, [rect_x, 50, 50, 50])
	rect_x += 1

Ha a dobozt gyorsabban akarjuk mozgatni, akkor a rect_x értéket ne 1-gyel, hanem 5-tel növeljük:

rect_x += 5

Kiterjeszthetjük ezt a kódot, és megnövelhetjük az x és y értéket, így már a négyzetünk le és jobbra mozog.

# A négyszög kezdő értéke
rect_x = 50
rect_y = 50

# -------- Fő  program ciklus -----------
while not done:
	for event in pygame.event.get():
		if event.type == pygame.QUIT:
			done = True

	# Háttér megadása
	screen.fill(BLACK)

	# A négyszög kirajzolása
	pygame.draw.rect(screen, WHITE, [rect_x, rect_y, 50, 50])

	# A kezdőpont elmozgatása
	rect_x += 5
	rect_y += 5

A doboz iránya és sebessége egy vektorban lesz tárolva. Ez könnyebbé teszi azt, hogy a mozgó tárgy irányát és sebességét megváltoztassuk később. A következő kódtöredék azt mutatja meg, hogy hogyan használjunk változókat, amelyek az x és y változását kezeli (5,5).

# A négyszög kezdő pozíciója
rect_x = 50
rect_y = 50

# négyszög mozgásiránya és sebessége
rect_change_x = 5
rect_change_y = 5

# -------- fő programciklus -----------
while done == False:
	for event in pygame.event.get(): # Felhasználó valamit csinál
		if event.type == pygame.QUIT: # Ha a felhasználó a bezárásra kattint
			done = True # Jelöljük, hogy kész vagyunk, végeztünk

	# Képernyő háttér megadása
	screen.fill(BLACK)

	# Négyszög kiíratása
	pygame.draw.rect(screen, WHITE, [rect_x, rect_y, 50, 50])

	# A kezdőpont mozgatása
	rect_x += rect_change_x
	rect_y += rect_change_y

Miután a ládika elérte a képernyő szélét, folytatja mozgását. Semmi sem fogja visszapattintani onnan. Ehhez meg kell fordítani a mozgásirányát, a rect_change_y változót 5-ről mínusz 5-re, amikor eléri a képernyő alját. A négyszög az alján van, amikor rect_y nagyobb, mint a képernyő magassága. A lenti kód ellenőrzi majd megfordítja a mozgásirányt:

# Pattanjon csak vissza a négyszög
if rect_y > 450:
	rect_change_y = rect_change_y * -1
fig.450
Figure 8.1: Négyszög helye az y koordinátán föggvényében

Miért ellenőrizzük a rect_y 450 ellenében? Ha a képernyő 500 pixel magas, akkor 500-zal szemben ellenőrizzük, ahogy az logikusan következik. De emlékezz arra, hogy a négyszöget a balfelső sarokból határoztuk meg. Ha 500-tól határoznánk meg, akkor 500-tól 550-ig rajzolódna ki, a képernyőn kívül. Lásd az alábbi ábrát: 8.1.

Tegyük helyére a dolgot, ha a négyszög 50 pixel magas, akkor a korrekt helyzet megadása, a pattogáshoz:
$500-50=450$.

A lenti kód a négyszöget pattogtatni fogja a 700x400-s ablakban:

# Szükség esetén pattogtatjuk a négyszöget
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

Sokkal összetettebb alakzatok iránt érdeklődsz? Számos más kirajzolással kapcsolatos parancs van, ami a rect_x és rect_y parancsra épül. Az alábbi kód kirajzol egy vörös négyszöget egy fehér négyszögben. A vörös négyszög 10 pixel nagyságú és a fehér négyszög bal felső sarkából számolódik. 20 pixellel kisebb minden irányban, aminek eredménye, hogy 10 pixel szélesen öleli körül a fehér szín a vörös négyszöget. Lásd az alábbi ábrát 8.2.

fig.two_part_rectangle
Figure 8.2: Fehér négyszög egy vörös négyszöggel a közepén
# Vörös négyszög kirajzolása egy fehérben
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 Havazás anaimálása

Egy dolog animálása nem volna elég? Többet akarsz animálni? Mi lenne akkor, ha többszáz dolgot animálnál egyszerre? Itt megmutatjuk azt a technikát, hogyan használd a 8.1 részt arra, hogy sok-sok lehulló hópelyhet animálj.

8.2.1 Kód elmagyarázása

Videó: Havazás animációja

Ahhoz, hogy elkezdjük ezt a programot, nyissuk meg az alap pygame sablonunkat, ami egy tiszta lapot dob fel nekünk. Újfent itt van, hol érhető el pygame_base_template.py :
ProgramArcadeGames.com/python_examples/show_file.php?file=pygame_base_template.py

Lehetséges készíteni egy x,y lokációt olyan dolgoknak, mint amilyenek a csillagok, vagy eső, vagy hó. Mindezt véletlen számokkal. A legegyszerűbb módja, hogy megvalósítsuk ezt, egy for ciklus, ami köröket rajzol ki véletlenszerű x, y pozíciókban. Próbáld ki az alábbi programot, a fő while cikluson belül.

for i in range(50):
    x = random.randrange(0, 400)
    y = random.randrange(0, 400)
	pygame.draw.circle(screen, WHITE, [x, y], 2)

Próbáld ki, de ennek a programnak is baja van! Másodpercente húszszor, minden cikluslefutás alkalmával véletlenszerűen rajzol. Próbáld meg beállítani, és figyeld hogyan változik a program.

Nyilván szükségünk lesz egy random pozícióra a hópelyhekhez, és a helyükön kell tartanunk. Nem akarunk új pozíciót húszszor minden másodpercben. Ott kell tartanunk egy listában őket. A program egy python listát fog erre használni. Ezt meg kell tennünk még a fő ciklusunk előtt, különben a program 50 hópelyhet ad majd minden 1/20 másodpercben.

for i in range(50):
    x = random.randrange(0, 400)
    y = random.randrange(0, 400)
    snow_list.append([x, y])

Miután a hópelyhek helyzetét megadtuk, hozzáköthetjük a normál listánkhoz. A következő kód ki fogja írni az x és y koordinátákra, mint első helyre, ami a nullás pozíciónál van tárolva:

print(snow_list[0])

Mi van akkor, ha mi csak az x és y koordinátákat akarjuk? Van egy listánk a listán belül. A fő listánkban megvan az összes koordináta. Ebben a listában, minden koordináta az x koordináta (0 pozíció) és y koordináta (1-s pozíció). Például, itt van három koordináta:

[[34, 10],
 [10, 50],
 [20, 18]]

Ahhoz, hogy kiírassunk egy y koordinátát, a 0 pozícióról, először ehhez válasszuk ki a 0 koordinátát, és azután az y értékét, az 1-es pozícióról. A kód így fog kinézni:

print(snow_list[0][1])

Ahhoz, hogy az x értékét a 21-dik koordinátára (20-s pozíció), először válasszuk ki a 20-s koordinátát, aztán kérjük le a 0-s pozíciót.

print(snow_list[20][0])

A fő while cikluson belül, a program egy for ciklust fog használni, hogy kirajzoljon mindent a listából. Emlékezz arra, hogy len(snow_list) vissza fog adni egy számot, ami a listában szereplő elemek számát adja meg.

# Minden hópelyhet a listában megmozgatunk
for i in range(len(snow_list)):
	# Hópehely kirajzolása
	pygame.draw.circle(screen, WHITE, snow_list[i], 2)

Emlékezz arra, hogy kétféle for ciklus van. A másik ciklust is használhatod, ami így néz ki:

# Minden hópehely helyének a MÁSOLÁSA a listából
for xy_coord in snow_list:
	# Hópehely kirajzolása
	pygame.draw.circle(screen, WHITE, xy_coord, 2)

De, mivel azt tervezzük, hogy a hópihe helyét módosítjuk, ezért nem használhatjuk a for ciklust mivel a hópehely helyét megadó elem másolatát változtatjuk, nem az aktuális hópehely helyét.

Ha a program minden objektumot lefelé mozgat, mint a havat, akkor a for ciklus kis módoításával elérhetjük azt, hogy az y koordinátát megnöveljük:

# Minden hópehely a listánkból
for i in range(len(snow_list)):

	# Hópehely kirajzolása
	pygame.draw.circle(screen, WHITE, snow_list[i], 2)

	# Egy pixellel lejjeb mozgatjuk a hópihét
	snow_list[i][1] += 1

Ez fogja a hópelyheket lefelé mozgatni, de amikor eléri a képernyő alját, semmi se fog történni. A lenti kódot hozzáadva, a hó újra megjelenik a képernyő random helyén.

	# Ha a hópehely a képernyő aljáig ér
	if snow_list[i][1] > 400:
		# reseteljük a képernyő tetejére
		y = random.randrange(-50, -10)
		snow_list[i][1] = y
		# új x pozíciót adunk
		x = random.randrange(0, 400)
		snow_list[i][0] = x

Továbbá, lehetséges, hogy dolgokat adjunk a listánkhoz, mégpedig különböző méretű, színű, gyorsaságú, és irányba mozgó dolgokat. Ez összetetté válik, mivel megsokszorozzuk az adattípust, amit listában kell tartanunk. Maradjunk az egyszerű megoldásoknál most, de ha majd megtanuljuk az osztályok kezelését a 13-dik fejezetből, akkor majd könnyű lesz kezelni több objektum attribútumát is.

8.2.2 A listát használó teljes program

"""
 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()

Ez a példa minden hópelyhet ugyanabba az irányba mozgat. Mi van akkor, ha minden objektumot más-más irányba akarunk mozgatni? Ha ezt szeretnéd a játékodban, akkor nézd meg a 13-dik fejezetet, hogyan használj osztályokat. A 8-as labor gyakorlat bepillantást nyújt abba, hogyan használj többszáz animált tárgyat, ahol mindőjük külön irányba mozog.

8.3 3D-s animáció

Videó: Blender-játék motorjának a demója

A 2 dimenziós környezetből a 3 dimenziós környezetbe való átmenet nem olyan nehéz, mint elsőre tűnne. Ugyan, ennek a leckének az anyagán túlmutat, azért érdemes megnézni azt, hogyan is működik ez.

Van egy szabadon letölthető 3D program, és ezt Blender-nek hívják, ami nem más, mint egy "játék motor", aminek segítségével a programozók 3D-s játékokat hozhatnak létre. A 3D-s objektumokat python nyelven megírt parancsok irányítják.

fig.blender01
Figure 8.3: Blender-fájlra példa

Vess egy pillantást erre az ábrára 8.3. Ezen sok objektummal teli zöld tálcát látunk. A kék objektumot python szkripttel irányítják, ami ide-oda mozog, és nekiütődik más objektumoknak. A szkript, amit lejjebb mutatunk, sokban hasonlít 2 dimenziós programunkra. Van egy fő ciklus, ahol x,y listázva van, és változók irányítják a vektort.

A fő program ciklust a Blender irányítja. A Python kód, ami a listázásnál látható, minden "frame"-nél lesz lehívva, a Blender által. Ez az oka annak, hogy a python kódban nem látjuk a fő ciklust. Mégis létezik ez a ciklus.

A kék objektumnak a helyzete x,y,z formában van tárolva. Ehhez hozzá lehet férni és meg lehet változtatni a blueobject.position változóval. A sorozatunk nullás pozíciójában az x van, az egyes pozíciójában van az y, és a kettes pozíció tartalmazza a z helyzetét.

Ahelyett, hogy a change_x és change_y változókat használnánk, mint a 2D-s esetben, a Blender egy egyszerűbb módon tartja nyilván a koordináták helyét:
blueObject["x_change"]
blueObject["y_change"]

Az if parancsunkkal tudjuk vizsgálni a kék objektumot, hogy elérte-e a képernyő szélét, és hogy az irányt meg kell fordítani. A 2 dimenziós játékokkal ellentétben, ahol pixellel mondtuk meg az objektum helyét, egy lebegő pontos számtípus lesz. Így, ha egy tárgyunk 5 és 6 közé esik, akkor a lehet 5.5-s helyen is.

#  Blender Game motorjának az importálása
import bge

# A kék objektum referenciája
cont = bge.logic.getCurrentController()
blueObject = cont.owner

# Kiírjuk az x,y koordinátát ahol a kék objektum van
print(blueObject.position[0], blueObject.position[1] )

# Megváltoztatjuk az x, y koordinátát az x_change és
# y_change parancsokkal. x_change és y_change a játékban olyan tulajdonságok
# melyek a kék objektumhoz tartoznak.
blueObject.position[0] += blueObject["x_change"]
blueObject.position[1] += blueObject["y_change"]

#Ellenőrizd le, hogy az objektum eléri-e a képernyő szélét.
# Ha igen, akkor meg kell fordítani az irányát. Mind a 4 élének.
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

A Blender-t le lehet tölteni innen:
http://www.blender.org/

A teljes blenderes fájl a példából elérhető innen:
ProgramArcadeGames.com/chapters/08_intro_to_animation/simple_block_move.blend.

8.4 Review

8.4.1 Multiple Choice Quiz

Click here for a multiple-choice quiz.

8.4.2 Short Answer Worksheet

Click here for the chapter worksheet.

8.4.3 Lab

Click here for the chapter lab.


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