Lekce 11 - Unity (C#) Android: Start, Skóre, PlayerPrefs
V minulé lekci, Unity (C#) Android: Přestavba, bodáky, jsme upravili pohyb hráče a pozměnili i způsob, jakým se nám posouvají překážky.
Dnes se podíváme na počítání skóre a také na počkání na startu, dokud hráč neklepne, aby měl čas se na hru plně připravit.
Vyčkání na start
Začneme s vyčkáním, dokud hráč nezačne hrát. Pro tento účel
upravíme skript pro pohyb hráče. Prvně ale objektu Player
nastavíme gravitaci na 0
, aby nám při čekání nepadal.
Ideálně si číslo, které jste měli nastavené předtím, někam
poznamenejte.
Nyní si otevřeme PlayerMoveScript
.
Do našeho skriptu si přidáme na úplný začátek novou proměnnou:
bool started = false;
A do Update()
metody vložíme následující kód:
if (!started) { return; }
Když hru spustíme, hráč nám stojí na místě a nehýbe se. To je
přesně to, co jsme chtěli. Ovšem v tento moment chceme čekat, dokud hráč
nezačne hrát. Předtím, než budeme reagovat na nějakou tu akci, si
vytvoříme metodu v tom samém skriptu. Metoda se bude jmenovat
StartGame()
:
void StartGame() { started = true; rigidbody2D.gravityScale = 1f; // Vaše hodnota, např. 1f Vector3 vel = rigidbody2D.velocity; vel = Flap(vel); rigidbody2D.velocity = vel; }
Metoda nám při jejím zavolání nastaví started
na
true
. Také nastaví gravitaci na původní číslo, které jsme
měli nastavené na hráčovi (to si v kódu upravte).
Když hráč klikne, začne padat a musel by rychle klepnout
znovu. Proto jsme zařídili, aby nám postavička zároveň i "flapla" a tím
hráč dostane chvíli času na zareagování. Používáme k tomu metodu
Flap()
, kterou si hned také přidáme.
Kód metody Flap()
je následující:
Vector3 Flap(Vector3 v)
{
v.y = flapAmount;
return v;
}
Nyní upravíme část pod podmínkou !started
do této
podoby:
if (!started) { if (Input.GetMouseButtonDown(0) || Input.GetKeyDown(KeyCode.Space)) { StartGame(); } return; }
Kdyby se někomu nepovedlo poskládat metody do skriptu, níže je pro kontrolu jeho finální podoba:
float flapAmount = 7; float speed = 5; bool started = false; void Start() { } // Update is called once per frame void Update() { if (!started) { if (Input.GetMouseButtonDown(0) || Input.GetKeyDown(KeyCode.Space)) { StartGame(); } return; } Vector3 vel = gameObject.GetComponent<Rigidbody2D>().velocity; if (Input.GetMouseButtonDown(0)) { vel = Flap(vel); } vel.x = speed; gameObject.GetComponent<Rigidbody2D>().velocity = vel; } void StartGame() { started = true; GetComponent<Rigidbody2D>().gravityScale = 1f; Vector3 vel = GetComponent<Rigidbody2D>().velocity; vel = Flap(vel); GetComponent<Rigidbody2D>().velocity = vel; } Vector3 Flap(Vector3 v) { v.y = flapAmount; return v; }
Není tu asi nic, co bychom museli speciálně vysvětlovat. Vše v tomto kódu již v tutoriálu zaznělo, tak se můžeme vrhnout na další část.
Počítání skóre
Nyní začneme s počítáním skóre. Na náš stalag přidáme další
objekt, který bude zjišťovat, zda hráč proletěl. Všem prefabům stalagu
přidáme objekt. Vytvoříme si prázdný gameObject, vynulujeme jeho
souřadnice a pojmenujeme ho například stalagScore
. Tento objekt
vložíme jako podobjekt stalagu. Přidáme mu Box Collider2D.
Collider si zvětšíme po ose Y dostatečně daleko a pokusíme se collider narovnat se spodkem stalagu. Nebo ho také můžeme dát více do středu, co se vám více líbí, na funkcionalitu to vliv mít nebude. Nebojte se to vážně roztáhnout klidně na desetinásobek velikosti stalagu. Mohlo by se stát, že tento collider bude moc malinký kvůli našem generování stalagů v náhodných velikostech a hráč se ho třeba ani nedotkne a nepřičte se mu skóre. Také musíme tento collider nastavit jako trigger:
Dále tomuto objektu přidáme ScoreScript
. Tento skript bude,
jak asi tušíte, přičítat skóre. Pro tyto účely si přidáme Text na
obrazovku jako GameObject -> UI -> Text.
Vytvoří se nám samozřejmě také Canvas, ale s tím již umíme pracovat
z minulých lekcí. Tudíž si ho nastavíme na kameru, aby byl stále pěkně
vidět. Tento text nám bude vypisovat aktuální skóre. Text si umístíme do
pravého horního rohu, zvětšíme si font a text nastavíme například jen na
Score: 0
. Tomuto textu přidáme TextScoreScript
.
Základ máme připraven.
Skripty
Vrhneme se na ony dva skripty.
TextScoreScript
Nejdříve upravíme náš TextScoreScript
:
static int score = 0; void Start () { score = 0; } public static void AddScore() { score++; }
Programátoři určitě tuší, proč před score
máme
static int
. Neprogramátoři asi moc neví. Když máme nějakou statickou
proměnnou nebo metodu, jsme schopní k ní přistupovat i z různých metod a
tříd, aniž bychom ji museli nějak získávat. Taková metoda ovšem ale
může pracovat zas jen se statickými proměnnými ve třídě. Díky tomu, že
metodu máme statickou, jsme schopni udělat další skript jednodušší.
ScoreScript
Když přes náš trigger přeletí hráč, chceme přičíst skóre. To
uděláme zavoláním metody na třídě TextScore
:
void OnTriggerEnter2D(Collider2D col) { if(col.CompareTag("Player")) { print("Adding score"); TextScore.AddScore(); } }
Díky tomu, že v dané třídě máme metodu statickou, jsme schopni k ní přistoupit, aniž bychom dělali něco takového:
GameObject.Find("název objektu").SendMessage...
Nebo ještě hůře takového:
GameObject.Find("název objektu").getComponent...
Hůře proto, že je to zbytečně složité, jak pro nás, tak pro počítač. Představte si scénu, kde máte tisíce objektů a počítač mezi nimi musí najít daný objekt, na objektu najít skript a až potom jsme tam, kde chceme být.
Nyní, když hráč proletí úspěšně kolem stalagu, vnitřně se nám skóre přičte, ale nikde jej nevidíme.
TextScoreScript
Proto se vrátíme zpět do TextScoreScript
, kde zavedeme novou
proměnnou. Pokud hráč zdolá překážku, tuto proměnnou si nastavíme na
true
a změníme text, který ukazuje aktuální body:
static int score = 0; static bool changed = false; void Start () { score = 0; } void Update () { if(changed) { changed = false; gameObject.GetComponent<Text>().text = "Score: " + score; } } public static void AddScore() { score++; changed = true; }
Při aktualizování textu se vždy odkážeme na aktuální objekt, kde se nachází náš text na vypisování skóre, a zároveň náš skript.
Přičítání skóre je hotové a my se můžeme vrhnout na ukládání nejlepšího skóre.
Nejlepší skóre
Přidáme si nový text, který si dáme menší než ten původní, a
pojmenujeme si ho GUIHighScore
. Dáme text pod naše aktuální
skóre. Také mu můžeme přidat text "HighScore: ".
Tomuto textu přidáme nový skript HighScore
. Nejdříve se ale
vrátíme k našemu ScoreScript
.
ScoreScript
Přidáme si do něj jednu metodu:
void OnDestroy() { int highscore = PlayerPrefs.GetInt("highscore", 0); if (score > highscore) PlayerPrefs.SetInt("highscore", score); }
Tato metoda je v Unity již předpřipravená podobně, jako jsou metody
Start()
nebo Update()
. Tato metoda se volá vždy při
zničení objektu nebo při ukončení scény.
PlayerPrefs
Dále tu máme PlayerPrefs
. To je třída s užitečnými
metodami pro ukládání velmi jednoduchých dat.
Načítání
Data hráče načteme takto:
PlayerPrefs.GetInt("highscore", 0);
Načteme hodnotu, která je uložena pod klíčem "highscore"
.
Pokud daná hodnota nebude nalezena, vrátí se nám 0
. Kdybychom
jako druhý parametr zadali například 5
, tak budeme mít v
highscore
hodnotu 5
, pokud se žádné uložené
skóre nenajde.
Ukládání
Metoda SetInt()
dělá přesný opak:
PlayerPrefs.SetInt("highscore", score);
Ukládáme pod klíč "highscore"
číslo, které se nachází v
proměnné score
:
Takže pokud hráč dosáhne skóre většího, než je uložené v
PlayerPrefs
, tak se zapíše do paměti.
Pokud by vás zajímalo, kam se například na Windows tyto hodnoty ukládají, můžete je najít uložené v registrech. Samozřejmě je tam najdete až alespoň po jednom odehrání hry. Pro nezkušené doporučuji se v registrech počítače moc nehrabat, můžete si nepříjemně poškodit soubory a funkčnost aplikací.
Pro účely testování hry si můžeme přidat na první řádku metody
OnDestroy()
vymazání skóre:
PlayerPrefs.DeleteKey("highscore");
To zajistí, abychom zbytečně nenahráli veliké skóre na našem počítači. Až hru budeme mít hotovou, nezapomeňme tuto řádku samozřejmě smazat.
Ještě jedna možnost je ta, že si můžeme přidat podmínku:
If (Application.isEditor)
PlayerPrefs.DeleteKey("highscore");
Takže dokud nebudeme mít hru vydanou a budeme ji spouštět pouze přes Unity, tak se pokaždé vymaže skóre.
HighScore
skript
Nyní se přesuneme do HighScore
skriptu:
void Start () { int highscore = PlayerPrefs.GetInt("highscore", 0); gameObject.GetComponent<Text>().text = "Highscore: " + highscore; }
Takže pokaždé, když hru spustíme, se nám aktualizuje text obsahující highscore na nejvyšší nahrané skóre.
V příští lekci, Unity (C#) Android: Vylepšení pohybu nakláněním, uděláme drobnou opravu sekání a dále vylepšíme pohyb postavy hráče.