Hirdetés

Alkalmazásfejlesztés badára: Fizikai motor mindenkinek

A valós fizikát szimuláló játékok minden platformon nagy népszerűségnek örvendenek. Egy jó ötlettel szinte biztos, hogy kaszálni lehet az alkalmazásfejlesztői versenyen és a jövőben egyaránt, ezért megmutatom az általam ismert legpraktikusabb fizikai motort, a Chipmunk Physicset.

Segítségével a probléma matematikai oldala egyszerűen és hatékonyan megoldható — jó eséllyel platformfüggetlenül: a Chipmunk egy tiszta C99, objektum-orientált kódhalmaz, így a legtöbb esetben minden C/C++-alapú platformon "plug and play" stílusban integrálható. Badán sincs különbség, minimális módosítást és kis konfigurálást igényel csupán a könyvtár felélesztése.

A Chipmunk világa

A könyvtár használatához néhány osztályt kell csak megismerni. Az első ilyen a cpSpace, a motor által szimulált világ. Funkciója három részre bontható:

  • Tartalmazza az összes objektumot (cpSpaceAdd/Remove függvények)
  • Végrehajtja a szimulációt (cpSpaceStep függvény)
  • Globális paramétereket szabályoz, mint például az iterációk száma (iterations, elasticIterations tagváltozók), vagy a minden nem-statikus testre ható "gravitációvektor" és "csillapítás" (gravity és damping).

Ez előbbi egy minden testre folyamatosan ható állandó erő, az utóbbi pedig a minden test lendületét befolyásoló szorzó (a perdület mondjuk így nem csökkenthető, meg kell dolgozni érte), ezek pedig egy lehetséges gyakorlati felhasználás részei.

Minden objektum két logikai részre bontható: a fizikai tulajdonságokkal (pozíció, sebesség, lendület stb.) rendelkező, a szimulációban aktívan résztvevő láthatatlan elem a cpBody, mely egy vagy több cpShape-pel áll kapcsolatban. Ez utóbbiak adják az ütközésdetektáláshoz szükséges formát: körök, vonalszakaszok vagy konvex poligonok tetszőleges kombinációi lehetnek. A testek között különböző sebesség-alapú megkötéseket is lehet definiálni, ezek a cpConstraint osztálynál találhatóak.

Az ütközésdetektáláshoz callbackeket lehet definiálni, melyek cpArbiter példányokat kapnak: ezek az ütközésben részt vevő objektumok párosai. A logika egyszerűsítésére több módszert is biztosít a könyvtár: a cpShape-ek csoportosíthatóak (azonos nemnulla csoportba tartozó formák nem ütköznek), rétegekbe szervezhetőek (bitmask, ha binárisan ÉS-elve nulla az eredmény, nem ütköznek), vagy egy egész számmal jelölt típusba sorolhatóak (ez nem befolyásolja az ütközést, csak nekünk segít). Emellett lehet egy cpShape ún. sensor, ilyenkor nincs fizikai hatása a vele való ütközésnek, de a callbackek meghívódnak.

Lefordítás badára

A forráskódot saját projektünkbe bedobva — pontosabban az include/chipmunk könyvtárat a projekten belüli inc, az src/chipmunk könyvtárat pedig a saját src-be másolva — fordítási hibák tömkelege fogad.

Az első gond az, hogy a tisztaság kedvéért külön könyvtárba helyezett chipmunk headerfájlokat nem találja a fordító. Ez gyorsan megoldható a könyvtár felvételével az include könyvtárok közé.

A következő viszont már komolyabb. A bada SDK nem C99 módban használja a GCC C fordítóját, így az ártatlan for ciklusokba is beletörik a bicska. A C99 mód aktiválásához egy új paramétert kell hozzáadni a C fordító konfigurációjához, ez a következő képen látható.

Chipmunk on bada
[+]

Ez sajnos még mindig nem elég. Simulator és Target célok esetén is először linkelési hiba, majd a valódi telefonon használhatatlan végtermék keletkezik — bármiféle hibaüzenet nélkül összeomlik a program a cpInitChipmunk függvény meghívásakor.

Bár az okával nem vagyok teljesen tisztában, egy gyors javításként a chipmunk.c forrásfájlból az összes *printf függvényhívást ki kell kommentezni. Ezután már helyesen fordul és fut a Chipmunk is.


[+]

Használat bada alatt

Mint említettem, a legtöbb hívás azonos minden platformon. A szimuláció elott meg kell hívni a cpInitChipmunk függvényt — jó hely erre például az OnAppInitializing metódus —, létrehozni a teret a cpSpaceNew() függvénnyel, létrehozni az objektumokat a cpBodyNew és pl. cpCircleShapeNew hívással, és így tovább, ahogy az a forráskódban látható lesz.

A kérdés az, hogy hogyan kötheto össze a bada keretrendszerrel? Én arra gondoltam, hogy egy eseményvezérelt szál legyen felelős a fizikáért, melyben egy Timer adja az ütemet. A rajzolás a UI szálban történik, a tér konzisztens ábrázolását pedig egyszerű Mutexszel valósítom meg. A rajzolást mondjuk leegyszerűsítettem: egy Frame-alapú alkalmazásban Canvasra rajzolom a köröket. Éles helyzetben természetesen az OpenGL ES is használható, de most nem tartottam lényegesnek.

A példaprojekt öt labdát és a képernyőt körülölelő négy falat hoz létre. A labdáknak véletlenszerűen kezdőlendületet ad egy-egy impulzussal, majd hagyja hogy pattogjanak tovább.

Az érdekesebb metódusok a PhysicsTest osztályban találhatóak: az InitShapes hozza létre a labdákat és a falakat — valódi program például tömbkonstansok helyett XML-ből vagy saját szkriptfájlból olvashatják az objektumokat —, az OnTimerExpired felelős a rajzolásért, itt látható a tér objektumai feletti iterálás a cpSpaceHashEach függvény segítségével.

Remélem sikerült meghoznom a kedvet a kísérletezésre. A dokumentáció elég száraz, sajnos vannak dolgok, amiket próbálgatni kell, de mindenféleképpen megéri — ajánlom még kipróbálni a Windowsos demót vagy csak simán a YouTube-ra feltöltött videókat.

Más platformon egy kicsit többet kísérleteztem már vele, úgyhogy ha bonyolultabb kérdés merülne fel, a fórumban megpróbálok segíteni.

Karma

Azóta történt

Előzmények