Besplatan online tečaj UNITY – NEPRIJATELJI I NJIHOVA OKOLINA

Photo of author

By Nenad Crnko

Tijekom dosadašnjeg upoznavanja s alatom Unity najviše smo se bavili samim igračem, to jest različitim svojstvima za promjenu različitih karakteristika objekta Player. Pokazali smo i to kako ga igrača možete pretvoriti u svojevrsnog supermena, ako se za nečim takvim ukaže potreba.

U današnjem nastavku pozabavit ćemo se malo više drugom stranom, a to su naravno neprijatelji.

Illustration byDiana Hlevnjak on PolarVectors

Prvi dio

Drugi dio

Treći dio

Četvrti dio besplatnog online UNITY tečaja za programiranje igara

Peti dio

Dodavanje neprijatelja i njegovo prilagođavanje

U demo modelu igre (korištenim do sada u svim primjerima za upoznavanje s razvojnom okolinom) postoji samo jedan neprijatelj. Priču o mogućnostima upravljanja neprijateljima započnimo od dodavanja novog neprijatelja u model. Način na koji se novi neprijatelj dodaje u projekt najjednostavnije je objasniti uz pomoć slike.

Dodavanje novog neprijatelja u model.

U popisu dodataka (Assets > FPS > Prefabs > Enemies) treba izabrati jednu od dostupnih vrsta neprijatelja (Enemy_HoverBot ili Enemy_Turret), a onda napraviti njegovo povlačenje negdje na slobodno mjesto na radnoj površiti. Nakon stvaranja novog objekta neprijatelja on ujedno postaje aktivni neprijatelj, što znači da se u dijaloškom okviru Inspector prikazuju njegove vrijednosti svojstava. Ako nismo zadovoljni početnim mjestom gdje se pojavio novi objekt, jednostavno ga možemo povući na bilo koje drugo mjesto na radnoj površini (to se uvijek može napraviti i s već postojećim neprijateljima).

Različita “osnovna” svojstva neprijatelja dostupna u dijaloškom okviru Inspector.

Osim povlačenjem neprijatelja na drugo mjesto, isti efekt može se postići i promjenom početnih koordinata objekta dostupnih u okviru grupe svojstava Transform (Position). Na sličan način mogu se precizno definirati vrijednosti svojstava za određivanje početne rotacije i veličine neprijatelja (Rotation i Scale).

Budući da je za svakog neprijatelja dostupan popriličan broj osnovnih svojstava, u ovom pregledu ograničit ćemo se na prikaz pojedinih grupa.

Parameters

Služi za definiranje nekoliko osnovnih parametara neprijatelja poput visine pada kod koje dolazi  do uništavanja neprijatelja, brzine orijentacije ili “dužine umiranja” (može se iskoristiti za dodavanje dodatnih efekata animacije).

Weapons Parameters

Parametri povezani s mogućnošću zamjene naoružanja neprijatelja, odnosno vremena do početka sljedećeg napada nakon zamjene naoružanja.

Eye Color

Obuhvaća nekoliko svojstava povezanih s izgledom očiju neprijatelja (materijal i boja).

Flash on Hit

Koriste se za promjenu izgleda neprijatelja prilikom njegovog napada na igrača.

Sounds

Definira vrstu zvučnog efekta koja se reproducira prilikom svakog oštećenja neprijatelja. Promjena zvučnog efekta izvodi se na identičan način kao što smo to objasnili prošli put kod zamjene zvučnih efekata za samog igrača.

VFX

Parametri zaduženi za upravljanje vizualnim efektima nakon uništavanja neprijatelja. Zajedno s demo projektom dolazi nekoliko različitih efekata koji se mogu iskoristiti umjesto podrazumijevanog efekta. Izgled svakog od dostupnih efekata može se pregledati (i izmijeniti dio parametara) prije nego što se upotrijebi u vlastitom modelu. Kako to izgleda za primjer efekta eksplozije može se vidjeti na sljedećoj slici.

Različita “osnovna” svojstva neprijatelja dostupna u dijaloškom okviru Inspector.

Naravno, ako niste zadovoljni izgledom postojećih efekata možete probati izraditi svoj vlastiti ispočetka, ili nabaviti neki od efekata iz online trgovine dodataka.

 Loot

Ovi parametri se koriste za definiranje objekata (nagrada) koje neprijatelji mogu ispustiti nakon njihove “smrti”. Na primjer, može se definirati da igrač nakon uništavanja neprijatelja dobije posebnu opremu za letenje (sljedeća slika).

Definiranje “nagrada” koje igrač može zaraditi uništavanjem nekog od neprijatelja.

Debug Display

Koristi se za definiranje različitih boja namijenjenih jednostavnijem praćenju opsega djelovanja neprijatelja.

Osnovna svojstva za definiranje načina ponašanja neprijatelja dostupna su i preko odgovarajuće skripte u C# jeziku na sličan način kao što smo to prošli put prikazali za igrača. U ovom slučaju skripta s programskim kodom naziva se EnemyController.cs, a osim početnog dijela za definiranje svojstava sadrži dijelove koda namijenjene upravljanju ponašanjem neprijatelja tijekom izvođenja modela.

Na primjer, sljedeći dijelovi koda izvode se kod pokušaja napada neprijatelja, odnosno inicijalizacije i promjene vrste naoružanja.

...
  public bool TryAtack(Vector3 enemyPosition)
  {
        if (m_GameFlowManager.gameIsEnding)
            return false;

        OrientWeaponsTowards(enemyPosition);

	if ((m_LastTimeWeaponSwapped + delayAfterWeaponSwap) >= Time.time)
            return false;

        // Shoot the weapon
        bool didFire = GetCurrentWeapon().HandleShootInputs(false, true, false);

        if (didFire && onAttack != null)
        {
            onAttack.Invoke();

            if (swapToNextWeapon && m_Weapons.Length > 1)
            {
                int nextWeaponIndex = (m_CurrentWeaponIndex + 1) % m_Weapons.Length;
                SetCurrentWeapon(nextWeaponIndex);
            }
        }

        return didFire;
    }

...

   void FindAndInitializeAllWeapons()
    {

        // Check if we already found and initialized the weapons
        if (m_Weapons == null)
        {
            m_Weapons = GetComponentsInChildren<WeaponController>();
            DebugUtility.HandleErrorIfNoComponentFound<WeaponController, EnemyController>(m_Weapons.Length, this, gameObject);

            for (int i = 0; i < m_Weapons.Length; i++)
            {
                m_Weapons[i].owner = gameObject;
            }
        }
    }

    public WeaponController GetCurrentWeapon()
    {
        FindAndInitializeAllWeapons();
        // Check if no weapon is currently selected
        if (m_CurrentWeapon == null)
        {
            // Set the first weapon of the weapons list as the current weapon
            SetCurrentWeapon(0);
        }

        DebugUtility.HandleErrorIfNullGetComponent<WeaponController, EnemyController>(m_CurrentWeapon, this, gameObject);

        return m_CurrentWeapon;
    }

    void SetCurrentWeapon(int index)
    {
        m_CurrentWeaponIndex = index;
        m_CurrentWeapon = m_Weapons[m_CurrentWeaponIndex];
        if (swapToNextWeapon)
        {
            m_LastTimeWeaponSwapped = Time.time;
        }
        else
        {
            m_LastTimeWeaponSwapped = Mathf.NegativeInfinity;
        }
    }

Primijetite da su u prethodnom kodu sve varijable i procedure nazvane tako da se vrlo jednostavno može uočiti veza između varijabli u programskom kodu te odgovarajućih svojstava u dijaloškom okviru Inspector. Nazivi varijabli na svojem početku imaju dodatak “m_”, kako bi se razlikovale od naziva funkcija. U slučajevima kad neka varijabla može imati više mogućih vrijednosti, onda je takva varijabla promijenjena u polje vrijednosti. Točno određenoj vrijednosti polja u tom slučaju pristupa se pomoću odgovarajućeg indeksa polja. Na primjer:

m_CurrentWeapon = m_Weapons[m_CurrentWeaponIndex];

Osim različitih grupa svojstava namijenjenih definiranju početnog izgleda i ponašanja svakog pojedinog neprijatelja, drugi dio svojstava zadužen je za određivanje ponašanja neprijatelja tijekom njegovog korištenja u modelu: Enemy Mobile (efekti tijekom kretanja), Health i Wordspace Help (upravljanje “zdravljem” neprijatelja i pratećim efektima), Nav Mesh Agent (upravljanje kretanjem neprijatelja po pozadini), Audio Source (zvučni efekti tijekom kretanja neprijatelja), itd.

Što se sve može postići izmjenom spomenutih svojstava najjednostavnije je demonstrirati primjerom. Nakon postavljanja drugog neprijatelja na radnu površinu napravite odmah pokretanje modela bez ikakvih izmjena početnih svojstava novog neprijatelja. Prijeđite u sljedeću prostoriju i pogledajte kako se ponašaju neprijatelji. Budući da su oba neprijatelja (i stari i novi) u biti jednaki po svojem karakteru, nema neke posebne razlike u međusobnom ponašanju, osim što u napad kreću s različitih pozicija.

U početku su oba neprijatelja jednaka po ponašanju.

Probajmo sada novog neprijatelja pretvoriti u “super neprijatelja”. Izmijenimo sljedećih nekoliko svojstava na nove vrijednosti povećanjem početnih vrijednosti dva puta:

Max Health: 200       

Speed: 7

Angular Speed: 240

Acceleration: 16

Pokrenite sada ponovo model i prijeđite u drugu prostoriju. Novi neprijatelj je mnogo opasniji (dva puta) od prvog tako da će vas on mnogo prije napasti nakon ulaska u prostoriju, a ako počnete pucati po njemu ponovo ćete ga dva puta teže ubiti.

Na primjeru ponašanja neprijatelja u demo modelu može se objasniti još jedna bitna stvar u pogledu djelovanja skripti u programskom jeziku. U duhu modernih softverskih sustava veliki dio ponašanja sustava odvija se preko takozvanih događaja (events). To znači da se izvođenje određenih dijelova programskog koda automatski pokreće kad se ispune određeni uvjeti, odnosno kad nastane odgovarajući događaj u modelu.

Nakon promjena nekoliko svojstava novi neprijatelj je mnogo opasniji i prvi počinje s napadom.

U alatu Unity postoje funkcije koje se konstantno izvode tijekom rada modela – npr. funkcija Update izvodi se automatski kao dio procedure za upravljanje kretanjem agenta (skripta EnemyMobile.cs) prije svakog ažuriranja izgleda modela.

    void Update()
    {
        UpdateAIStateTransitions();
        UpdateCurrentAIState();

        float moveSpeed = m_EnemyController.m_NavMeshAgent.velocity.magnitude;

        // Update animator speed parameter
        animator.SetFloat(k_AnimMoveSpeedParameter, moveSpeed);

        // changing the pitch of the movement sound depending on the movement speed
        m_AudioSource.pitch = Mathf.Lerp(PitchDistortionMovementSpeed.min, PitchDistortionMovementSpeed.max, moveSpeed / m_EnemyController.m_NavMeshAgent.speed);
    }

Drugi dio funkcija  predstavlja odgovor na određene vrste događaja. Za takve procedure prvo treba napraviti povezivanje s odgovarajućim događajem objekta (neprijatelja). Nešto kao “+=” naredbe u nastavku (to sve je već automatski pripremljeno u modelu):
    void Start()
    {
        m_EnemyController = GetComponent<EnemyController>();
        DebugUtility.HandleErrorIfNullGetComponent<EnemyController, EnemyMobile>(m_EnemyController, this, gameObject);

        m_EnemyController.onAttack += OnAttack;
        m_EnemyController.onDetectedTarget += OnDetectedTarget;
        m_EnemyController.onLostTarget += OnLostTarget;
        m_EnemyController.SetPathDestinationToClosestNode();
        m_EnemyController.onDamaged += OnDamaged;
...

Sam programski kod takvih funkcija manje ili više sličan je drugim dijelovima koda, jedino što njihovo izvođenje “okida” napad (OnAttack), oštećenje (onDamaged) i neka druga vrsta događaja u modelu.

    void OnAttack()
    {
        animator.SetTrigger(k_AnimAttackParameter);
    }

    void OnDamaged()
    {
        if (randomHitSparks.Length > 0)
        {
            int n = Random.Range(0, randomHitSparks.Length - 1);
            randomHitSparks[n].Play();
        }

        animator.SetTrigger(k_AnimOnDamagedParameter);
    }

Komponente neprijatelja

U dosadašnjem dijelu teksta čitavo vrijeme smo se odnosili prema neprijatelju kao jedinstvenom objektu. Unity nudi detaljniju mogućnost upravljanja pojedinim dijelovima objekta. Označite prvo novog neprijatelja postavljenog na radnu površinu, a onda kliknite na crveno označeni gumb Open u desnom dijelu slike.

Pristup dijelovima objekta neprijatelja.

Kao rezultat izvođenja prethodne operacija razvojna okolina otvara posebnu vrstu prikaza s detaljima o neprijatelju (sljedeća slika). Brojne oznake na objektu omogućavaju pristup različitim dijelovima objekta, a nakon toga i izmjenu početnih, podrazumijevanih vrijednosti. Na primjer, probajte kliknuti na objektu na točno označeni crveni dio sa sljedeće slike, čime bi u dijaloškom okviru Inspector trebao postati aktivan dio VFX_AngryStream. Ako slučajno ne uspijete od prve pogoditi baš taj dio, napravite još nekoliko klikova po objektu dok ne pogodite pravo mjesto.

Spomenuti dio objekta preko vrijednosti pripadajućih svojstava upravlja načinom na koji neprijatelj pokazuje svoju „ljutitost“. Izmjenom nekoliko vrijednosti – na primjer povećanjem vrijednosti StartSize na 3 ili Duration na 1, možete postići da novi neprijatelj bude znatno strašniji od onog početnog, koji je stigao u samom demo modelu.

Kako izgleda pojedino svojstvo objekta nakon izmjene njegovih vrijednosti možete provjeriti klikom na crveno označeni gumb Play.

Na sličan način možete isprobati izmjene brojnih drugih svojstava neprijatelja, kako u pogledu njegovih sastavnih dijelova tako i u pogledu svojstva cijelog objekta

Pregled i izmjena različitih dijelova objekta.

Za povratak na pregled cijelog modela potrebno je kliknuti na gumb Scenes smješten odmah lijevo od naziva trenutno aktivnog objekta čije detalje pregledavamo.

Gumb za povratak na pregled cijelog modela.

Slično kao što se pomoću gumba Open može prijeći na pregled dijelova aktivnog neprijatelja, to isto možete napraviti i za samog igrača (kojim smo se detaljnije bavili u prethodnim nastavcima), pa možete isprobati dostupne izmjene i na njemu. Kako to izgleda možete vidjeti na sljedećoj slici.

Nakon današnjeg nastavka imate već dovoljno materijala da vježbom probate izmijeniti brojna svojstva objekata igrača i neprijatelja (ili njihovih pojedinih dijelova), kako bi početni model igre izmijenili u nešto potpuno drugačije.

Pregled detalja za objekt igrača.