Reptess madarat az Aurduinóval!
Csak egy Arduino, egy érintőképernyő, valamint némi kódolás kell, és a Flappy Bird már fut is az eszközödön.
Nem lehetetlen Arduinóra játékot készíteni, de annak, hogy egy ténylegesen működő prototípust alkoss, elengedhetetlen feltétele, hogy alaposan ismerd a platformot, és persze az sem árt, ha a megfelelő kiegészítők is a rendelkezésedre állnak. A kulcs a sikerhez egyértelműen a megjelenítőkben rejlik, mert bár képernyő nélkül is össze lehet állítani szórakoztató kis játékokat, a kijelzős interakció rengeteget dob a felhasználói élményen. Ha pedig a vezérléshez még további gombokkal és kábelekkel sem kell vesződnöd, hanem mindent megoldhatsz egy érintőképernyőn, az már csak hab a tortán. Cikkünkben most egy Flappy Bird-szerű játék elkészítését mutatjuk be részletesen.
Ami mindenképp kell
A komponensek összeállítása során pusztán egyetlen kiegészítőt kell felírnod a bevásárlólistádra, mégpedig egy érintésérzékeny megjelenítőt. Mi a példakód készítéséhez egy 2,4 hüvelykes, TFT LCD Shieldet használtunk, amely 240×320-as képpontos. Ez az adat azért kiemelten fontos, mert a pixelek kirajzolása kulcsszerepet játszik, így amennyiben esetleg más felbontással kívánod elkészíteni a programot leírásunk alapján, muszáj arra is figyelned, hogy a megfelelő helyeken mindenképp a saját minimum és maximum értékeiddel dolgozz.
Persze nem elég, ha a hardver a rendelkezésedre áll, még lesz egy kis szoftveres teendőd is, mielőtt a kódolásba belekezdhetnél, mivel számos programcsomagot is telepítened kell. Éppen ezért miután elindítottad az Arduino programozói környezetét, navigálj a menüsávon az Eszközökön belül található Könyvtárak kezelése menüpontba, és installáld az Adafruit GFX Libraryt, az Adafruit TFTLCD Libraryt, valamint az Adafruit TouchScreen nevű csomagot is, rájuk lesz ugyanis szükséged a képernyő életre keltéséhez. Ha már minden hozzávalóról gondoskodtál, egy példakód segítségével ellenőrizd a képernyő megfelelő működését.
Ehhez navigálj a Fájl menü Példák menüpontjába, és válaszd az Adafruit TFTLCD Libraryben található tftpaint kódot, majd futtasd is le. Ez a kis szoftver a képernyő és az érintési funkciók élesítése során is a segítségedre lesz, így az ellenőrzés mellett tökéletes alapot kínál játékod létrehozásához.
Írd át!
Az alapbeállításokat mindenféle módosítás nélkül érintetlenül hagyhatod a kódban, mindössze annyi a teendőd, hogy megkeresed a setup() függvény kezdetét, és mindent kigyomlálsz a magjából, majd hasonlóan jársz el a loop() esetén is. Első lépésként hozz létre egy külön függvényt a program végén a madár kirajzolásához (void madar(int n){}).
Az alkotás lényege most a funkcionalitás lesz, nem feltétlenül a szépség, így a bemutatásra szánt verzióban csak egy kör és egy csőr alkotja majd az elkészített madár sziluettjét (tft.fillCircle(tft.width()/3,tft.height()/2-45+v,18,YELLOW); tft.fillRect(tft.width()/3,tft.height()/2-40+v,25,7,RED);). Ha ez megvan, ugorj át a setup() belsejébe, és indítsd el a kijelzőt (tft.reset(); tft.begin(0x9341);), állítsd be a forgatását (tft.setRotation(135);), és töltsd ki kék színnel (tft.fillScreen(CYAN);).
A következő sorban jelenítsd meg a madarat (madar(0);), majd állítsd át a kurzort az üdvözlőszöveg kiíratásához (tft.setCursor (30,135); tft.setTextColor(BLACK); tft.setTextSize(4); tft.print("NYOMJ IDE!");). Annak érdekében, hogy ne rögtön a loop()-ban zajló játékba ugorj fejest, ezeknek az adatoknak a megjelenése után muszáj közbeiktatni egy kis várakoztatást, vagyis egészen pontosan arra van szükség, hogy csak akkor lépjen tovább, ha hozzáértél a képernyőhöz.
Az érintésérzékelés megvalósításához lépj ismét a szoftver végére, és kreálj egy függvényt erintes() néven (TSPoint erintes(){}), a magjában pedig hozz létre egy TSPoint p lokális változót. A detektálás egy do-while ciklus futtatásával jön létre, amelyből csak akkor lép ki a program, ha ténylegesen a képernyőhöz nyúltál (do{ p=ts.getPoint(); pinMode(XM, OUTPUT); pinMode(YP, OUTPUT); } while((p.z < MINPRESSURE )|| (p.z > MAXPRESSURE)); return p;).
Visszalépve a setup()-ba már használhatod is az imént létrehozott erintes() függvényt, majd a következő sorban kirajzoltathatod a játék képét. Ehhez színezd át teljesen a képernyőt (tft.fillScreen(CYAN);), rajzold meg az alsó és felső sávokat (tft.fillRect(0,0,tft.width(),20,GREEN); tft.fillRect(0,tft.height()-25,tft.width(),25,GREEN);), végezetül pedig helyezd el a főszereplő madaradat is a jelenetben (bird(0);).
Indulhat a játék
Mielőtt megtanítanád repülni ezt a jelképes szárnyast, érdemes elhelyezned az akadályokat, velük ellenőrizheted a koncepció működőképességét. Ehhez nincs más teendőd, mint létrehozni egy globális i változót (int i=0;), a loop belsejében a tft.fillRect(tft.width()-40-(i*2),20,20,20,GREEN); és a tft.fillRect(tft.width()-40-(i*2),165,20,50,GREEN); sorokkal életre hívni az oszlopokat, a ciklus végén pedig növelni az i értékét (i++;).
Ügyelned kell arra, hogy a mozgás során az előző verzió is a képernyőn maradna, ezért egy 20-as értékkel elcsúsztatva törölnöd is kell nagyjából azonnal, méghozzá úgy, hogy az oszlopot a háttérrel megegyező színűre fested. Hogy ismétlődően megjelenjenek az akadályok, amint az i értéke eléri a 160-at, állítsd azonnal nullára ( if(i==160){ i=0; }). Ezzel el is jutottál arra a pontra, hogy animációként már működik a pálya, vagyis csupán a konkrét karaktermozgatás megvalósítása hiányzik.
A játékban a főszereplő repülésének koncepciója annyiból áll, hogy folyamatos zuhanás mellett érintésekkel próbálod a kijelzőn belül tartani. Vagyis lényegében minden körben megszabadulsz az előző madár képétől, és a helyére rajzolod az újat, ehhez pedig kelleni fog két globális változó (int j=0, most=0;). Ha megvagy velük, a loop()-ban aktiváld az érintések figyelését (TSPoint r = ts.getPoint();pinMode(XM, OUTPUT); pinMode(YP, OUTPUT);), és amennyiben teljesül ez a feltétel, csökkentsd a j értékét (if (r.z > MINPRESSURE && r.z < MAXPRESSURE) { j=-5; }).
Itt az ideje, hogy rátérj a zuhanásra: a most=most+(2*j); segítségével beállíthatod a madár új pozícióját, a madar(most); utasítással ki is rajzolhatod, a j++; kódba biggyesztésével pedig garantálhatod a folyamatos esést. Mivel minden körben az oszlopokhoz hasonlóan törölnöd kell a madár előző verzióját: hozz létre egy void madarTorol() függvényt a kód végén úgy, hogy teljes egészében lemásolod a korábbi madárfüggvényt, és csupán annyit módosíts rajta, hogy a YELLOW és a RED értékeket írd át CYAN-ra.
Ha ezek után a madarTorol(most); függvényt a loopban elhelyezed a most értékét módosító sor fölé, akkor még az előző adatokból kalkulálja majd a törlés helyét a program, és eltávolítja a korábbi frame alatt a kijelzőre rajzolt szárnyast. Ezzel tulajdonképpen el is készültél a fő mechanizmussal, már csak a földbe csapódás van hátra, illetve az oszlopok érintésekor muszáj jelezned a felhasználóknak, hogy itt bizony véget ért a játék.
Szerencsére ez sem túl bonyolult, mindössze néhány feltételt kell vizsgálnod. Közülük talán a legfontosabb a plafon és a talaj érintése (most>=122 || most<(-34)), illetve az, hogy jelenleg merre is találsz oszlopokat a képernyőn ((280-(2*i)<tft.width()/3 && 280-(2*i)>tft.height()/2-45 &&( 57+most<38 || 93+most>165 ))). Ha ebbe a függvénybe belép a program, akkor színezd át a hátteret (tft.fillScreen(CYAN);), majd tudasd a játékossal, hogy sajnos most véget ért számára ez a kör (tft.setCursor(tft.width()/3+36,tft.height()/2);tft.setTextSize(3); tft.println("Jatek vege!");), és állíts be némi késleltetést (delay(10000);).
Igény esetén ezek után visszaállíthatod az eredeti értékeket, és folytatódhat a játék, vagy resetelheted a mikrovezérlődet a void(* resetFunc) (void) = 0; funkció beemelésével és meghívásával. Ezzel kész is a saját Flappy Bird-klónod. A fejlesztésben ezek után elkészíthetsz pontkalkulátort, csinosíthatod a menüt, és randomizálhatod az oszlopok méretét is. Annak érdekében pedig, hogy megkönnyítsük a dolgodat, az említett a funkciókat kivétel nélkül megtalálod a PC World Plusra feltöltött példakódban.
Kód
int i=0, j=0, most=0;
void setup(void) { tft.reset(); tft.begin(0x9341); tft.setRotation(135); tft.fillScreen(CYAN); madar(0); tft.setCursor (30,135); tft.setTextColor(BLACK); tft.setTextSize(4); tft.print("NYOMJ IDE!"); erintes();
tft.fillScreen(CYAN); tft.fillRect(0,0,tft.width(),20,GREEN); tft.fillRect(0,tft.height()-25, tft.width(), 25, GREEN); madar(0);}
#define MINPRESSURE 10
#define MAXPRESSURE 1000
void(* resetFunc) (void) = 0;
void loop() {TSPoint r = ts.getPoint();pinMode(XM, OUTPUT);pinMode(YP, OUTPUT);
if (r.z > MINPRESSURE && r.z < MAXPRESSURE) { j=-5;}
tft.fillRect(tft.width()-40-(i*2),20,20,20,GREEN); tft.fillRect(tft.width()-40-(i*2),165,20,50,GREEN); tft.fillRect(tft.width()-20-(i*2),20,20,20,CYAN); tft.fillRect(tft.width()-20-(i*2),165,20,50,CYAN); i++;
if(i==160){i=0; }
madarTorol(most); most=most+(2*j); madar(most); j++;
if(most>=122||most<-36||(280-(2*i)<tft.width()/3 && 280-(2*i)>tft.height()/2-45 &&( 57+most<38 || 93+most>165 ))){tft.fillScreen(CYAN); tft.setCursor(50,tft.height()/2); tft.setTextSize(3); tft.println("Jatek vege!"); delay(2000); resetFunc();} }
void madar(int v){ tft.fillCircle(tft.width()/3,tft.height()/2-45+v,18,YELLOW); tft.fillRect(tft.width()/3,tft.height()/2-40+v,20,7,RED); }
void madarTorol(int v) { tft.fillCircle(tft.width()/3,tft.height()/2-45+v,18,CYAN); tft.fillRect(tft.width()/3,tft.height()/2-40+v,20,7,CYAN); }
TSPoint erintes() { TSPoint p;
do{ p=ts.getPoint(); pinMode(XM, OUTPUT); pinMode(YP, OUTPUT);} while((p.z < MINPRESSURE )|| (p.z > MAXPRESSURE));return p; }