7. lecke Guruló golyó

Online Android szakkör (DKRMG)


A korábbi leckékben elkezdtünk megismerkedni a telefonokban található érzékelőkkel (közelségérzékelő, iránytű). Ez idáig egyszerre mindig egy értéket közöltek velünk a szenzoraink (távolság, elfordulás szöge). Most azonban megnézzük, hogy mi történik, amikor kilépünk ebből az egysíkú, egydimenziós világból. Néhány szenzor ugyanis képes egyszerre több mindent is érzékelni!

Ebben a leckében egy egyszerű játékot fogunk elkészíteni, amiben egy golyó gurul össze-vissza a képernyőn aszerint, hogy épp milyen gyorsulási erők hatnak a telefonunkra (hogyan tartjuk, merre forgatjuk és merre mozgatjuk).

A cél

Induláskor jelenjen meg egy golyó(t ábrázoló kép) a képernyő közepén. Ha jobbra-balra illetve előre-hátra billentjük a telefont, a golyó induljon el a megfelelő irányba, engedelmeskedve a gravitáció csábításának.

A terv

Ebben a leckében is egy szenzort fogunk igénybe venni, mégpedig az ACCELEROMETER szenzort, azaz a gyorsulásmérőt. Ez a szenzor közvetlenül elárulja nekünk, hogy a képernyő tetején pihenő képzeletbeli golyó merre gyorsulna.

Most is az lesz a feladatunk, hogy feliratkozzunk a szenzorra, új adat érkezésekor az kiolvasott értékeknek megfelelően módosítsuk a golyó pozícióját, majd a program leállásakor leiratkozzunk a szenzorról.

Vázlatosan tehát:

  1. A golyó képének bemásolása az alkalmazásba
  2. Felhasználói felület kialakítása
    • ImageView komponens elhelyezése a layout szerkesztővel
    • szükséges tulajdonságok (méret, elhelyezkedés) beállítása
    • kép forrásának megadása
  3. Feliratkozás az ACCELEROMETER (gyorsulásmérő) szenzorra
    • ImageView komponens elhelyezése a layout szerkesztővel
    • szükséges tulajdonságok (méret, elhelyezkedés) beállítása
    • kép forrásának megadása
  4. Az onSensorChanged függvény törzsének befejezése, hogy a szenzortól érkező adatoknak megfelelően arrébb mozdítsa a golyót.

Az eredmény

Előfordulhat, hogy több olyan játékkal is találkoztatok már, amit a telefon mozgatásával kellett vezérelni (pl.: SpeedX 3D, Doodle Jump, aTilt 3D Labyrinth Free). Ezek mind úgy működnek, mint a mi egyszerű Guruló golyó játékunk. A különbség csak annyi, hogy a kiolvasott értékeket máshogy használják fel. Ha az alapok megvannak, akkor már csak egy jó ötlet kell, és ilyen népszerű játékok születhetnek nem túl sok munkával!

Néhány megjegyzés

A komponenseket, mint amilyen az ImageView is, csak Android 3.0 (API 11) óta lehet kényelmesen forgatni és arrébb tologatni. Szerencsére majdnem mindenkinek ennél újabb készüléke van, de ha esetleg mégis egy régebbi (pl.: API 9 Gingerbread-et futtató) készüléken dolgoznál, van megoldás. Figyeld az utasításokat!

Importálás helyett… új projekt

Ebben a leckében szakítunk az eddigi gyakorlattal, és Ti fogjátok előkészíteni a projektet! Nem kell megijedni, nem olyan nagy ördöngösség, csak oda kell figyelni pár dologra. Emlékeztetésként: a második leckében (Hellow World) is csináltatok már hasonlót!

  1. Indítsuk el az Android Studiot. Ha rögtön megnyitja a legutóbbi projektet, akkor a File -> Close project menüvel zárjuk be!
  2. A megjelenő kisebb ablakban válasszuk a "Start a new Android Studio project" lehetőséget.
  3. A következő lépésben adjuk meg az alkalmazásunk alap adatait:

    • Application name: RollingBall (Ez jelenik meg az ikon alatt a telefonon)

      Felmerülhet a kérdés, hogy miért nem magyar nevet adunk a programunknak. Azért, mert itt a projekt létrehozásakor nagyon sok mindent a név alapján állít be az Android Studio, és mivel a "Guruló golyó" szóközt és ékezetet is tartalmaz, furcsa dolgok történhetnének. Sajnos az informatika mai gyakorlatainak megalapozásakor nem gondoltak a magyarhoz hasonló egzotikus nyelvekre :)

      Később lesz lehetőség a feliratot átírni magyarra.

    • Company domain: android.dkrmg.hu

      Mint a második leckében is írtuk, Java-s körökben az a szokás, hogy minden cég/szervezet a saját weboldalának címével azonosítja az általa írt kódokat. Mi a DKRMG-n belül az Android csoport vagyunk, így ez a képzeletbeli weboldalunk címe.

    • Project location:

      Ebbe a könyvtárba fogja létrehozni a projektet az Android Studio. Célszerű ott megadni egy könyvtárat, ahol a korábbi leckékhez tartozó projektek is vannak. pl.: C:\DkrmgAndroid\L07-RollingBall vagy Win7 alatt gyakran: C:\Users\[felhasznalo]\AndroidStudioProjects\L07-RollingBall.

    • Lépjünk tovább a következő oldalra.

  4. Ezen az oldalon megadhatjuk, hogy milyen eszközökre szeretnénk fejleszteni.

    • Egyelőre maradjunk a Phone and Tablet opciónál!

    • A "Minimum SDK" mező jelzi, hogy mi legyen a legrégebbi Android verzió, amire fel lehet majd telepíteni az appot.

      FONTOS!!! Itt mindenképp (akkor is ha régebbi telefonod van) az API 19: Android 4.4 (KitKat) legyen kiválasztva! Ez azért szükséges, mert az Android Studio ebben ay esetben kicsit túlzottan is segítőkész, és ha alacsonyabb verziód adunk meg, akkor mindenféle kiegészítő dolgokat is belerak a projektbe, ami egy komolyabb alkalmazásnál jó lehet, de minket most csak zavarna!

    • Lépjünk tovább a következő oldalra.

  5. Innentől ismerős lesz a folyamat, ugyanis most következik a kiinduló Acitivity hozzáadása. Az előző leckéhez hasonlóan válasszuk a Empty Activity lehetőséget; a következő oldalon pedig a neve legyen GolyoActivity.

  6. Végül kattintsunk a Finish gombra!

  7. Némi gondolkodás után (ami számítógéptől függően sajnos akár pár percig is eltarthat) el is készült az új projektünk! Még annyi dolgunk van, hogy visszaállítsuk a Minimum SDK verziót a telefonunknak megfelelően. Ellenkező esetben az SDK 19-nél régebbi telefonokon nem indulna el az alkalmazásunk.

    • Győződjünk meg arról, hogy bal oldalt a projektünk fájlai Android nézetben láthatóak.

    • Bal oldalt nyissuk meg a Gradle Scripts -> build.gradle (app) fájlt.

    • Ebben keressük meg a minSdkVersion 21 sort és írjuk át a számot:

      • Ha Android 2.3.3 Gingerbread-es telefonunk van, akkor 10-re
      • Ha újabb, akkor legalább 11-re.

Kép bemásolása a projektbe

Ha a kedves kis Android ikonon kívül más képeket is szeretnénk használni az Android projektünkben, akkor a képfájlokat előbb elérhetővé kell tennünk az Android rendszer számára.

Ehhez a képfájlokat be kell másolnunk az Android Studio projektünkbe, hogy aztán az Android Studio bepakolhassa őket az alkalmazásba és elküldje a telefonnak is.

Ahhoz, hogy az Android Studio megtalálja a képfájlodat, először is keresd meg a Windows fájlkezelővel az app\src\main\res mappát a projekteden belül.
(Ezt úgy is megteheted, hogy az Android Studioban a res mappára rákattintasz jobb gombbal, majd kiválasztod a Show in Explorer menüpontot)

Készíts a res mappában egy új mappát drawable néven, és másold bele ezt a képet!

Ezzel pedig elérhetővé vált a golyó képe a programod számára. Mostantól kezdve a fájl nevével (R.drawable.ball) tudsz hivatkozni rá (pl. megjeleníteni)

Amikor saját képet másolsz be a projektbe, vigyázz, hogy a fájl neve csak az angol abc kisbetűit és a _ karaktert tartalmazhatja. Nagybetűk és ékezetek nem megengedettek.

Valamint az is fontos, hogy a drawable mappába kerüljenek a képek, különben nem lesznek elérhetőek!

Pl. egy jó fájl: drawable/ez_egy_kep.png

Miért forog a képernyő…

Ha most futtatod az alkalmazásodat, akkor bizonyára észreveszed, hogy a telefon elforgatásakor a kijelzően az applikáció ablaka mindnent megtesz azért, hogy továbbra is a jó irányba forduljon.

Ez a viselkedés sokszor hasznos, azonban (mint remélhetőleg emlékeztek rá), minden elfordításnál egy pillanatig leáll az alkalmazásunk (onPause), majd újraindul (onResume). Tehát az automatikus elforgatás egy olyan játéknál, ami a képernyő elforgatására épül, komoly gondokat okozhat.

Szerencsére az alkalmazásunk tulajdonságaiban meghatározhatjuk azt, hogy szeretnénk-e élni az automatikus elforgatás lehetőségével.

Ennek a beállításához nyisd meg bal oldalt az app -> manifests -> AndroidManifest.xml fájlt! Ez az Xml fájl tartalmazza az applikációnk néhány fontos beállítását.

Nem szeretnénk részletekbe bocsátkozni a manifeszt fájllal kapcsolatban (bár az érdeklődőbbek megnézhetik az android:icon vagy épp az android:label tulajdonságokat is)

Készíts egy új sort az activity pont tulajdonságain belül (pl. android:name=".GolyoActivity" alá), és másold be az alábbi szöveget. Figyelj, hogy a záró > elé írd be!

android:screenOrientation="portrait"
Ezzel beállítottuk, hogy mi bizony minden esetben ragaszkodunk a portrait nézethez; és köszönjük, de nem szeretnénk, hogy minket bárki is elforgasson.

Felhasználói felület (layout)

layout szerkesztő

A felhasználói felületünk igen egyszerű, és nagyban hasonlít az előző leckében használtakhoz. A következőket kell tenned (ha bizonytalan vagy, ott a gyorssegély, vagy vissza is olvashatsz egy kicsit a korábbi leckékben)

  1. Nyissuk meg a Layout szerkesztőt. A Design fül legyen kiválasztva.
  2. Az Android Studio a projekt létrehozásakor belerakott egy TextView komponenst, csak hogy látszódjon valami rögtön az elején. Ez nekünk nem fog kelleni, jobbklikk -> Delete-el töröljük is!
  3. Helyezzünk el egy ImageView komponenst a telefon kijelzőjére. Az ImageView legyen A bal felső sarokban.
    • Állítsuk be az ImageView méretét (layout:width és layout:height). Mondjuk legyen 30dp magas és 30dp széles. Vagy bármilyen más értéket is választhatsz, ami jól néz ki.
    • Adjunk az ImageView-nak egy id tulajdonságot is. Mondjuk golyo
    • Nézzük meg, hogy a layout:centerInParent tualjdonság legyen üres. Ellenkező esetben a golyó nem fog rendesen mozogni.
    • Válasszuk ki, hogy az ImageView az golyó képét mutassa. Ehhez az src (source) tulajdonságnál kattints a […] gombra. Ha mindent jól csináltál a kép bemásolásakor korábban, akkor ott kell lennie a ball képnek. Ezt válaszd is ki!
  4. Kattints a háttérre, hogy a RelativeLayout komponens legyen kiválasztva. Ez a komponens az ablakunk háttere.
    • Adj valamilyen azonosítót a háttérnek is(id), pl.: root.
    • Állítsd be a színét (background) feketére. Némely telefonnál ez az alapértelmezett szín, de menjünk inkább biztosra. Ezt (emlékeztetésként) vagy úgy tudod megtenni, hogy beírod a fekete szín kódját (#ff000000), vagy a sor végén lévő […] gombra kattintasz, majd a Colour fülön kiválasztod a tetszőleges színt. .
    • Győződjünk meg arról, hogy a RelativeLayout a teljes képernyőt kihasználja, és nincsen semmilyen belső margó beállítva. Ehhez állítsuk a padding tulajdonságot üresre (vagy mindenhol 0-ra).

Az Android Studio újabb verzióiban előfordulhat, hogy hiába futtatod a programot, a kép valamiért nem jelenik meg a képernyőn. Esetlegesen még egy hibaüzenet is fogadhat:

Error:(13) No resource identifier found for attribute 'srcCompat' in package 'hu.dkrmg.android.rollingball'

A hiba áthidalásához sajnos egy kicsit bele kell másznunk a layout fájl szöveges verziójába.

  1. Nyisd meg a layout fájlt
  2. Az ablak alján válaszd ki a Text nézetet.
  3. Ezen a ponton láthatod a layoutot leíró XML dokumentumot. Ha nem találkoztál még XML dokumentumokkal, akkor sem kell megijedni.
  4. Keresd meg az ImageView pont alá tartozó app:srcCompat="@drawable/ball" sort, és írd át erre: app:src="@drawable/ball"

Java változók

Java kód

Érdemes most még az elején létrehozni az ImageView típusú változónkat (az onCreate fölött!), és hozzárendelni a valódi képhez. Ezt javasolnánk, hogy az onCreate függvényben tedd meg a findViewById segítségével. Így a program indulásakor már túl is essünk a munka ezen részén.

Javaslatunk az, hogy legyen az ImageView változónk neve golyoKep (mivel ez a kép mutatja magát az golyót).

Ezen kívül a golyó mozgatása során szeretnénk a háttér (root) szélességére is hivatkozni. Ezért érdemes a hátteret még most elmenteni egy változóba. Készítsünk egy RelativeLayout típusú változót is a program elején. A neve lehet root. Ne felejtsd el ehhez a változóhoz is hozzárendelni a megfelelő értéket az onCreate függvényben!

Az Android Studio megint segíteni szeretett vonlna, ezért beleírt két függvényt a GolyoActivity fájlba (onCreateOptionsMenu és onOptionsItemSelected). Ezek a menü kezelésére szolgálnak, de mi nem fogjuk használni őket. Ha zavarnak, nyugodtan töröld ki őket, de ekkor nagyon figyelj, hogy minden részét töröld (@Override-al kezdődik, és a vele egy oszlopban lévő } jellel végződik)!!! Ha inkább benne hagynád, akkor sem történik semmi.

A gyorsulásmérő szenzor
(TYPE_ACCELEROMETER)

Java kód

Ez a rész egy az egyben megegyezik az előző két leckének az idevágó feladatával, a szenzor típusától eltekintve. Javasoljuk, hogy lapozz vissza az ötödik leckére, ha bizonytalan vagy. Különben pedig itt a Gyorssegély is.

  1. írd be a public class GolyoActivity… sor megfelelő pontjára az implements SensorEventListener szavakat. Így jelezve, hogy képes az Activitynk üzeneteket fogadni.
  2. Írd meg az onSensorChanged és az onAccuracyChanged függvények vázát.
    • Kezdd el beírni az egyik függvény nevét, majd az első 4-5 betű után válaszd ki a megfelelő függvényt a felbukkanó listából. Ezek után Enter.
    • Ne felejtsd el ugyanezt megtenni a másik függvénnyel is!
  3. Hozz létre egy SensorManager típusú változót (a neve lehet sensorManager kisbetűvel), és rendeld hozzá a rendszer szenzor managerét. Ezt az onCreate függvényben tettük meg a getSystemService(SENSOR_SERVICE) függvénnyel.

    A hozzárendelés teljes parancsa szerepelt már a korábbi leckében is: sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);

  4. Iratkozz fel az accelerometer szenzor üzeneteire / érzékeléseire.
    • Készítsd el az onResume függvényt (az Android Studio segít, ha elkezded beírni)
    • Nézd meg, hogy az ötödik leckében (vagy akár a hatodikban) hogyan használtuk a getDefaultSensor és a regesiterListener függvényeket. Csak annyi a dolgod, hogy átírod a TYPE_PROXIMITY-t TYPE_ACCELEROMETER-re. Ezzel máris a gyorsulásmérő szenzorra iratkozunk fel.

      Tényleg megnézted az előző leckéket?! Most még itt megadjuk a megoldást, de ezt a két sort vésd a fejedbe!

      
      Sensor sensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
      sensorManager.registerListener(this, sensor, SensorManager.SENSOR_DELAY_UI);
      
  5. Ne felejts el leiratkozni sem az üzenetekről az onPause függvényben! Ehhez először meg kell írnod az onPause függvényt (az Android Studio segít).

    sensorManager.unregisterListener(this);

Kész is! Ha elakadtál, vagy nem vagy benne biztos, hogy jól csináltad-e, akkor hasonlítsd össze az előző lecke megoldásával. Ne feledd, ezen a ponton az onSensorChanged függvényed még üres (a két kapcsos zárójel között nincs semmi).

Futtasd le a programodat! De honnan tudod, hogy működik-e?

Akik megcsinálták a negyedik lecke Logging szorgalmiját, azok remélhetőleg itt felhasználják a tudásukat. Ha te nem vagy ennek a szorgalmas társaságnak a tagja, akkor olvass tovább!

Kitérő – tömbök

Az eddigi leckék során találkoztunk már egyszerű/primitív típusokkal (int, long, float), amelyek csupán egy értéket tárolnak. Illetve kissé felszínesen megismerkedtünk az összetettebb osztályokkal is (pl Button, SensorManager, Activity...), melyek egy rakás érdekes belső tulajdonsággal és belső függvénnyel rendelkeznek.

A mai napon pedig bemutatjuk az egyik legfontosabb adatszerkezetet: a tömböt.

Mi az a tömb?

A tömb képes több azonos típusú változót tárolni. A tömb elemei a sorszámuk alapján érhetőek el, értékük bármikor változtatható.

Például ha van egy t nevű tömbünk, akkor az elemei:t[0], t[86], t[242], stb.

Ez eddig nagyban hasonlít a Pascal/Lazarus tömbjeihez. Fontos azonban megjegyeznünk, hogy a fenti nyelvektől eltérően a Java 0-tól indexeli a tömbjeit. Ez azt jelenti, hogy az első elem a tömbben a t[0], a második a t[1], harmadik a t[2] és így tovább. Tehát ha van egy 10 elemű tömbünk, akkor annak elemei: t[0], t[1], t[2], … , t[7], t[8], t[9].

Tömb használata

Az összes többi változóhoz hasonlóan a tömbünket is deklarálni kell valahol. Itt adjuk meg a változó nevét és a típusát. Ezt pedig úgy tehetjük meg, hogy a típus után illesztjük a [] jeleket.

Pl. egy tömb, amelynek minden eleme int típusú

int[] szamokatTartalmazoTomb;

Egy másik tömb, melynek minden eleme a Button osztályba tartozik

Button[] gombok;

Egy létező tömbnek így nyerhetjük ki a harmadik elemét:

int harmadikElem = szamokatTartalmazoTomb[2];

De hozzá is rendelhetünk új értéket valamelyik elemhez. Pl.

gombok[1] = findViewById(R.id.button);
Megjegyzés:

ha egy teljesen új tömböt szeretnénk használni, akkor azt nem elég deklarálni, létre is kell hoznunk első használat előtt (pl. az onCreate függvényben). Ilyenkor kell megadnunk a tömb hosszát is, azaz hogy összesen hány elemet tárol. Pl. a szamokatTartalmazoTomb tömb inicializálása úgy, hogy 100 elemet tartalmazzon (0.. 99).

szamokatTartalmazoTomb = new int[100];

Amikor egy függvény paramétereként kapjuk a tömböt, erre a fenti parancsra nincs szükség, hiszen a tömbünket (remélhetőleg) az Android rendszer már inicializálta / létrehozta és feltöltötte hasznos adatokkal.

A golyó mozgatása a képernyőn

A golyó mozgatását első megközelítésben nagyon egyszerűen fogjuk megoldani:

Amikor érkezik egy új mérés a telefon gyorsulásmérőjéről, annyiszor arrébb toljuk a golyót mutató képet a megfelelő irányba.

Természetesen a golyó nem tud kilépni a telefon síkjából, így csak két tengely (x,y) mentén mozgunk. A golyót a gyorsulásmérő által mért értékkel fogjuk arréb tolni. Ez a megoldásunk fizikailag nem teljesen helyes (gyorsulás nem egyenlő sebesség, lásd szorgalmik), de egy ilyen egyszerű programnál megengedhetünk magunknak ennyi csalást.

A golyó mindenkori pozícióját két int típusú változóban fogjuk tárolni (xPos, yPos). Amikor új mérés érkezik, akkor ezeknek a változóknak az értékét frissítjük, majd beállítjuk, hogy az ImageView komponensünk is a megfelelő helyen legyen.

Feladat

Hozz létre két int típusú változót (xPos és yPos) az osztályod elején. Az onCreate függvényedben adj értéket mind a két változódnak. A golyó eleinte a bal felső sarokban van, így xPos = 0; és yPos = 0;.

Adatok kinyerése az event.values tömbből

A tömbökkel kapcsolatos új tudás birtokában talán nem túl meglepő ha itt megállapítjuk: az event.values változónk float[] típusú, tehát egy olyan tömb, melynek minden eleme float típusú. (event.values változóval az onSensorEvent függvényben találkoztunk).

Gondoljunk vissza a korábbi leckékre, ahol az event.value[0] kifejezést használtuk. Hiszen ezzel valójában az event.values tömb első elemét nyertük ki, nem?

Most nézzük meg, hogy a gyorsulásmérő mit is küld nekünk pontosan az event.values tömbben:

[0]: első elem [1]: második elem [2]: harmadik elem
gyorsulás az x tengely mentén gyorsulás az y tengely mentén gyorsulás a z tengely mentén

A képről is láthatjuk, hogy a harmadik érték (z tengely) számunkra haszontalan, hiszen a telefonunk (3D-s kijelző híján) nem tudja kiemelni a golyót a saját síkjából. Viszont a másik két értéket használni tudjuk!

Feladat

Az onSensorEvent függvényben hozzunk létre két float típusú változót (xGyorsulas, yGyorsulas. Majd rendeljük hozzá a változónkhoz a megfelelő értéket az event.values tömbből.

Új pozició kiszámítása

Az új pozíció kiszámítása az egyszerűsített modellünkben elég egyszerű. Mint említettük, egy kis csalással azt feltételezzük, hogy az új pozíció nem más, mint a régi pozíció + a mért gyorsulás.

Tehát például:

float ujX = xPos + xGyorsulas;

Hasonlóan írd meg az ujY változót is!

Ha kiszámítottuk az új pozíciót, akkor frissítsük erre az xPos és az yPos változó értékét.

Óvatosnak kell lennünk, hiszen az xPos és yPos változóink egész számokat tartalmaznak (int), míg ujX és ujY változónk float típusúak. Tehát könnyedén lehet, hogy a tartalmuk nem egész szám. Az értékadás közben emiatt kerekítenünk is kell egyet! Ezt szerencsére az Android segítségével könnyen megtehetjük.

xPos = Math.round(ujX);

Ez alapján írd meg az yPos értékadását is!

A negyedik szorgalmiban megnézzük, hogy ez a modell fizikailag miért nem helyes, és hogyan lehetne javítani.

Golyó áthelyezése

Ha a telefonunk Android 3.0 (API 11), vagy annál újabb rendszert futtat, akkor nagyon egyszerűen megadhatjuk az ImageView komponenseink helyét. Annyit kell csak tennünk, hogy meghívjuk az ImageView setX és setY függvényeit az x és y ko-ordináta megadásához.

Tehát például ha például ha azt akarjuk elérni, hogy a golyó a bal felső sarokban legyen (kezdéskor remélhetőleg már ott van), akkor az alábbi két parancsot használjuk:


golyoKep.setX(0); // vizszintes
golyoKep.setY(0); // fuggoleges

Ha esetleg egy kicsit öregebb telefonod lenne, akkor se csüggedj. Van mód a képek áthelyezésére így is, bár kicsit bonyolultabb. Hogy ne érjen hátrány ebben a leckében, így írtunk neked egy segédfüggvényt, ami elvégzi helyetted a piszkos munkát.

ImageViewHelper.setImageViewPosition(imageViewValtozo, xErtek, yErtek);

(Ez a függvény egy általunk írt osztályban található. Mivel ebben a leckében saját projektet használtok, ezért ezt a java fájlt le kell töltenetek, és be kell másolnotok a MainActivity.java fájl mellé.)

Most írd meg azt a két parancsot az onSensorEvent függvényben, ami a golyoKep változónkat nem az origóra, hanem az (xPos;yPos) pontra helyezi!

Futtasd a programot, és nézd meg, hogy működik-e. Előfordulhat, hogy a golyó a várttal ellentétes irányba indul el (x irányban). Ezt a hibát remélhetőleg könnyedén tudod javítani az előző lecke után ( + helyett - ).

Ne feledd, a golyó ilyenkor még könnyedén kiléphet a telefon kijelzőjéről. Ezt az első szorgalmiban fogjuk orvosolni. Javasoljuk, hogy mindenki nézze ezt meg

Ha esetleg úgy érzed, hogy a golyó nem mozog elég gyorsan, akkor próbáld az xGyorsulas változót megszorozni mondjuk 3-al. pl.:

float ujY = yPos+ yGyorsulas * 3;

Ha végeztél és minden működik, töltsd fel a projektedet tömörített (pl zip) formátumban a szakkör feltöltő oldalán keresztül!

Szorgalmi feladatok

1*. Ütközés a képernyő szélén

Bizonyára észrevetted, hogy amikor a golyó eléri a képernyő szélét, akkor egyszerűen tovább gurul. Mi persze azt szeretnénk, hogy a golyó soha ne kerüljön le a képernyőről. Ehhez nem kell mást tennünk, mint hogy elvégzünk néhány ellenőrzést az ujX és az ujY változóinkon, még mielőtt naívan felhasználjuk őke

Tehát mire kell vigyáznunk?

Hogyan tudjuk ezt leprogramozni? A legegyszerűbb, ha az ujX, ujY változók értékadása után (de még az előtt, hogy az értékeiket tovább adnánk kerekítéssel a posX és posY változóknak), írunk 4 if-elágazást az alábbi formában


HA (ujX túl kicsi) {
    ujX = 0;
}                
                
pl.:

if (ujX < 0) {
  ujX = 0;
}
                
A golyoKep méreteit a golyoKep.getWidth() és golyoKep.getHeight() függvényekkel lehet lekérdezni:

if (ujX > root.getWidth() - golyoKep.getWidth())
{
  ujX = root.getWidth() - golyoKep.getWidth();
}
                

Most már a golyó nem fog eltűnni a képernyő bal és jobb oldalán.

A képernyő magassága ehhez hasonlóan megkapható a root.getHeight() függvénnyel. Ez alapján írjál még két elágazást, ami eléri, hogy a golyó ne tűnjön el a képernyő alján és tetején sem!

2*. Karácsonyi kép

Közeledik a karácsony (a leckék írásakor legalábbis december eleje van már). Cseréld le a golyót valami karácsonyi hangulatúra!

Ehhez:

3*. Rezgés, amikor a golyó a képernyő falának ütközik

Szükséges az első szorgalmi elvégzése Használjuk a negyedik leckében tanultakat, és változtassuk a jelenlegi programunkat úgy, hogy amikor nekiütközik egy falnak, akkor a telefon elindít egy rövid rezgést (pl. 100ms) Ehhez az alábbiakat kell tenned:

Fontos!!! Ha most elindítod a programodat, akkor a rezgés még nem fog működni. Az Android rendszer ugyanis bizonyos erőforrásokhoz (pl. rezgő motor) csak olyan alkalmazásoknak hajlandó hozzáférést biztosítani, akik előre engedélyt kértek.

Ha telepítettél már Android programokat a telefonodra, akkor minden bizonnyal találkoztál már egy olyan a dialógussal, ami kiírja, hogy "az alkalmazás az alábbi erőforrásokhoz kér hozzáférést:".

A rezgő motor is egy ilyen védett erőforrás, ezért az alkalmazásunknak a manifeszt fájlban jeleznie kell, hogy használni szeretné.

Most már futtathatod az alkalmazást. Ezzel az egy sorral jelezted az Android rendszernek, hogy a programod szeretné a rezgő motort (a korábbi leckékben ezt a sort mi beleírtuk az előkészített projektekbe).

4*. A fizika törvényei

Számos fizikatanár kapna szívrohamot, ha meglátná azt a módszert, amivel a golyót egészen idáig mozgattuk. A helyes képlet természetesen azt mondja ki, hogy az elmozdulás (megtett út) egyenlő a sebesség és az eltelt idő szorzatával. A sebesség pedig egyenlő a gyorsulás és az eltelt idő szorzatával.

Az ehhez hasonló (ún. diszkrét) szimulációknál ezért inkább az alábbi képleteket használjuk (delta (Δ) a változást jelenti. pl. sebesség változása):

5*. Egy kis tehetetlenség

Főként a fizikát kedvelőknek javasoljuk, hogy vizsgálják meg, mi történik, ha az álló telefont hirtelen megmozdítjuk. Hogyan reagál a golyó? Mi a helyzet akkor, ha az egyenletesen mozgó telefont hirtelen megállítjuk? Mit csinál ilyenkor a golyó? Milyen magyarázatot tudsz adni minderre?

6*. Pattogás

Jelenleg a golyó, amikor eléri a képernyő szélét, egyszerűen megáll. Bizonyára azonban a való életben tapasztalhattátok, hogy a falnak lökött golyók és labdák egyáltalán nem így viselkednek! Az elmozdíthatatlan fal és a kemény fémgolyó ütközésekor a golyónak legalább egy kicsit vissza kéne pattannia!

Az ütközésre vonatkozó szabályok szerencsére elég egyszerűek (bár fizikailag ezek sem tökéletesek, de egy Android programnál már így is nagyon jól néznek ki

A valódi életben az ütközéskor valamennyit veszítene a golyó a sebességéből. Az elveszített energia mértéke pedig sokban függene a golyó anyagától. Gondolj bele: amikor egy kosárlabdát elengedsz, akkor majdnem az ugyanolyan magasra visszajön (de csak majdnem). Egy fémgolyó is pattan, de kisebbet

Próbáld meg ezt úgy szimulálni, hogy ütközéskor megszorzod a sebességet (mind a két irányban) egy állandóval. Például 0.35-el. Minél nagyobb az érték, annál nagyobbat pattan a golyó!