V minulém díle jsme se vytvořili základní model hada, kterému ovšem něco chybí. Nemáme ošetřeny situace, kdy had narazí do zdi. V takovém případě by hra měla skončit. Proto si vytvoříme proceduru GameOver, kterou zavoláme, pokud se hra má ukončit. Tato procedura zastaví časovač a do okna napíše Konec hry.
Otevřete si tedy svůj projekt a úplně nakonec před řádek End Class napište kód:
Public Sub GameOver()
Timer1.Enabled = False
g.FillRectangle(New SolidBrush(Color.FromArgb(128, Color.LightBlue)), 0, 0, 420, 420)
Dim f As New Font(System.Drawing.FontFamily.GenericSansSerif, 40, FontStyle.Bold)
g.DrawString("KONEC HRY", f, Brushes.Black, 40, 160)
End Sub
Tuto proceduru spustíme, když hráč udělá chybu. První příkaz není třeba vysvětlovat, prostě jen deaktivujeme časovač, aby hra už neběžela.
Druhý řádek je zajímavý. Funkce FillRectangle vybarví obdélníkovou oblast na grafické ploše zadaným štětcem Brush. Jako štětec předáváme argument New SolidBrush(Color.FromArgb(128, Color.LightBlue)). To způsobí, že se vytvoří nový štětec (SolidBrush - bez efektů) a jako základní barva se mu předá Color.FromArgb(128, Color.LightBlue). To definuje barvu. Argb je označení pro RGB barvu (RGB - Red, Green, Blue - skládání barev na monitoru, každá barva je definována poměrem složek červené, zelené a modré). A - alpha kanál udává průhlednost této barvy. Takže jako základní barvu máme Color.LightBlue, což je jakási světle modrá, a 128 je průhlednost. Průhlednost má hodnoty od 0 do 255, takže jsme průhlednost nastavili přesně na polovinu. A tímto štětcem vybarvíme obdélník, který začíná na pozici 0,0 a má šířku a výšku 420,420. To znamená, že se přebarví celá plocha, má totiž rozměry 420x420. Všechny jednotky jsou v pixelech.
Třetí příkaz vytvoří nový font - základní rodina písma je System.Drawing.FontFamily.GenericSansSerif, což je standardní systémové bezpatkové písmo (Microsoft Sans Serif, ale lepší je třeba Tahoma nebo Verdana), velikost je 40 a styl je FontStyle.Bold, čili tučné.
Čtvrtý příkaz vykreslí text KONEC HRY písmem f, které jsme právě vytvořili, na pozici [40,160].
Ošetření nárazů hada
Pokud had leze, může samozřejmě narazit. Buď narazí do zdi, anebo narazí na políčko, kde se nachází jiný jeho článek. Proto jsme si také zakládali pole Plocha, které má informace o tom, jestli je políčko volné, jestli je na něm bonus, anebo had. Nám tedy stačí zjistit, jestli pozice hlavy (obě dvě) jsou v rozmezí 1 až 10. Pokud jsou menší než jedna nebo větší než 10, pak už je had ve zdi. Pokud je na políčku s novou pozicí hlavy hodnota Had a hlava se tam ještě nezapsala, jednoznačně jsme do hada narazili.
Tento zdrojový kód přidejte do procedury časovače hned před blok, který kreslí novou hlavu hada:
If Had(0).x < 1 Or Had(0).x > 10 Or Had(0).y < 1 Or Had(0).y > 10 Then
GameOver()
PictureBox1.Refresh()
Exit Sub
End If
If Plocha(Had(0).x, Had(0).y) = POLE.Had Then
GameOver()
PictureBox1.Refresh()
Exit Sub
End If
Myslím, že funkce je jasná. Možná se ptáte, proč jsem vytvářel dvě podmínky, když by stačilo vše "nacpat" do jedné. Je to záměrně. Pokud had vyskočí z hrací plochy, nemáme pole Plocha správně nadimenzované - je od nuly do desítky a mohli bychom požadovat položku mimo hranice pole - došlo by k OutOfRangeException. Pokud projdeme první podmínkou, tato chyba nemůže nastat. I tak by se to dalo dát do jedné podmínky, ale museli bychom použít komplikovanější operátor OrElse, který funguje jako klasický Or s tím rozdílem, že pokud první výraz vyhovuje podmínce, druhý už ani nezkouší a vyhodnotí se jako True.
Pokud hru spustíme, vše bude fungovat - pokud had narazí do zdi nebo do sebe, hra skončí. Ještě poznámka - Exit Sub předčasně vyskočí z procedury, kde se právě nacházíme.
Bonusy
Dále potřebujeme proceduru, která vytvoří v hrací ploše bonus. Protože hrací pole má 10x10 políček a had po sežrání jednoho bonusu zvětší svou délku o jeden článek, může mít maximální délku 100 článků. Možná si řeknete, že takhle dlouhý had, aby zaplnil celé pole, se snad nikomu nepovede, ale znám několik expertů, kterým se tohle povedlo i na daleko vyšší rychlost, než máme nastavenou my.
Bonusů bude v hrací ploše vždy 5, pokud bude délka hada větší než 95 políček, další se už vytvářet nebudou - nebylo by je kam dát. Jakmile má had 100 políček, hra končí také. Na začátku hry musíme vygenerovat 5 bonusů, při každém sežrání se nový bonus vytvoří.
Vytvořte tedy novou proceduru Bonus, která náhodně umístí nový bonus:
Public Sub Bonus()
Dim b, x, y As Integer
b = Int(Rnd() * 4)
Do
x = Int(Rnd() * 10) + 1
y = Int(Rnd() * 10) + 1
Loop While Plocha(x, y) <> POLE.Volno
Plocha(x, y) = POLE.Bonus
g.DrawImage(ImageList1.Images(b), x * 35, y * 35)
End Sub
Na prvním řádku vygeneruje náhodné číslo od 0 do 3, které určí, jaký obrázek se má použít. V cyklu Do generujeme čísla X a Y od 1 do 10 a opakujeme, dokud neukazují na prázdné políčko. Pak již jen vykreslíme bonus a na políčko napíšeme, že je tam bonus.
A jak funguje generování náhodných čísel? Funkce Rnd() vrací desetinné číslo z intervalu <0;1), to znamená, že jsou to různá desetinná čísla menší než jedna (včetně nuly, ale jednička tam už není). Pokud tuto hodnotu tedy vynásobíme čtyřmi, dostaneme interval <0;4). Funkce Int zaokrouhluje dolů, to znamená, že i nejvyšší možná hodnota z tohoto intervalu se zaokrouhlí na 3. Tím pádem dostaneme číslo od 0 do 3.
Pokud potřebujeme čísla od 1 do 10, stačí takto vygenerovat čísla od 0 do 9 a přičíst k nim jedničku. Takot se generuje X a Y.
Poznámka: Náhodná čísla se dají generovat i pomocí objektu Random, funkce Rnd je přežitek již z původního jazyka BASIC, ale funkčnost je stejná.
Žraní bonusů
Nyní musíme zabezpečit, aby had mohl žrát bonusy. Proto do procedury Form1_Load, která se spustí hned po startu programu, přidejte na konec zdrojový kód:
Randomize()
Bonus() : Bonus() : Bonus() : Bonus() : Bonus()
Funkce Randomize nastaví generátor náhodných čísel. Čísla ve skutečnosti náhodná úplně nejsou, jsou založeny na statisticke pohybů myši, stisku kláves, datumu, času a dalších faktorech, které náhodné nejsou. Pokud si čísel necháme vypsat víc, dá se každé další uhodnout. Ale pro účely hry to bohatě stačí. Pak se spustí pětkrát funkce, která generuje bonus, takže se na hrací ploše objeví 5 bonusů.
Do procedury časovače přidejte tento blok kódu, hned za kontrolu nárazů.
If Plocha(Had(0).x, Had(0).y) = POLE.Bonus Then
g.DrawImage(ImageList1.Images(5), Had(Delka).x * 35, Had(Delka).y * 35)
Plocha(Had(Delka).x, Had(Delka).y) = POLE.Had
Delka = Delka + 1
If Delka < 96 Then Bonus()
Me.Text = "Hungry Snake - Skóre: " & Delka
End If
Pokud hlava hada narazí na bonus, spustí se příkazy. V minulém díle jsem se zmínil o tom, že položka Had(0) obsahuje informace o hlavě a položka Had(Delka - 1) obsahuje poslední políčko. Abychom si zjednodušili práci, při posouvání těla hada si pozici starého posledního článku uchováváme na položce Had(Delka). To se nám teď velmi dobře hodí. Na tuto pozici vykreslíme článek, na políčko napíšeme, že je tam had a délku zvětšíme o jednu. Tím pádem se nám položka, kterou jsme používali, bude chovat jako poslední článek - je to Had(Delka - 1). Pokud je had kratší než 96 článků, přidáme nový bonus a každopádně do titulkového pruhu okna napíšeme, jaké máme skóre. To je celé.
Tím bychom s hadem jednou provždy skoncovali, pokud byste měli ještě nějaký nápad, jak článek rozšířit nebo případně doplnit vysvětlení věcí, které vám nejsou úplně jasné, určitě se ozvěte do diskuse.
(ve zdrojových kódech najdete i návod, jak udělat automatické ovládání hada tak, aby snědl všechny bonusy a zaplnil hrací pole)
Jsem hlavním softwarovým architektem ve společnosti Riganti. Mám dlouholeté zkušenosti s technologiemi ASP.NET, Silverlight, WPF a XNA. Působím též jako lektor ve společnosti Gopas a již třetím rokem jsem držitelem ocenění Microsoft Most Valuable Professional.