Játékok készítése a pygame keretrendszerrel - 1. rész

A játékok készítés komoly feladat több szempontból is. Először is a játékok intenzív interakciót követelnek meg a felhasználóval, hasonlóan a grafikus felületű szoftverekhez. A játék típusától és a játékmenettől függően megfelelően jól kell kinéznie, hogy közönséget kapjon, ezenkívül a játékoknak egy bizonyos szintű mesterséges intelligenciát kell nyújtania (gondoljunk akár egy szimpla amőbára). A játékoknak szórakoztatónak kell lenniük, mindamellett megfelelő kihívást kell nyújtania a játékosoknak. Ezek igen összetett követelményrendszert jelentenek.
A komoly és látványos játékok sosem egy ember munkájának az eredményei, hiszen a megalkotásukhoz szükséges szakmai ismeret annyira szerteágazó (nem beszélve az emberek saját adottságairól, tehetségéről), hogy azokat megfelelő szinten elsajátítani egy embernek egy életre szóló feladat - és akkor még semmit sem csinált. Ezeket a játékokat több emberből álló csapatok készítik (producer, designer, artist, coder, marketinges, webfejlesztő, és sok esetben PR-os is kell) igen nagy ráfordításokkal.
Ha valaki a játékiparban szeretne dolgozni, mindenképpen ki kell választania magának egy testhez álló szakirányt, majd keresnie vagy építenie kell egy csapatot. A játékfejlesztés kemény munka, ami lassan térül meg, hiszen néha egy játék fejlesztése több évig is eltart, addig finanszírozni kell a készítők bérét, és a fejlesztés infrastruktúráját.



Gondolom most kellőképpen sikerült mindenkit elijeszteni a témától, de nem eszik olyan forrón a kását, egy ember is készíthet jó, érdekes és jól kinéző játékokat, csak tudomásul kell venni, hogy ezek bonyolultsága és/vagy színvonala azért messze el fog maradni a fent említett komoly alkotásokétól :D A DRY (Don't Repeat Yourself) és DNRTW ( Do Not Reinvent The Wheel) elvet szem előtt tartva, a legokosabb ha valamilyen keretrendszert alkalmazunk a játékok készítése során (a komolyabb és összetettebb keretrendszereket már játékmotornak hívják). Ezek leveszik a terhet a vállunkról egy csomó olyan dologgal kapcsolatban, mint pl. képadatok tárolása, átalakítása, képernyőre rakása, hangok, zenék kezelése, stb.

Pythonhoz, az egyszerű gyalogbéka hobbiprogramozónak a pygame (ejtsd: pájgém), esetleg a pyglet (ejtsd: piglet) áll rendelkezésére, vagy komolyabb dolgokhoz az ogre, vagy a crystalspace. A pygame-en kívül a többinél konyitani kell valamennyire az opengl-hez, ha valaki nem foglalkozott vele, akkor mindenképpen a pygame-et ajánlanám kezdésnek.

Mi is ez a pygame? Nos létezik linuxon egy programkönyvtár (lib) amely elvben hasonló hozzáférést enged a gép video, hang és input elemeihez, mint a winen a directx. Simple Directmedia Layer a becsületes neve, de SDL-ként közismertebb. Ez egy C-ben írt lib, amit tud azt jól tudja, bár nem annyira sokrétű, mint a wines párja (egyébként elérhető win alatt is, így a pygame is).

 Lelkes pythonosok megírták a pygame-et, ami tulajdonképpen pár általuk hozzáadott plusz dologtól eltekintve az SDL pythonos nyelvi kötése. Habár opengl mutatványokra is felhasználható, a pygame elsősorban 2D játékok készítésére készült. Ennyi unalmas bevezető után csapjunk a sűrűjébe, is próbáljunk meg valamit kezdeni a pygame-mel!

Természetesen fel kell telepíteni használat előtt, ubuntu, debian alatt van a repóban, sudo apt-get install python-pygame, más rendszereknél nézzétek meg a telepítési leírásokat. Használatához be kell importálnunk a pygame modult a programunkba. Mindjárt két importot is érdemes elvégezni, ezzel egy csomó gépeléstől kímélhetjük meg magunkat (pl. pygame.QUIT helyett elegendő QUIT-et írni), majd inicializálni kell a pygame összetevőket:

import pygame
from pygame.locals import *
pygame.init()

Az inicializálás során a pygame felkészíti az SDL réteget a működésre, ami egyben a hardverek inicializálását is magában foglalja. Ha ezt elmulasztjuk, a programunk működésképtelen lesz.

Ahhoz, hogy legyen egy ablakunk, ahol a játék grafikáját, elemeit megjelenítjük, meg kell kérnünk az SDL-t, hogy készítsen nekünk egyet. És itt jön a pygame és az SDL első korlátja, mert meg kell adni neki, hogy mekkorát készítsen. Ezzel nincs semmi baj, a baj azzal van, hogy ezen utána nem tudunk változtatni :(
Ezért már a játék kezdetekor ki kell találni, hogy mekkora ablakban fogunk dolgozni.
Most egy 800x600-as ablakot hozunk létre, ez elfér manapság szinte minden képernyőn, ahol a pygame elfut (a telefonokat most hagyjuk ki ebből).

screen = pygame.display.set_mode((800,600))

A screen változóban egy pygame.surface objektum tárolódik el. Mi is ez a surface? A szó maga felületet jelent, a pygame-ben egy kicsit többet, de a lényeg az a mi szempontunkból, hogy egy olyan felület, amire rajzolni tudunk.

Nosza rajzoljunk rá! Csinálunk egy pattogó labdát. Nem egy hasznos dolog, és semmiképpen nem játék még, de kezdetnek jó lesz :) Itt a forráskód, és átbeszéljük.

#!/usr/bin/env python
# -*- coding:utf8 -*-

import sys,pygame
from pygame.locals import *

# pygame inicializálása
pygame.init()

# ablak megjelenítése és a játékadatok beállítás
SCREEN_SIZE = (800,600) # ablak mérete
screen = pygame.display.set_mode(SCREEN_SIZE) # ablak "surface"

BGCOLOR = (0,0,0) # háttérszín
BALLCOLOR = (255,0,0) # labda színe

d = 30 # labda átmérője
dh = d/2 # átmérő fele
x,y = dh,dh # labda kezdőpontja (középpont)
vx,vy = 12,18 # függőleges és vízszintes sebessége a labdának
fps = 30 # képkocka / másodperc

# ezek adják meg hol kell a labdának visszapattannia
xlimits = (dh,SCREEN_SIZE[0]-dh)
ylimits = (dh,SCREEN_SIZE[1]-dh)

def rajzol(x,y):
    """rajzol(x,y):
    letörli a képernyőt, és felrajzolja a labdát az x,y pozícióba"""
    screen.fill(BGCOLOR)
    pygame.draw.circle(screen,BALLCOLOR,(x,y),dh,0)

def mozgat(x,y,vx,vy):
    """mozgat(x,y,vx,vy):
    kiszámolja a labda következő pozícióját.
    megfordítja a labda mozgását az ablak szélénél"""
    x = x + vx
    y = y + vy
    if x < xlimits[0]:
        x = xlimits[0]
        vx = -vx
    if x > xlimits[1]:
        x = xlimits[1]
        vx = -vx
    if y < ylimits[0]:
        y = ylimits[0]
        vy = -vy
    if y > ylimits[1]:
        y = ylimits[1]
        vy = -vy
    return x,y,vx,vy

def kilep():
    """kilep():
    Megnézi, hogy megnyomták-e az ESC gombot
    vagy megpróbálták bezárni az ablakot"""
    for event in pygame.event.get():
        if event.type==QUIT:
            return True
        elif (event.type==KEYDOWN and event.key==K_ESCAPE):
            return True
    return False

# ezzel az objektummal tudjuk szabályozni a játék sebességét
clock = pygame.time.Clock()

# fő ciklus
# ismétlődik amíg ki nem lépünk
while True:
    # kirajzoljuk a játékunkat
    # és elvégezzük a mozgatását a labdának
    rajzol(x,y)
    x,y,vx,vy = mozgat(x,y,vx,vy)

    # ki akart lépni a felhasználó?
    if kilep():
        pygame.quit()
        sys.exit()

    # kirakjuk az ablak tartalmát a képernyőre
    pygame.display.flip()

    # szabályozzuk a futási sebességet
    clock.tick(fps)

    # infóként kinyomjuk a labda adatait a konzolra
    print x,y,vx,vy

Az 1-25 sorok magától értetődőek, de azért elláttam kommentekkel is a biztonság kedvéért. Mielőtt a rajzol és mozgat függvényeket megnéznénk, inkább foglalkozzunk a kód végén látható végtelenített ciklussal, a 67-87 sorokban.

Ez a programunk fő ciklusa. Mit is csinál? Kirajzolja az ablak tartalmát a rajzol() függvénnyel, kiszámolja a labda következő koordinátáját a mozgat() függvénnyel, megnézi, hogy kapott-e kilépési parancsot - ha igen, akkor kilép - , logolja a labda koordinátáit és sebességét a konzolra, és kezdődik minden előlről.
Van itt viszont egy fontos parancs, amiről fontos szót ejteni: pygame.display.flip()
Az SDL dupla pufferelést alkalmaz, minden rajzolási műveletet a képernyő egy másolatán végez a memóriában. Ahhoz, hogy ezt lássuk is, meg kell jeleníteni a képernyőn. Ezzel a paranccsal a puffer tartalmát villámgyorsan kiteszi a videomemóriába, így az számunkra is láthatóvá válik.
A clock.tick(fps) paranccsal a játék futásának sebességét állítjuk be, így minden hardveren azonos sebességel fog futni a ciklusunk, nevezetesen egy másodperc alatt fps alkalommal.

Ejtsünk szót még a rajzol() függvényünkről!
A függvény elején letöröljük a képernyőt, azaz kitöltjük a BGCOLOR színnel, majd a kapott x,y koordinátákra kirajzolunk egy BALLCOLOR színű kört. Ha nem törölnénk le a képernyőt, csak a kört rajzolnánk ki, akkor a labdánk gyakorlatilag csíkot húzna a képernyőre. Ebből is látható, hogy minden képkockán elő kell állítani a teljes képet. Ez bonyolult játékoknál elég időigényes feladat lehet, ezért mindenféle praktikához kell folyamodni a megfelelő teljesítmény elérése érdekében.

A mozgat() függvényünk egyszerű, "csak" számítási feladatot végez, a kapott x,y és vx,vy sebességadatok alapján kiszámolja hova kerül a labda. Ha a képernyőn kívülre kerülne, akkor visszateszi a képernyőre, és megfordítja a vízszintes vagy függőleges (vagy mindkettő) mozgási irányát.

Nos ennyi fért az első részbe, remélem tanulságos volt, folyt. köv...

3 megjegyzés: