11. diel - 3D strieľačka v Unity - Prebíjanie
V minulej lekcii, 3D strieľačka v Unity - Vloženie modelu nepriateľa do scény , sme si importovali model nepriateľa a vložili ju do scény.
V dnešnej lekcii sa vrhneme na zvyšok vecí, ktoré potrebujeme k prebíjaniu a zobrazenie počtu nábojov hráčmi. Podobným spôsobom, ako sme robili mieridlá, si urobíme aj značenie nábojov. Až na to, že tentoraz pridáme na naše plátno text s aktuálnym počtom nábojov v zbrani.
Ukazovateľ nábojov
Klikneme na záložku Scene a pozrieme sa do záložky
Hierarchy. Tu máme náš Canvas
, ktorý obsahuje
crosshair
. Klikneme pravým tlačidlom myši na objekt
Canvas
, zvolíme možnosť UI a klikneme na
Image. Na obrazovke, v záložke Game alebo
Scene, sa nám objaví štvorec.
Premenovanie obrázka pre náboj
Najskôr zo všetkého si musíme náš obrázok premenovať na niečo iné
ako Image
, inak by sa nám to plietlo a to je zbytočné. V
záložke Inspector klikneme do bieleho obdĺžnika, kde je
napísané Image
, a vpíšeme iný názov, napr.
nabojeObr
.
Teraz klikneme na kruh s bodkou uprostred a nápisom Source
Image, tento obdĺžnik je súčasťou
komponenty s názvom Image
. Otvorí sa nám
okienko, kde do vyhľadávacieho poľa vpíšeme názov obrázku, ktorý
hľadáme, tým je assault_rifle_01_icon
. Potom na vyhľadaný
obrázok dvakrát klikneme:
Zmena pozície obrázka pre náboj
Následne si nastavíme v komponente Rect Transform
hodnoty tak,
aby sa obrázok zbrane nachádzal v ľavom dolnom rohu. Ja zadal hodnoty pre
x, y, z -280
, -191
a 0
.
Teraz klikneme na štvorec v záložke Inspector s nápisom
centier a postranným middle. Po kliknutí sa
nám zobrazí okno s týmito sieťami, rovnakými ako sú na obrázku:
Tieto siete majú vždy jednu bodku, jeden bod, ktorý znázorňuje, kam sa bude
náš UI prvok zarovnávať. Celé okno so sieťami je
rozdelené na stĺpce a riadky. My ľavým kliknutím na sieť zvolíme stĺpec
left a riadok bottom. Ak sa vám páči iné
zarovnanie, môžete si ho zvoliť.
Všimnite si, že sa nám zmenili súradnice x, y, z, ale obrázok zostal na mieste. To preto, že sme nastavili, aby sa obrázok zarovnával do ľavého dolného rohu. Tieto súradnice sa teda teraz vzťahujú k ľavému dolnému rohu.
Tvorba textu značiaceho počet nábojov
Teraz si vytvoríme samotný text, ktorý nám bude ukazovať, koľko máme
nábojov. Klikneme pravým tlačidlom na objekt Canvas
a vyberieme
možnosť UI a potom Text. Vyberte možnosť
Text a nie možnosť TextMeshPro.
TextMeshPro je pre naše účely až moc detailne zameraný na
úpravu textu, ktorú teraz nepotrebujeme.
Klikneme na náš novo vytvorený Text a premenujeme si ho,
ja som zvolil názov naboje
. Teraz klikneme na obdĺžnik s názvom
Tag, hneď pod názvom objektu, čím sa rozroluje zoznam
všetkých tagov na scéne. My zvolíme
možnosť Add Tag. Klikneme na tlačidlo so znamienkom plus,
ktoré sa nám objavilo, a napíšeme naboje
:
Zmeňme pozíciu nášho textu tak, aby sa nachádzal vľavo od nášho obrázku
zbrane. Moje súradnice boli: -318
, -194
,
0
(Ešte je nastavené zarovnanie na stred). Už ste si mohli
všimnúť, že objekt naboje
má komponentu s názvom
Text
a tento komponent má vlastnosť Text
: .<>
Do tejto vlastnosti budeme vpisovať náš text s počtom nábojov. Do
príslušnej kolónky napíšeme 30/30
a veľkosť fontu si
nastavte, aká sa vám osobne páči, ja zvolil 25
. Len
pozor, pri veľkosti fontu 28
a viac sa vám text už
nezobrazí! Font je potom totiž väčší ako samotné "okienko", do
ktorého text vykresľujú. Riešením je zväčšiť hodnoty Width
a Height
, ktoré sa nachádza priamo pod súradnicami objektu v
komponente Rect Transform
.
Teraz si ešte nastavíme zarovnanie. Klikneme ľavým tlačidlom na štvorec
nachádzajúce sa v komponente Rect Transform
. Tento štvorec má v
sebe obrázok siete. Po rozkliknutí zvolíme stĺpec left a
riadok bottom.
Nastavenie škálovanie obrázkov a textov
Klikneme na náš Canvas
a v záložke
Inspector sa zobrazí komponent Canvas
Scaler
. Tento komponent má dôležitú vlastnosť UI Scale
Mode, ktorú potrebujeme nastaviť. Klikneme na tento obdĺžnik a
zvolíme možnosť Scale With Screen Size. Všetko ponecháme
tak, ako je, okrem položky Match. Ja som zvolil hodnotu
0
, čo má za následok, že sa bude náš Canvas
zväčšovať len podľa veľkosti šírky displeja:
Úprava skriptu shoot
Upravíme si skript na streľbu. Ako vždy si prvýkrát uvedieme celý kód, zmeny si vysvetlíme pod ním:
using System.Collections; using System.Collections.Generic; using Unity.Collections.LowLevel.Unsafe; using UnityEditor; using UnityEngine; using UnityEngine.Networking; using UnityEngine.UI; public class shoot : MonoBehaviour { RaycastHit hitInfo; Animator objectwithAnim; // hráč public float dostrel; public float sila; // udělovaná síla Rigidbody public float poskozeni; public GameObject efekttrefy; // impact Image crosshair; ParticleSystem effekt; // výstřel Text nabojeText; public int naboje; int maxnaboje; bool reloading; void Start() { objectwithAnim = GameObject.FindGameObjectWithTag("Animobject").GetComponent<Animator>(); crosshair = GameObject.FindGameObjectWithTag("crosshair").GetComponent<Image>(); effekt = GameObject.FindGameObjectWithTag("effekt").GetComponent<ParticleSystem>(); nabojeText = GameObject.FindGameObjectWithTag("naboje").GetComponent<Text>(); maxnaboje = naboje; nabojeText.text = naboje.ToString() + "/" + maxnaboje.ToString(); } // Update is called once per frame void Update() { if (Input.GetMouseButtonDown(0) && !objectwithAnim.GetBool("Run") && !objectwithAnim.GetBool("Holster") && !objectwithAnim.GetCurrentAnimatorStateInfo(0).IsName("Inspect") && naboje > 0&&!reloading) // pokud stiskneme levé tlačítko a neběžíme { InvokeRepeating("Shoot", 0, 0.25f); // opakuje metodu Shoot(), začíná okamžitě bez prodlevy, opakuje se každých 0.25 sekundy } if (objectwithAnim.GetBool("Aim")|| objectwithAnim.GetBool("Holster")) { crosshair.enabled = false; // změní hodnotu na false } else { crosshair.enabled = true; } if (naboje <= 0|| reloading) { CancelInvoke("Shoot"); objectwithAnim.SetBool("Shoot", false); } if (Input.GetMouseButtonUp(0)) { CancelInvoke("Shoot");// přestává se volat metoda Shoot() objectwithAnim.SetBool("Shoot", false); } if (effekt.isPlaying && effekt.time >= 0.15f) { effekt.Stop(); } if (objectwithAnim.GetCurrentAnimatorStateInfo(0).IsName("Reload Ammo Left") || objectwithAnim.GetCurrentAnimatorStateInfo(0).IsName("Reload Out Of Ammo")) { CancelInvoke("Shoot"); } if (Input.GetKeyDown(KeyCode.R)) { StartCoroutine(Reload()); } } void Shoot() // námi nově vytvořená metoda { effekt.Stop(); effekt.Play(); transform.GetComponent<AudioSource>().Stop(); // kdyby byl spuštěn ještě předchozí výstřel, tak se zastaví, aby nedošlo k tomu, že uslyšíme 15 výstřelů najednou transform.GetComponent<AudioSource>().Play(); // spustí zvuk, který už je naboje -= 1; nabojeText.text = naboje.ToString() +"/"+maxnaboje.ToString(); if (Physics.Raycast(Camera.main.transform.position, Camera.main.transform.forward, out hitInfo, dostrel)) // pokud něco trefíme, parametry jsou: místo, odkud půjde polopřímka, směr, kam má ukládat informace o tom, co se zasáhlo, dálka, kam až povede { GameObject trefa = Instantiate(efekttrefy, hitInfo.point, Quaternion.LookRotation(hitInfo.normal)); Destroy(trefa, 1); if (hitInfo.transform.GetComponent<Rigidbody>()) // pokud objekt, který jsme trefili, má komponentu Rigidbody { hitInfo.transform.GetComponent<Rigidbody>().AddForce(Camera.main.transform.forward * sila); // přidáme sílu ve směru, kterým se díváme } if (hitInfo.transform.GetComponent<EnemyHealth>()) // pokud objekt, který jsme trefili, má komponentu EnemyHealth { hitInfo.transform.GetComponent<EnemyHealth>().GetDammage(poskozeni); // spouštíme metodu v jiném skriptu, parametrem jsme si nastavili množství poškození } } } IEnumerator Reload() // typ metody, ve které můžeme používat WaitUntil(zastaví metodu, dokud něco neplatí) { reloading = true; if (naboje > 0) { objectwithAnim.SetTrigger("Reload"); yield return new WaitUntil(() => objectwithAnim.GetCurrentAnimatorStateInfo(0).IsName("Reload Ammo Left")); // čeká, dokud nedostane hodnotu true od animátoru, že se přehrává animace yield return new WaitUntil(() => !objectwithAnim.GetCurrentAnimatorStateInfo(0).IsName("Reload Ammo Left")); // čeká, dokud nedostane hodnotu false od animátoru, že se už nepřehrává animace naboje = maxnaboje; } else { objectwithAnim.SetTrigger("ReloadNoAmmo"); yield return new WaitUntil(() => objectwithAnim.GetCurrentAnimatorStateInfo(0).IsName("Reload Out Of Ammo")); yield return new WaitUntil(() => !objectwithAnim.GetCurrentAnimatorStateInfo(0).IsName("Reload Out Of Ammo")); naboje = maxnaboje; } nabojeText.text = naboje.ToString() + "/"+maxnaboje.ToString(); reloading=false; } }
Aby nám mohlo WaitUntil()
fungovať, potrebujeme vytvoriť
metódu s návratovou hodnotou typu IEnumerator
. Skript sme preto
upravili tak, aby sme mali novú metódu Reload()
. Tú voláme na
konci Update()
, keď my stlačíme kláves R.
Táto metóda, akonáhle sa spustí, nastaví hodnotu bool
reloading
na true
. Túto hodnotu využívame pri
volanie metódy Shoot()
. Ak je reloading
true
, nemôže sa spustiť opakované volanie metódy
Shoot()
. Toto zabraňuje výstrelom pri nabíjaní s neprázdným
zásobníkom.
Potom, ak je počet nábojov väčšia ako nula, tak sa aktivuje trigger
Reload
, teda animácie prebíjanie. Pomocou objektu
WaitUntil()
zastavíme metódu Reload()
do doby, než
sa začne prehrávať animácia nabíjania. Inak by ju ďalšie
WaitUntil()
, ktoré čaká, než animácie skončí (teda
nebeží), automaticky okamžite ukončil, pretože ešte nebola ani
spustená.
Akonáhle skončí obe WaitUntil()
, náboje sa nám rovnajú
počtu maximálnych nábojov, ktorý sa stanoví po spustení hry na rovnakú
hodnotu, akú má premenná public
naboje
. Potom sa
nastaví hodnota premennej nabojeText.text=**
, týmto získame
vlastnosť tohto komponentu.
Nakoniec sa hodnota bool
reloading
nastaví na
false
, pretože už nepřebíjíme. V prípade else
všetko prebehne rovnako, akurát s odlišným parametrom animátore a
animácií.
Úprava skriptu Move
Teraz upravme ešte skript pre pohyb:
using System.Collections; using System.Collections.Generic; using UnityEngine; using System; using UnityEditor; using System.Linq; public class Move : MonoBehaviour { Rigidbody rb; // proměnná, která zastupuje naši komponentu Rigidbody Animator objectwithAnim; bool running; // hodnota running je rovna true tehdy, když běžíme a nemůžeme dělat nic jiného bool nicnedelani; void Start() { rb = transform.GetComponent<Rigidbody>(); // získáme komponentu Rigidbody objektu se skriptem objectwithAnim = GameObject.FindGameObjectWithTag("Animobject").GetComponent<Animator>(); // metoda FindGameObjectWithTag() vyhledá herní objekt, který má tag = Animobject. Tag našemu objektu nastavíme později v editoru. Cursor.visible = false; } // Update is called once per frame void Update() { if (Input.GetKey(KeyCode.W) && Input.GetKey(KeyCode.LeftShift) && !objectwithAnim.GetBool("Aim") && !objectwithAnim.GetCurrentAnimatorStateInfo(0).IsName("Inspect")) // pokud držíme W a zároveň levý Shift, nemíříme a není aktivní animace Inspect { rb.AddRelativeForce(new Vector3(0, 0, 280 * Time.deltaTime)); objectwithAnim.SetBool("Run", true); // spustíme animaci běhu změněním bool parametru running = true; // náš bool, pomocí kterého pozná náš program, že hráč běží } else // // pokud se nespustí podmínka výše, nastaví se bool hodnota na false a program ví, že hráč zrovna neběží { objectwithAnim.SetBool("Run", false); // zastaví se animace running = false; } if (!running) // pokud hráč neběží, tak se spustí podmínky níže { if (Input.GetMouseButtonDown(0) && !objectwithAnim.GetCurrentAnimatorStateInfo(0).IsName("Inspect") && !objectwithAnim.GetBool("Holster")) // pokud se stisklo levé tlačítko (označení 0), nepřehrává se animace Inspect a zároveň není zbraň schována { objectwithAnim.SetBool("Run", false); // zastavujeme animaci běhu a chůze, aby se mohla spustit animace střelby objectwithAnim.SetBool("Shoot", true); // aktivujeme parametry trigger, jako jsou Shoot a Inspect v našem případě } if (objectwithAnim.GetBool("Shoot")) { objectwithAnim.SetBool("Walk", false); // použijeme objekt, který jsme si definovali, a získáme z něj komponentu Animator, tím získáme možnost upravovat animace, které jsou k tomuto objektu přichycené } if (Input.GetKey(KeyCode.D)) // pokud někdo stiskne klávesu W, spustí se tato podmínka, dokud bude klávesa držena { rb.AddRelativeForce(new Vector3(200 * Time.deltaTime, 0, 0)); // na proměnnou přešly vlastnosti a metody komponenty Rigidbody. // metoda AddForce() přidá sílu do určitého směru, který nastavujeme pomocí os x y z. if (!objectwithAnim.GetBool("Shoot")) { objectwithAnim.SetBool("Walk", true); // pomocí animátoru získáme možnost upravovat animace, které jsou k tomuto objektu přichycené. } // jednou z funkcí komponenty Animator je změnit hodnotu parametru } if (Input.GetKey(KeyCode.A)) { rb.AddRelativeForce(new Vector3(-200 * Time.deltaTime, 0, 0)); if (!objectwithAnim.GetBool("Shoot")) { objectwithAnim.SetBool("Walk", true); } } if (Input.GetKey(KeyCode.W)) { rb.AddRelativeForce(new Vector3(0, 0, 200*Time.deltaTime)); if (!objectwithAnim.GetBool("Shoot")) { objectwithAnim.SetBool("Walk", true); } } if (Input.GetKey(KeyCode.S)) { rb.AddRelativeForce(new Vector3(0, 0, -200 * Time.deltaTime)); if (!objectwithAnim.GetBool("Shoot")) { objectwithAnim.SetBool("Walk", true); } } if (Input.GetKeyUp(KeyCode.W) || Input.GetKeyUp(KeyCode.A) || Input.GetKeyUp(KeyCode.S) || Input.GetKeyUp(KeyCode.D))// pokud pustíme W a S nebo D, tak se spustí tato podmínka { objectwithAnim.SetBool("Walk", false); } if (Input.GetMouseButton(1)&& !objectwithAnim.GetCurrentAnimatorStateInfo(0).IsName("Reload Ammo Left") && !objectwithAnim.GetCurrentAnimatorStateInfo(0).IsName("Reload Out Of Ammo")) // pokud dostane Input signál, že se stisklo pravé tlačítko (označení číslo 1) { objectwithAnim.SetBool("Aim", true); // nastaví hodnotu parametru Aim typu bool na true } else { objectwithAnim.SetBool("Aim", false); } if (Input.GetKeyDown(KeyCode.T)) { objectwithAnim.SetTrigger("Inspect"); } if (Input.GetKeyDown(KeyCode.Z)) { objectwithAnim.SetBool("Holster", !objectwithAnim.GetBool("Holster")); } } } }
Kód Move
sme trochu upravili, pretože obsahoval niektoré
podmienky, ktoré neboli už potrebné. Time.deltaTime
je doba od
posledného snímky. Keď týmto vynásobíme našu silu, zaručíme tým, že
na silnom aj slabom PC bude hráč rovnako rýchly.
Môžeme sa pozrieť na výsledok:
V budúcej lekcii, 3D strieľačka v Unity - Zmena zbraní , si vytvoríme systém pre zmenu zbraní.
Mal si s čímkoľvek problém? Stiahni si vzorovú aplikáciu nižšie a porovnaj ju so svojím projektom, chybu tak ľahko nájdeš.
Stiahnuť
Stiahnutím nasledujúceho súboru súhlasíš s licenčnými podmienkami
Stiahnuté 35x (4.08 kB)
Aplikácia je vrátane zdrojových kódov v jazyku C#