3# Textová adventura

Tentokrát již napíšeme publikovatelnou hru, kterou si můžete vyzkoušet na konci příspěvku. Samozřejmě neočekávejte nic zázračného. Jedná se o něco velmi základního jen pro vysvětlení “jak na to”. Jsem si jistý, že vaše první hra bude rozhodně zajímavější a vtipnější než ta moje.

První čím začneme je vytvoření nového projektu. Můžeme si ho nazvat TextAdventure. Opět budeme pracovat ve 2D. Vytvoříme pouze jednu obrazovku, ve které se bude postupně měnit text který obsahuje a hráč bude moci dělat rozhodnutí 1-9 (víc kláves by bylo náročné použít). Jedna obrazovka může obsahovat například něco jako:

“Nacházíš se v chodbě hradu, před sebou máš vitrínu s historickými zbraněmi. Vedle vitríny jsou schody nahoru a chodba pokračuje doprava a doleva.”

  1. Prohlédnout si vitrínu
  2. Jít po schodech nahoru
  3. Pokračovat chodbou doprava
  4. Pokračovat chodbou doleva

Na to budeme očekávat, že hráč zmáčkne tlačítko 1-4 a podle toho se nám změní obrazovka na další část příběhu.

Na začátek začneme úpravou barvy pozadí. Naše výchozí barva všech projektů je modrá a tato barva je definovaná hlavní kameře. Tam ji také můžeme změnit. Rozbalíme si “Sample Scene” a vybere “Main Camera”. Na pravé straně v Inspektorovi je nastavení Background.

Samozřejmě barvu můžeme změnit na jako chceme podle tématu hry, o které se snažíme

Nyní můžeme začít vytvářet vizuální elementy hry. Pár základních tvarů můžeme vytvářet rovnou v unity, komplikovanější tvary a barevné přechody budeme vkládat samostatně jaké PNG soubory do Assets. Ale k tomu se dostaneme až v další lekci. Na začátek vytvoříme čtverec/obdélník. Klikneme pravým tlačítkem do Assets, vybereme “Create” -> “Sprites” -> “Square”.

Aby se nám objevil ve hře musíme ho zařadit do scény. Stačí ho přetáhnout z Assets do Hierarchy případě do scény. Toto vytvoří kopii v naší scéně. Schválně říkám kopii, protože jeden asset můžeme do hry dát kolikrát chceme a pak každý můžeme nezávisle upravovat.

Nyní můžeme naše čtverce začít upravovat. Nad hierarchy máme základní panel nástrojů. Ručička, neboli hand tool, pomocí kterého se posouváme po scéně. Move tool, kterým posouváme objekty po scéně.  Rotate a scale tool upravuje otočení a velikost objektů. Rect tool upravuje tvar objektů, kdy můžeme například z čtverce udělat obdelník, Move, rotate and scale selected objects si předpokládám domyslíte a poslední jsou Custom tool, které zatím nemáme. Pro naše první cvičení můžeme naše čtverce přesunout tak aby jsme je všechny viděli a nebyly všechny na středu scény přes sebe. Zároveň můžeme upravit jejich barvu.To opět najde v Inspektor jednotlivých čtverců. Výsledek u mě vypadá nějak takto, u vás může vypadat úplně dost jinak protože barvy a posunutí jste si mohli udělat jak chcete.

Tento náhled je ze záložky “Game”, kde není vidět kamera a barva pozadí odpovídá té co jste si v kameře vybrali. Jedna drobná rada. Pokud by se vám nedařilo nějaký objekt najít, můžete na něj udělat dvojklik uvnitř Hierarchy a automaticky se vám vybere a upraví se pohled na scénu tak, aby byl uprostřed scény dostatečně velký. Předpokládám, že základní úpravy velikosti a tvaru pro vás nebudou problém a tak si všechny naše čtverce odstraníme ze scény a začneme tvořit přímo náš herní layout. Čtverce si můžete s Shift nebo CTRL označit a pomocí Delete smazat, protože základem pro naši hru bude “Canvas”. Ten vložíme tak, že klikneme do Hierarchy pravým tlačítkem -> “UI” -> “Canvas”.

 

Po vložení se nám na scénu přidají na první pohled jen dvě čáry směřující na naši kameru uprostřed scény. To je ve skutečnosti, levý dolní roh našeho canvasu. Canvas pro nás od teď bude fungovat stejně jako Main Camera a nahradí její funkci. Doporučuji dvojklik na “Canvas” Hierarchy, aby se nám upravil náhled na celý Canvas.

Ano, ten malý obdélník v levém dolním rohu je naše kamera. Ta nás však již nemusí zajímat. Canvas je nyní naše Camera a vše co je uvnitř Canvas bude zobrazeno hráči. Neznamená to, že můžeme Cameru smazat! Samotný canvas nyní upravuje svou velikost podle velikosti obrazovky. To si můžeme vyzkoušet. Naše náhledové okno “Game” Je aktuálně naše obrazovka a můžeme si upravit zobrazení tak, aby jsme viděli Scene a Game najednou. Když máte otevřenou scénu, chytnete nápis “Game” a přesunete jej do pravé strany “Scene”. Okno se automaticky připne s připnutím se upraví i náš canvas na tvar okna, které nám vzniklo.

Toto se bude samozřejmě hodit, když budete chtít dělat aplikace optimalizované pro různé tvary obrazovky. To by ale pro nás na začátek bylo příliš náročné a tak si nastavíme statickou velikost náhledového obrazu. Na záložce “Game” vidíme “Free Aspect”, v něm vybereme úplně dole malé + a nastavíme velikost na 1920 a 1080. Nyní je jedno jak máme nastaveno naše okno, vždy budeme naše hra ve stejném poměru stran.

Na náš prázdný canvas teď můžeme přidat text. V Hierarchy stačí kliknout pravým tlačítkem na “Canvas”, vyberete UI a Text. Ten se ze začátku objeví dost miniaturní, ale v Hierarchii by se vám měl objevit pod Canvasem.

Pokud máte na text kliknuto tak si můžete na pravé straně v inspektoru změnit barvu textu (Já si určitě vyberu bílou, protože mám černé pozadí kamery), velikost písma a jeho zarovnání. Samotný text, který je v poli napsaný můžete upravit v políčku text,  kde je aktuálně předepsaný nápis “New text”. Brzy budeme toto pole plnit pomocí scriptu, ale zatím ho můžeme upravit ručně na něco pro účely testování.

 

Také můžete text přejmenovat jako já na “Text Adventury” pro lepší orientaci.

Můžeme textu také přidat nějaké pozadí, aby jsme neměli “jen” černé pozadí a bílí text. Uděláme to tak, že do “Canvas” v hierarchy přidáme UI -> Image. Přidá se nám malý bílí čtverec. ten můžeme zvětšit přes celý text a vytvoříme tím zatím “překrytí textu”. To se nám stane kvůli pořadí v jakém renderujeme scénu. Ta vzniká podle hierarchie od shora dolů a tak se nám napřed vytvoří text a přes něj se nám vytvoří “obrázek”. U toho samozřejmě změní barvu, proto i kdyby jsme změnili pořadí renderování, stále nic neuvidíme, protože bílí text na bílém pozadí. Já jsem si vybral tmavě šedou jen aby text byl nějak ohraničený.

Prakticky stejným způsobem můžete vytvořit do horní části nadpis opět s nějakým pozadím pro naši hru. Výsledek může vypadat třeba takto:

Zatím to moc nedělá, jen tak si to existuje. Takže teď si napíšeme první script, kterým nahradíme text uvnitř pole “Text Adventury” a rovnou ho napíšeme tak, aby bylo snadné vytvářet další texty a přechod mezi nimi. K tomu se dostaneme, ale postupně, napřed vytvoříme prázdný herní objekt. Minu jsme náš script dávali přímo na kameru. To se většinou nedělá a místo toho se vytváří prázdný objekt, na který se pověsí scipty. Takže začneme kliknutím pravým do Hierarchy a vybereme “Create empty”. V základu se bude jmenovat GameObject a můžeme ho přejmenovat na co chceme. S ohledem na to, že jej používám pouze pouze na start herního scriptu, pojmenuju si ho “GameScriptObject”. Nyní již vytvoříme konečně script a začneme zase programovat. Pravé tlačítko do assets, vybereme Create -> C#Script. Rovnou musíte mu dát nové jméno. Přejmenování musíte pak dělat ještě ručně uvnitř scriptu a je to zbytečná práce navíc. Script si otevřeme a můžeme začít psát.

Před void Start() si založíme proměnou Text se jménem textComponent; (řádek 8.). Text je speciální typ proměnné a umožňuje uložení informací o textových polích v unity. Aby jsme ji mohli používat musíme připojit knihovnu. Můžete si to představit jako, když v HTML chceme používat menu z bootstrap musíme připojit 3 soubory, aby bootstrap fungovat. Zde stačí připojit Unity knihovnu pro UI. Ty jsou definovány na začátku (u mě na řádku 4.). Pokud nemáte UI knihovnu připojenou zůstane Text podtržený červeně.

Aktuálně je naše proměnná založena, ale aby jsme do ní mohli něco uložit musíme s tím pracovat uvnitř Visual Studia. Pokud chcete mít proměnou dostupnou z Unity musíme z toho udělat takzvané serializované pole. To se nám ukáže i v inspektoru v Unity. Jak udělat z naší proměnné serializované pole? Staří před proměnnou napsat [SerializeField] s hranatými závorkami.

Uložit a přepnout do Unity. Tam musíme náš script napojit na “GameScriptObject” tím, že do něj náš script přetáhneme. Po kliknutí na GameScriptObject by jste v inspektoru měli vidět náš AdventureGameScript a u něj Text Component.

Napravo od našeho “None (Text)” máme malé kolečko s tečkou. Pokud na něj kliknete Unity vám nabídne co můžete na tuto proměnou napojit a to konkrétně jaké textové pole chcete na tuto proměnou napojit.

Vybereme dvojklikem a přepneme se zpět do Visual Studia, kde musíme s proměnnou začít pracovat. Budeme psát do naší funkce Start() a první začneme pracovat s naší proměnou textComponent a její částí “text”. Zde se jedná o ten text, který je obsah našeho Text Adventury, který jsme přiřadily do scriptu v unity. Takže to co přiřadíme do této proměnné se objeví hráči v tom velkém poli.

Uložit, přepnout do unity, kliknout na play a měl by se nám změnit náš hlavní text na to co jsme přiřadily do proměnné.

Teď nás čeká to asi nejnáročnější. V unity vytvoříme třídu (ScriptableObject), pomocí kterého budeme vytvářet jednotlivé části našeho příběhu jako soubory (assets) v unity, aniž by jsme museli vše psát ve Visual Studiu. Lépe řečeno, když vytvoříte toto jádro, již budete moci dát projekt někomu dalšímu kdo jen bude kopírovat text a přiřazovat kam na který stav příběhu odkazuje které číslo. Takže začneme tím, že vy tvoříme druhý C# Script což už jistě umíte a dáme mu jméno State neboli stav. Pomocí toho budeme vytvářet jednotlivé stavy hry (obrazovky s textem). Nezapomeňte soubor přejmenovat před tím než kliknete jinam.

Když si náš script otevřeme ve Visual Studiu musíme hned jako první upravit řádek 5. Náš stav totiž nebude MonoBehavior (velmi zjednodušeně funkce) ale bude to ScriptableObject. To znamená, že jej budeme používat jako vzor pro naše části příběhu, které budeme vytvářet v Unity. Uvnitř Scriptable object vše smažeme.

Vytvoříme si opět serializované pole, aby jsme mohli jeho obsah upravovat přímo v unity, ale tentokrát typu string. String je řetězec znaků (slovo, věta, odstavec textu nebo klidně víc odstavců). Díky tomu budeme moci v Unity do této proměnné přímo psát v inspektoru. Pokud by jsme to nechali takto, tak budeme mít jako náhled jen jeden řádek, do kterého budeme psát. To samozřejmě není dost na náš příběh, takže ještě před [SerializedField] přidáme, že se bude jednat o TextArea. Za tím následují dvě čísla. První je výchozí počet řádku, druhé je počet řádků pokud se ten první naplní. Pokud by se vám povedlo naplnit i ten druhý počet řádků objeví se vám scroll bar.

Výborně. Již předtím jsem zmínil, že toto budeme používat jako vzor pro naše stavy naší hry, aby jsme mohli stavy vytvářet musíme přidat kus kódu, který unity bude chápat jako “Přidej do pravé tlačítko – > create položku STAV, který vytvoří soubor podle tohoto vzoru.”. To budeme psát před public class. Budeme vytvářet položku menu pro Assets se jménem State. Pokud zvládnete předchozí vetu přeložit do angličtiny tak vám ten řádek bude dávat naprostý smysl.

Uložit, přepnout do unity. Pravé tlačítko myši do Assets -> Create a hned první položka by měl být “State”. Pojmenujte si “StartState” a po kliknutí na něj by jste měli v inspektoru vidět velké pole s názvem StoryText. Do pole si můžete napsat první část vašeho příběhu a budeme pokračovat dál, protože zatím je váš příběh uložený na na místě odkud ho nijak nepoužíváte.

Napřed si musíme ujasnit pár věcí ohledně funkcí. Do teď jsme používali pouze funkce, které mají před sebou void. To znamená, ta funkce nic nevrací, ale pouze něco udělá. Takže můžeme mít funkci co nám změní barvu pozadí a když ji zavoláme jen nám změní barvu pozadí. Nic nevrací takže Void. Můžeme ale mít funkci, které dáme dvě čísla a ona je sečte. Tím pádem něco vrací (Return). Funkci, která něco vrací budeme teď potřebovat, protože bez funkce, která nám bude měnit jednotlivé části (state) příběhu se dál nedostaneme. Poslední část je dostupnost funkce. Každá funkce pokud není jinak určeno je “Private” a je dostupná pouze třídě (class), ve které je vytvořena. Můžeme ji ale udělat “Public” a pak ji můžeme volat z dalších tříd.

Pokračujeme ve stejném souboru jako jsme skončili (State.cs) a budeme psát pod vytvoření textového pole. Vytvoříme zde veřejnou funkce, která bude každému vracet to co máme aktuálně napsané v našem textovém poli.

Nyní máme způsob jak získat text musíme ho ale někde použít. Takže se vrátíme do AdventureGameScript a budeme doplňovat. První věc co by jsme měli definovat je začátek, co se má hráči ukázat jako první obrazovka. Takže si vytvoříme druhé Serialized field po našem testovém. To bude typu State se jménem startingState. Nyní můžeme definovat v Unity co je počáteční stav hry.

Pokud by jste hledali, kde se definuje začínající stav, kliknete v Unity na GameScriptObject a pod Text Component by jsme měli mít “Starting State”.

Aktuálně můžeme říct, co je počáteční stav hry, ale nemáme způsob jak si průběžně ukládat v jakém stavu (části) hry se nacházíme. Proto musíme přidat proměnou, která bude ukládat stav (State) a bude se jmenovat stateNow. Definujeme ji pod naše pole na začínající stav.

Když hra začne (void Start()) musíme říct, že jsme na začátku hry (uložit do proměnné stateNow) a musíme načíst text stánky na které aktuálně jsme pomocí funkce GetStateStory(), kterou jsme vytvořili před chvílí.

Uložit, přepnout do Unity a zapnout hru. Náš předepsaný text by se měl změnit na to co jste napsali do objektu startingState. Pokud se nic nestane, ověřte si, že jste přiřadili v GameObjectScript něco do Starting State v inspektoru. Mě se na to jednou povedlo zapomenout a chybu jsem pak hledal velmi dlouho.

Nyní se dostáváme do poslední části programování. Musíme najít způsob jak ukládat jaké jsou další možnosti jednotlivých stavů a kam jednotlivé možnosti vedou. Na uložení více možností/hodnot můžeme použít array což je řada čísel/textů/stavů co budeme chtít. Jednotlivé části jsou číslovány a začíná se od 0. Takže když si založíme proměnou na čísla (int) můžeme do ni uložit čísla 5, 10, 15, 20, 25. Pokud budeme chtít druhé číslo (10) chceme číslo z pozice 1, protože 5 je na pozici 0. Většina programovacích jazyků počítá od nuly a je potřeba to brát v potaz. Pokud by jsme takovou Array chtěli vytvořit bude zápis vypadat int[] nasobkyPeti = {5,10,15,20,25}. A pokud by jsme chtěli volat opět tu desítku bude zápis vypadat například nasobkyPeti[1]. Něco podobného budeme používat v našem scriptu pro jednotlivé rozhodnutí stavů. samozřejmě místo “int” bude “state” a nebudeme rovnou do něj zapisovat jednotlivé stavy, ale ty budeme pro jednotlivé stavy definovat v Unity pomocí serializovaných polí. Takže přepneme se do state.cs a začneme upravovat.

Pod TextArea přidáme další serializované pole. Bude typu State, ale za slovo State přidáme hranaté závorky. Tím definujeme, že se nejedná o jeden stav ale skupinu (array) stavů. Pojmenuje si to nextStates.

Můžeme si ověřit, že vše funguje. Uložte, přepněte se do unity. Klikněte na objekt StartingState v assets. Pod příběhem se nám objevil nápis “Next States” s šipkou. Po kliknutí na ni se rozbalí nabídka ve které můžete nastavit kolik je dalších stavu a pak přiřazovat jaké jsou další stavy (opět kliknutím na kolečko s tečkou a pak dvojklik na další stav). Pro tyto účely doporučuji vytvořit alespoň pár dalších stavů (pravé tlačítko do assets -> create -> state. Ty přiřaďte k dalším stavům.

Nyní sice máme uloženo jaké jsou další stavy, ale nemáme možnost jak s nimi pracovat. Takže si musíme vytvořit novou veřejnou (public) funkci, která bude vracet (return) array dalších stavů a dáme jí jméno GetNextStates.

Když máme vytvořenou veřejnou metodu (funkci) můžeme pokračovat dál. Musíme ji někde použít, tak se přepneme do AdventureGameScript a tentokrát budeme psát do update. Budeme totiž čekat, kdy hráč zmáčkne nějakou klávesu, aby jsme mohli podle toho změnit stav. Update se nám spustí každý snímek a pokaždé bude kontrolovat jestli hráč nestiskl nějakou klávesu. Takže do update si předepíšeme funkci updateState a tu si pod to vytvoříme. Je to hlavně do budoucna, kdy není vhodné vše psát přímo do Update. Bylo by toho tam časem hodně a velmi špatně by se v tom orientovalo.

Slovíčko Private nemusíte akutně psát, protože pokud není řečeno jinak je funkce private, ale pro orientaci je lepší psát private. Uvnitř funkce si založíme proměnou nextStates, do které si uložíme další stavy našeho aktuálního stavu (stateNow). Proměnou můžeme definovat jako State[] případně jako var. Var je libovolná proměnná, která se definuje podle obsahu.

Nyní se můžeme trochu vrátit k předchozí lekci, protože teď budeme potřebovat již input od hráče. Opět to bude zabaleno v podmínce IF hráč zmáčkne tlačítko stane se něco. To něco bude, že se stateNow změní na další stav podle stisknutého tlačítka. Takže pokud stiknul 1 stane se novým stavem to co máme v Array jako 0 (nextState[0]).

Samozřejmě hráč nemá jen jednu možnost co udělat, ale má jich víc. Na numerické klávesnice máme čísla od 0-9 (Keypad0-Keypad9), nulu používat nebudeme protože by hráče zbytečně mátla a tak maximální počet dalších stavů je 9. Mohli by jsme jen udělat 8 dalších kopií a upravit Keypad1 na Keypad2, nextStates[0] na nextState[1] a pokračovat ještě 7x. NEBO můžeme vytvořit smyčku. Ta bude dynamicky upravovat počet aktivních kláves podle toho kolik máme dalších stavů a nebudeme muset dělat 9x to samé. Smyčky se používají velmi často a mohou mít předem daný počet opakování nebo se počet opakování mění na základě proměnné, můžeme kontrolovat podmínky před provedením smyčky nebo až po prvním provedením smyčky a tak dále. Možností je hodně. Pro náš případ použijeme for smyčku. Definice smyčky má 3 části “for (část 1; část 2; část 3)”. První část se spustí vždy a používá se na nějakou počáteční definici proměnné podle, podle které budete počítat kolikrát již smyčka proběhla. Druhá část určuje jak dlouho má smyčka běže. Například dokud proměnná z části 1 nebude 100 a třetí část říká co se stane na konci každého cyklu smyčky. Můžeme například přičíst 1 k té počáteční proměnné a podle toho poznáme, že smyčka již jednou proběhla a díky části dva nám udělá přesně 100 cyklů.

Pro nás potřebujeme si nastavit proměnnou (index), které na začátku bude nula a budeme k ní přičítat 1 tak dlouho, dokud bude menší než počet nových stavů v array nextStates[]. Pokud chcete použít délku array stačí za její jméno napsat Length a pokud chcete proměnou zvětšit o 1 nemusíte psát index = index + 1; Ale můžete použít zkrácený zápis index++; Na konec naší updateState ještě doplníme řádek s výpisem textu pro hráče a máme hotovo.

Nyní máme již všechno programování za sebou. A již je jen na vás jak uděláte příběh hry. Jestli řešíte jak například udělat Game Over? Stačí vytvořit state, kde hráči popíšete jak prohrál a dáte mu možnost začít znovu:

“Stojíte v půdě starého domu vidíte na druhé straně místnosti blyštivý předmět a vpravo od vás jsou schody dolů”

  1. Dojít k blyštivému předmětu
  2. Jít ze schodů dolů

*Hráč zmáčkne 1* -> měníte state na “fallGO”

“Když jsi se snažil přejít přes půdu staré trámy to nevydržely a ty jsi se propadl dolů. Tvá cesta zde končí”

  1. Opakovat

*Hráč zmáčkne 1* -> Návrat na úvodní obrazovku hry

Poslední část bude již jen změna písma. Zatím používáme výhradně výchozí font a minimálně náš nadpis by si zasloužil lepší písmo. Vyberte si nějaký font na https://www.ceskefonty.cz kde jsou fonty s podporou české diakritiky. Když font stáhnete, musíte ho rozbalit a hledáte soubory ve formátu ttf případně otf. V unity si otevřeme záložku Window a tam najdeme package manager. U starších verzí unity nebyl doplněk TextMeshPro rovnou nainstalovaný a může se stát, že ho ani v novější verzi mít nebudete. V seznamu si najděte TextMesh Pro a pokud u něj máte malou fajfku nemusíte nic dělat. Pokud tam není tak na něj musíte kliknout a pak vybrat Install v pravém dolním rohu.

Text mesh pro nám dovolí vytvářet skiny na naše písmo podle fontů, které mu dáme. Znovu klikneme na Window a vybereme TextMeshPro -> Font Asset Creator. Vyskočí nám dvě okna. V jedno je pouze jedno aktivní tlačítko “Import TMP Essentials”, na to klikneme a po chvilce čekání nám dovolí kliknout i na druhé “Import TMP Examples & Extras”. Na to klikneme také, znovu počkáme a pak můžeme TMP importer zavřít. Zůstane nám okno Font Asset Creator ve kterém již můžeme vytvářet naše Font Atlas (skiny pro písmo). Jediné co musíme udělat je přetáhnout náš font do Assets a z Assets ho můžeme znovu uchopit a přetáhnout do řádku “Source Font File” případně ho vybrat přes kolečko vedle pole. Poté klikneme na Generate Font Atlas.

Po kliknutí na Generate Font Atlas klikneme na Save a v novém okně na uložit. V našich Assets by se nám měla objevit modrá ikona Font atlas a to již můžeme používat pro naše písmo.

To co jsme původně pojmenovali Nadpis v naší hře a já jsem zatím pro to používal zástupný nápis *Jméno hry* musíme odstranit (označíme v hierarchy a klikneme na delete) a místo toho vložíme Text – TextMeshPro (Pravé tlačítko na Canvas v hierarchy -> UI -> Text – TextMeshPro). V základu se nám vloží na prostředek stránky, tak upravíme velikost a místo pomocí nástrojů nad hierarchy. Nezapomeňte se přepnout z Game do scene jinak vám to nepůjde upravovat. Pokud máte na Text (TMP) kliknuto v inspektoru na pravé straně vidíte Font Asset, kde můžete snadno vybrat váš vlastní font. Také máte pak možnost pracovat s Glow a Lighting což vašemu písmu dodá trochu “šmrnc”. Obsah hry je už na vás. Hodně štěstí. 

Démoni z hlubin – Gamebook

 

 

0 Comments

Submit a Comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Share This