Unity Ohjeita
Unity
Unity on suosituin ilmainen pelimoottori jolla voi tehdä pelejä. Se antaa pelikehittäjälle vapaat kädet lähteä luomaan minkälaista peliä tahansa kaikille pelialustoille tietokoneista pelikonsoleille ja kännyköille, ja jopa vr-laitteille. Unity saattaa olla haastavaa aluksi, mutta kaikki Unityssä käytettävät asiat ovat netistä helposti löydettävissä mikä auttaa aloittelijoita alkuun.
Kun unityn käynnistää ensimmäisen kerran, kysyy se käyttäjältä koodia mikäli hänellä on sellainen. Koska käytämme unityn ilmaisversiota, meillä ei kyseistä koodia ole, joten painamme sen viereisestä ruksista saadaksemme käyttää ilmaista versiota. Tämän jälkeen Unity pyytää meitä kirjautumaan sisään Unity-tunnuksillamme. Ellette ole aijemmin unitya käyttäneet, niin teillä ei tätä vielä ole. Painakaa sinisestä "create one" kohdasta niinkuin kuvassa, niin teidät viedään unityn nettisivuille jossa voitte luoda tunnukset. Tässä ei pitäisi kovin kauaa mennä.
Luo projekti
Kun pääsemme edellisestä osasta pois, pääsemme unityn perus käynnistys näkymään. Tässä näkyy kaikki viimeaikaiset projektit jota olet muokannut. Teillä tämän pitäisi olla täysin tyhjä. Jotta saamme luotua uuden projektin, painetaan "new" oikeasta yläkulmasta. Tämän pitäisi avata teille tämän näköisen ikkunan.
Tässä voimme luoda projektimme. Ensin valitsemme projektillemme nimen. Kannattaa olla tekemättä nimestä liian monimutkaista, ja välttää ääkkösien käyttämistä. Seuraavassa kohdassa valitsemme kansion johon projekti luodaan. Tähän kannattaa luoda oma projektikansionsa jonnekkin tietokoneelle johon tallennat kaikki tulevat projektimme. Seuraavana on organization. Siihen voitte vain valita nimen jonka asetitte itsellenne unityn sivuilla.
Oikealla näkyy ruksittuna 3D ja ilman ruksia 2D. Tämä tarkoittaa teemmekö 3D vai 2D projektia, ja luo perusnäkymän sen mukaan. Koska teemme 3D pelin niin meidän ei tarvitse muuttaa mitään.
Add Asset Package tarkoittaa haluatko lisätä erillisiä assetteja peliin. Assetit ovat unitylle luotuja kirjastoja joita kuka tahansa voi tehdä ja käyttää. Näitä kirjastoja myydään ja ladataan unity assets storessa. Emme kuitenkaan tarvitse näitä harjoitusksien aikana, joten meidän ei tarvitse painaa siitä.
Näiden alla on vielä Enable unity analytics, joka on unityn tarjoama palvelu, jolla pelin kehittäjä voi seurata millaisia valintoja pelaaja pelissä tekee. Emme kuitenkaan tätäkään tarvitse, joten sen voi ottaa pois, jolloin myös organization kohta menee pois, sillä sitä tarvitaan vain unity analyticsissa. Jos haluatte tietää analyticsista enemmän, lukekaa ominaisuuksista täältä.
Tämän jälkeen voimme painaa create project ja odottakaa kun unity luo projektin perusnäkymää. Myöhemmin tehdessämme projekteja, ohjeissa ei mainita kaikkea näin tarkkaan.
Kun unity on luonut projektin, menee se projektin perusnäkymään.
Käydään hieman läpi mitä kuvassa näemme.
Scene
Katsomaan ensimmäisenä Scene-näkymää, joka on keskellä ruutua. Tässä voit muokata kaikkea mitä peliin on laitettu. Voit liikkua näkymässä vapaasti. Hiiren keskimmäistä näppäintä pitämällä pohjassa näkymässä ja siirtämällä hiirtä voit liikkua sivuille sekä ylös ja alas. Hiiren rullalla saat mentyä scenessä eteen ja taakse. Hiiren vasenta näppäintä pitämällä pohjassa ja liikuttamalla hiirtä näet ympärillesi. Scene-näkymän oikeassa yläkulmassa on värikkäitä lieriöitä, ja näitä painamalla saat helposti siirryttyä katsomaan suoraa eli suuntiin. Näkymän yläreunassa on myös erilaisia nappeja, joissa pitäisi lukea shaded ja 2D, yms. Näitä painamalla voit muuttaa hieman mitä kaikkea scenessä näkyy ja miten, mutta me emme halua muuttaa näistä nyt mitään. Näin voit liikkua ja tutkia peliä. Nyt pelissä ei ole vielä mitään tutkittavaa koska se on suhteellisen tyhjä, mutta voit huomata kaksi ikonia scenessä, kameran ja valon.
Hierarchy
Kamera ja valo löytyvät oletuksena joka scenestä kun se luodaan. Kun katsot vasemmalle Hierarchy-näkymään, näet täällä kaikki objektit mitä scenessä on, ja siellä pitäisikin olla Main Camera sekä Directional Light. Kun lisäämme peliin objekteja, ne on kaikki helposti löydettävissä täältä.
Inspector
Oikealla on näkyvissä Inspector, joka on tällä hetkellä tyhjillään, mutta johon ilmestyy asioita, kun napsautamme jotain objektia scenessä tai hierarchy-näkymässä. Inspector-näkymässä näemme kaikki ominaisuudet mitä valitsemassamme objektissa on.
Ylimpänä näkyy objektin nimi, ja sen vasemmalla puolella ruksi onko objekti aktiivinen. Jos napsautamme tästä, saamme objektin epäaktiiviseksi, eli peli käyttäytyy niinkuin kyseistä objektia ei olisi olemassa, mutta se silti näkyy hierarchyssa, ja saamme sen takaisin aktiiviseksi napsauttamalla ruksin päälle.
Näiden alapuolella on objectin tag, eli perjaatteessa ryhmä johon se kuuluu, sekä layer, eli taso jolla objekti on. Kun luomme uuden objectin, oletuksena sen ryhmä on "Untagged", eli se ei perjaatteessa ole missään ryhmässä. Poikkeuksena tälle on projektiin automaattisesti luotu kamera, jonka tag on MainCamera. Opetamme tagien käytön hieman myöhemmin ohjeissa. Jos haluamme että jokin objekti on olemassa ja vaikuttaa peliin, muttemme halua että pelaaja näkee tätä, vaihdamme sen eri layerille, eli tasolle.
Näiden alapuolella on lista objektin komponenteista, eli kaikista ominaisuuksista joita objektilla on. Jokaisella objektilla on ylimpänä ominaisuutena transform, sillä mikään objekti ei voi toimia ilman tätä. Transformissa muutamme objektin positionia eli paikkaa, rotatiota eli mihin päin se on kääntyneenä, sekä scale eli skaalausta, eli kuinka suuri objekti on. Voimme muuttaa jokaista yksittäistä arvoa miten haluamme nappaamalla arvon vieressä olevasta kirjaimesta eli joko x, y, tai z. Voimme myös vain painaa arvosta ja laittaa siihen haluamamme luvun. Kaikki muut objektin komponentit ovat kaikilla eri, joten käymme ne läpi silloin kun se on ajankohtaista.
Project
Näytön alhaalla pitäisi olla auki projekti näkymä, jossa vain lukee että "this folder is empty", eli tämä kansio on tyhjä. Täällä näkyy kaikki tiedostot mitä pelissä käytämme, kuten kaikki lisäämämme 3D-mallit, scriptit eli kooditiedostomme yms. Koska meillä ei vielä projektissa mitään ole, niin on tämä tyhjä.
Unityn näkymä on siis jaettu 4 eri näyttöön, joissa on tällä hetkellä näkyvissä Hierarchy, Scene, Inspector ja Project. Näille näkymille voidaan kuitenkin asettaa useampiakin ikkunoita, kuten esimerkiksi nettiselaimessa on monta eri ikkunaa, niin samoin näillä unityn näytöillä on ns. monta eri ikkunaa, ja nämä ikkunat näemme aina näytön yläreunassa.
Console
Jos katsot projektinäkymän yläreunaan, huomaat sen vieressä lukevan "Console". Paina tästä, niin pääsemme konsolinäkymään, eli avaamme toisen ikkunan näkymään samasta näytöstä. Konsolinäkymä toimii samoin kuin ohjelmoinnissa käyttämämme konsoli, eli jos haluamme nähdä jotain tiettyä pelissä, voimme tulostaa sen tähän. Tämä on kuitenkin näkyvissä vain pelin kehittäjälle, eli mitään pelillisiä ominaisuuksia niin kuin käyttäjän syötettä ei tähän voi saada, vaan se on lähinnä tarkistamaan että koodimme toimii oikein.
Asset Store
Kun katsomme Scene-näkymän yläreunaan, huomaamme että silläkin on eri ikkunoita. Asset Store avaa Asset kaupan sivun, josta voimme halutessamme ladata ja ostaa assetteja. Koska emme tätä kuitenkaan halua tehdä, niin voimme klikata sitä oikealla hiirellä, ja painaa "close tab" niin suljemme kyseisen ikkunan. Voitte kuitenkin halutessanne tutkia mitä kaikkea täältä löytyy.
Game
Scene-näkymän vieressä näkyy myös Game-näkymä. Kun painamme tästä, niin näemme Scenen nyt hieman eri kuvakulmasta emmekä voi liikkua mitenkään. Tämä on peli-näkymä, jossa näkyy aina se mitä näkyy Main Camerasta eli pääkamerasta. Kun testaamme peliämme, tulemme automaattisesti tähän näkymään, ja voimme kokeilla ominaisuuksia joita olemme peliin lisänneet.
Objektin luominen
Nyt kun tiedämme vähän mitä näemme, niin voimme kokeilla jo tehdä jotain. Hierarchy-näkymän yläreunassa lukee "Create", painakaa tästä niin meille avautuu valikko eri asioita joita voimme peliin luoda.
Viekää kursori 3D Object kohdan päälle, niin meille avautuu valikko perus 3D objekteista joita voimme luoda. 6 ylintä vaihtoehtoa on kaikkein perinteisimpiä objekteja joita voimme luoda. Paina ylintä vaihtoehtoa eli cube, niin luomme peliin kuution. Kuution nimi "Cube" lukee Hierarchy-näkymässä, ja kuution pitäisi näkyä myös Scene näkymässä. Jos kuutio ei kuitenkaan näy Scene-näkymässä, niin painakaa kuution nimeä Hierarchy-näkymässä, ja katsokaa Inspector näkymästä oikealta mikä on kuution position. Jos objektin position on muuta kuin (0,0,0), niin painakaa Transformista hiiren oikealla ja painakaa reset, niin saamme muutettua kaikki arvot nopeasti perusarvoiksi. Viimeistään nyt pitäisi laatikon näkyä keskellä Sceneä. Mikäli ei näy, niin tupla-klikkaamalla kuution nimeä Hierarchy-näkymässä pääsemme suoraa siihen missä kuutio on.
Voimme luoda myös objekteja painamalla hiiren oikeaa Hierarchyssa ja valitsemalla haluamamme objekti, tai valitsemalla ikkunan yläreunasta GameObject ja valitsemalla haluamamme objekti.
Objektin muokkaus
Voimme helposti liikutella objekteja Scenessä käyttämällä työkaluja jotka näkyvät Hierarchy-näkymän yläpuolella.
Tämä työkalu toimii täysin samoin kuin hiiren keskimmäinen nappi scenessä, mutta nyt hiiren vasemmalla painikkeella. Tämän pikanäppäin on 'Q'
Tämä työkalu näyttää valitsemamme Objektin sivuille nuolet joista saamme kiinni hiiren vasemmalla näppäimellä, ja voimme helposti siirrellä objektia scenessä. Tämän pikanäppäin on 'W'
Tämä työkalu luo objektin ympärille monta eriväristä ympyrää, ja niistä vetämällä hiiren vasemmalla näppäimellä saamme käännettyä objektia haluamallamme tavalla. Tämän pikanäppäin on 'E'
Tämä työkalu luo objektin ympärille kuutioita joista vetämällä saamme helposti muutettua objektin kokoa aina jokaisella akselilla, tai jos vedämme valkoisesta laatikosta objektin keskeltä saamme muutettua sen kokoa kaikilla akseleilla yhtä paljon. Tämän pikanäppäin on 'R'
Tämä työkalu on myös skaalaus työkalu, joka luo objektin reunoille kehikon josta vetämällä voimme muuttaa objektin kokoa. Tämän pikanäppäin on 'T'
Emme voi itse objektin ulkomuotoa muuttaa Unityssa, vaan tähän tarvitsemme blenderiä. Unityssa itsessään voimme vain luoda yksinkertaisia muotoja ja muutta
Objektien lapset
Voimme laittaa eri objekteja myös toisten lapsiksi vetämällä Hierarchyssa objekti toisen objektin päälle. Tällöin, jos siirrämme tai muuten muutamme isäntä-objektia, myös kaikki lapsiobjektit muuttuu samalla tavalla.
Objektien tuhoaminen
Voimme tuhota objektit painamalla sen nimestä Hierarchy-näkymässä oikealla hiiren painikkeella ja valita delete.
Jotta voimme tehdä pelejä, on meidän kokeiltava koko ajan että se toimii juuri oikein. Tämä onnistuu painamalla oikealla osoittavaa nuolta näytön yläreunasta. Tämä käynnistää pelin, ja peli etenee niinkuin se on ohjelmoitu toimimaan. Voit kokeilla jo miten peli käynnistyy. Se vaihtaa ikkunan suoraa pelinäkymään, ja näyttää mitä kamerasta ikinä näkyykään. Siellä ei pitäisi näkyä paljoa, sillä pelissä ei vielä juuri mitään ole. Testi loppuu kun painamme nuolesta uudestaan. Kun testi on päällä, kun painamme keskimmäistä tauko-näppäintä, menee testi taukotilaan, jolloin voimme vaihdella objektien paikkaa tai arvoja aloittamatta kokonaan peliä alusta. Nämä arvot eivät kuitenkaan ole pysyviä, ja palautuvat takaisin normaaleihin arvoihin kun lopetamme testi-tilan. Kun olemme tauko-tilassa, painamalla oikeanpuoleisinta näppäintä, pystymme kulkemaan pelissä frame kerrallaan, ja katso hyvin tarkkaan, mitä kullakin framella tapahtuu. Useimmissa tapauksissa tätä ei kuitenkaan tarvitse edes käyttää. Kun tulevissa ohjeissa pyydämme testaamaan peliä, paina tällöin vasemman puoleisinta näppäintä.
Nyt kun tiedämme vähän miten unityssa asiat näkyvät ja niitä muutettua, niin aloitetaan ensimmäinen projekti. Ei hätää, jos projektin aikana tulee jotain mitä ei aijemmin vielä mainittu, niin siitä selitetään kyllä paremmin.
Aivan alkuun luodaan pelaaja hahmomme peliin. Luo Sceneen uusi "Plane" objekti, joka toimii pelimme maana vähän aikaa. Tämä littana objekti kannattaa siirtää hieman alas. Sitten luodaan sceneen objekti nimeltä "Capsule" joka luo kapselimaisen objektin sceneen. Nimeä objekti uudestaan "pelaaja" nimiseksi Inspector-näkymän yläreunasta. Tämän jälkeen hieman alimman komponentin alapuolella pitäisi olla nappi jossa lukee "Add Component". Paina siitä, niin sinulle avautuu valikko täynnä alavalikoita. Etsi alavalikko nimeltään "Physics" ja avaa kyseinen alavalikko. Täältä etsi sen niminen komponentti kuin "Rigidbody" ja paina siitä. Tämä lisää kyseisen komponentin objektiimme. Nyt pelaaja hahmostamme pitäisi löytyä kaksi tärkeintä komponenttia mitä Unityssä on, colliderin ja rigidbodyn.
Mikäli vahingossa lisäsimme jonkin komponentin mitä emme halunneet, siirrä kursori komponentin nimen päälle, paina vasenta hiirtä ja paina "Remove Component". Tämä poistaa komponentin objektista, mikäli se on vain mahdollista, mutta esimerkiksi transform-komponenttia ei voi poistaa miltään objektilta.
Rigidbody
Rigidbody lisää fysiikat objektillemme, eli nyt objektiimme vaikuttaa painovoima, ja muut objektit vaikuttavat siihen törmätessä. Voimme komponentin arvoja halutessamme
.
Ensimmäisenä on objektin Mass, eli kuinka paljon objekti painaa. Tällä ei ole niin paljoa väliä, koska pelaaja tulee luultavasti olemaan ainut objekti jolla on paino.
Drag jonka arvoa muuttamalla saamme hidastettua objektin liikettä.
Angular Drag joka muuttaa objektin pyörimisnopeutta.
"Use Gravity". Jos tämä on päällä niin objektiin vaikuttaa painovoima, jos taas ei niin objekti leijuu ilmassa. Tämä pitää olla ruksittuna.
"Is Kinematic". Jos tämä on päällä niin objekti on kinemaattinen, eli vaikuttaako Rigidbody-komponentin mitkään asetukset vielä objektiin. Tällä voimme helposti määritellä objektille painovoiman valmiiksi, ilman että se vaikuttaa objektiin tietyissä tilanteissa.
Seuraavaan vaihtoehtoon joka on "Interpolate", voit tutustua enemmän täältä.
"Collision Detection", tarkoittaa miten tämä objekti osuu muihin objekteihin, ja sitä käytetään hyödyksi nopeasti liikkuvien objektien kanssa. Discrete ei vaikuta objektin normaaliin toimintaan mitenkään. Kaksi muuta vaihtoehtoa, "Continuous" ja "Continuous Dynamic" on luotu juuri nopeasti liikkuviin objekteihin etteivät ne mene toistensa läpi. Aseta nopeasti liikkuvaan objektiin tämän arvoksi "Continuous Dynamic", ja muihin objekteihin joiden läpi tämä objekti ei saa mennä, aseta arvoksi "Continuous". Tässä pelissä meillä eivät objektit kuitenkaan kulje niin nopeaa, joten tähän arvoksi saa jäädä "Discrete"
.
Viimeiseksi meillä on aukaistavana alavalikko "Constraints", eli rajoitukset. Täällä voimme asettaa ettei painovoima tai fysiikat vaikuta objektiin, ylempänä objektin paikkaan, ja alempana objektin kääntyvyyteen. Asetamme fysiikat vaikuttamaan objektin kääntymiseen, eli laitamme ruksin Freeze Rotation:n kohtiin X, Y ja Z.
Collider
Collider on periaatteessa suojakehikko joka pitää huolen ettei objektit mene päällekkäin. Collidereita on monenlaisia ja muotoisia, ja niitä kannattaakin vähä kokeilla aina tilanteesta riippuen mikä on paras. Collider siis pitää huolen että objektimme on mahdollista törmätä muihin objekteihin. Voimme halutessamme muuttaa colliderin koon sopivaksi, mutta tällä kertaa muuhun ei tarvitse koskea. "Is Trigger" ruksista ja "Material" kohdasta kerrotaan tarkemmin myöhemmin ohjeissa.
Voit kokeilla Rigidbodyn ja colliderin toimintaa pelissä. Lisää sceneen kaksi tavallista objektia kuten kuutiota, ja aseta toinen päälle ja toinen hieman ensimmäisen alapuolelle. Lisää ylempään Rigidbody. Molemmissa pitäisi olla jo valmiina BoxColliderit, eli laatikon muotoiset colliderit. Nyt kun painamme play, ylempään objektiin alkaa painovoima vaikuttaa, ja se alkaa tippua alas päin, kun taas alempi objekti jää paikoilleen, koska siitä puuttuu rigidbody. Ylempi objekti myös törmää alempaan objektiin ja lopettaa tippumisen, koska molemmilla on colliderit. Nyt jos otamme toisesta objektista colliderin pois, ja teemme saman testin uudelleen, ne eivät enää törmää toisiinsa, vaan laatikko vain jatkaa tippumistaan.
Objektissamme oli valmiiksi jo CapsuleCollider, joka on juuri hyvä pelaajahahmon luomiseen, sillä se ei todennäköisesti jää jumiin minnekkään.
Objektissamme on myös "MeshFilter" ja "MeshRenderer" komponentit. Näiden avulla näemme pelaajahahmomme scenessä. MeshFilter määrittelee minkälainen mesh objektissamme on, eli miltä objektimme näyttää. Tällä kertaa siellä pitäisi lähinnä lukea Capsule. MeshRenderer on se joka saa objektimme näkyväksi, eli "renderöi" objektimme. Täällä voimme muuttaa asetuksia jotta objekti renderöidään eri tavalla, eli että se näyttäisi hieman erilaiselta. Koska objekti on pelaajahahmo, ja teemme tästä tasohyppelystä ensimmäisen persoonan pelin, poistame kyseiset komponentit. Mutta testitarkoituksessa on hyvä että vielä näemme objektimme, eli jätetään komponentit näin.
Scriptin luominen
Luo project-näkymässä uusi kansio, ja anna sille nimeksi esimerkiksi scriptit. Avaa kansio, paina hiiren vasemmalla, valitse "Create", paina kohdasta "C# Script" ja nimeä scripti nimellä "liikkuminen". Näin olemme luoneet ensimmäisen ohjelmamme peliin, mutta tämä ei vielä tee mitään. Avaa koodi tuplaklikkaamalla siitä. Koodin tulisi aueta erillisessä koodin muokkaus ohjelmassa, joka on luultavasti "Visual Studio". Tämä on koodin muokkaus ohjelma jotka toimivat vähän samalla tavoin kuin käyttämämme nettisivu c# harjoituksissa. Visual Studio asentuu samalla kun asensimme Unityn koneellemme. Jotta voimme käyttää Visual Studiota, tulisi meidän taas kirjautua visual studioon käyttäjällämme. Mikäli teillä ei ole microsoft/outlook/hotmail tiliä, niin joudumme mennä taas nettiin luomaan itsellemme sellaiset. Onneksi meidän ei tarvitse luoda koko sähköpostia, vain yhdistää jo olemassa oleva sähköposti outlookiin.
Kun olemme kirjautuneet sisään, niin huomaamme että koodi näyttää hieman erillaiselta mitä olemme c# tehtävissä tottuneet käyttämään. Alussa on perinteisen kirjaston lisäksi otettu kaksi unityn omaa kirjastoa käyttöön. Näiden avulla pystymme käyttämään paremmin unityn omia ominaisuuksia hyödyksi. Seuraavaksi luodaan classi, ja sen nimeksi annettiin automaattisesti sama minkä annoit tiedostolle nimeksi. Tämän jälkeen tulee 2 meille ennen näkemätöntä metodia nimeltä "Start" ja "Update". Nämä ovat unityn omia metodeja jotka se tunnistaa, ja käyttää tietyllä tavalla.
Start-metodissa olevat koodit unity suorittaa kaikkein ensimmäisenä kun objekti luodaan. Tänne kannattaa laittaa muuttujien perusarvojen asettaminen.
Update-metodissa olevat koodit unity suorittaa joka framella, eli koko ajan. Tänne laitetaan pelin kaikki pelilliset ominaisuudet, kuten näppäinten painalluksien tunnistus ja liikkuminen, sekä muita vastaavia asioita.
Testataan aluksi että kaikki toimii oikein. Kirjoita Update-methodin sisään:
int i=0; print("TOIMIIIII "+i); i++; |
Tämä tulostaa konsoliin printin sisällön, samalla tavoin kuin Console.WriteLine(); tekee tavallisessa c# ohjelmassa. Täällä meillä ei kuitenkaan ole normaalia konsolia, vain unityn oma konsoli käytössä, joten joudumme käyttää tätä koodia asioiden tulostamiseen. Tässä myös näemme kuinka usein Update-methodia kutsutaan. Testatkaa siis mitä tapahtuu kun nyt painamme play-nappia. Ja muistakaa, aina kun teemme koodiin jotain muutoksia, muistakaa tallentaa kaikki muutokset, tai muuten koodi ei toimi oikein. Tämä onnistuu nopeiten CTRL+S näppäin yhdistelmällä.
Pelissä ei pitäisi tapahtua mitään, tai ei ainakaan konsoliin. Tämä johtuu siitä, että tekemämme koodi ei ole vielä pelissä. Loimme kyllä project-kansioon koodin, mutta meidän on asetettava se jollekkin objektille, ennen kuin se tekee pelissä yhtään mitään. Lisätään siis koodi pelaaja-objektiimme. Tämä onnistuu samalla tavalla kuin muidenkin komponenttien lisääminen, eli painamalla Add Component, sieltä ala-valikko scripts ja scriptimme nimi. Voimme myös asettaa koodin suoraa objektiin vetämällä koodin project-kansiosta Hierarchyyn haluamamme objektin päälle. Nyt kun kokeilemme peliä, pitäisi konsoliin tulostua TOIMIIII ja aina kuinka mones tulostus se on. Jos lisäämme saman koodin johonkin toiseen obejktiin, huomaamme että nyt kaikki tulostuu kaksi kertaa. Koodi ei välitä vaikka sama koodi olisi jo toisessakin objektissa, se tekee sen mitä se on ohjelmoitu tekemään, joten jos samaa koodia on monessa eri paikassa, se tekee samat asiat useaan kertaan.
Mutta, aloitetaan koodin teko. Tasohyppelyn ohjelmointi on helppoa, koska siihen tarvitaan lähinnä c# tehtävissä opittuja if-lausekkeita. Aloitetaankin laittamalla if-lauseke Update methodin sisään, ja laitetaan se tapahtumaan aina kun Inputista löytyvä axis nimeltään "Horizontal" on muuta kuin 0. Koodi siis näyttää tältä.
if(Input.GetAxis("Horizontal")!=0){ } |
Katsotaan hieman mitä koodi pitää sisällään.
Input
Input on unityn omia muuttujia, jonka avulla voimme lukea kaikki käyttäjän painamat näppäimet, jos haluamme. Tällä kertaa haluamme Input:tista Axis eli akselin nimeltään "Horizontal". Mitä tämä tarkoittaa on se, että ohjelma katsoo koko ajan olemmeko painaneet oikealle tai vasemmalle nuolta, tai A tai D. Voisimme asettaa ohjelman tunnistamaan tarkalleen nämä näppäimistön painallukset, mutta tämä koodi on parempi, sillä tunnistaa myös tatin ja nuolten näppäimet mikäli pelaaja käyttääkin peliohjainta. Input.GetAxis("Horizontal") antaa ohjelmalle numeron, joka on välillä -1 ja 1. Mikäli pelaaja ei yritä liikkua sivuille, antaa tämä ohjelmaan 0. Jos pelaaja yrittää liikkua vasemmalle, antaa tämä -1, ja jos oikealle, niin 1. Jos pelaaja käyttää tattia, antaa tämä hieman tarkemman luvun sitä mukaa kuinka kauas hän tattia siirtää. Koska luku on 0 vain silloin kun ei edes yritetä liikkua, laitetaan if-lauseke tarkistamaan onko arvo 0, ja jos ei ole, pelaaja yrittää liikkua joten laitetaan hahmo liikkumaan.
transform
Laitetaan nyt koodia if-lausekkeeseen. Kuten aijemmin mainittiin, jokaisessa objektissa on transform-komponentti joka sisältää kaikki objetkin paikka tiedot. Voimme muokata näitä arvoja helposti koodissa, käyttämällä pelkästään sanaa transform, jolloin pääsemme käsiksi sen objektin transformiin johon koodi on lisätty. Kirjoittamalla esimerkiksi transform.position pystymme muuttamaan objektin paikkaa.
Vector3
Unity pelimoottori käyttää paljon Vector3 arvoja. Vector3 on kolmesta float-arvosta koottu muuttuja, jota käytetään esimerkiksi objektien kohdan muuttamiseen. Vector3 siis sisältää aina x,y ja z arvon tässä järjestyksessä. Voimme luoda uuden Vector3:sen kirjoittamalla "new Vector3(x,y,z)" jossa x,y,z ovat haluamamme arvot. Voimme myös käyttää valmiiksi tallennettuja arvoja, kuten esimerkiksi "Vector3.right" joka antaa automaattisesti arvot (1,0,0).
Kirjoitetaan siis koodiin if-lauseen aaltosulkeiden sisälle:
transform.position+=Vector3.right*Input.GetAxis("Horizontal");
Kuten arvata saattaa, koodi lisää transform.positioniin Vector3.right, eli (1,0,0). Me kuitenkin kerromme tämän arvon ensin Input.GetAxis("Horizontal") arvolla, mikä mahdollistaa sekä vasempaan että oikeaan menemisen, koska arvo voi olla miinus-lukuinen. Input.GetAxis arvolla kertominen myös mahdollistaa nopeuden säätämisen jos pelaaja pelaa ohjaimella tateilla. Eli nyt pystymme liikkumaan sivuille.
Kopioidaan juuri tekemämme koodi, ja pastetaan se sen alapuolelle, että meillä on kaksi samannäköistä koodia peräkkäin. Tämän jälkeen, muokkaa jälkimmäistä siten, että kaikki missä lukee "Horizontal" on nyt "Vertical", ja Vector3.right onkin nyt Vector3.forward. Näin tekemällä pystymme nyt liikkumaan myös eteen ja taakse.
Nyt jos testaamme peliä, niin näemme pelaajahahmomme liikuvan sinne minne painamme.. Liike saattaa olla hieman nopea peliimme, joten voimme lisätä /4 tekemiemme koodien loppuun, että tämä nopeus jaetaan neljällä. Voitte itse säätää nopeuden kuten haluatte. Koodin tulisi siis nyt näyttää tältä:
void Update () { if (Input.GetAxis("Horizontal") != 0) { transform.position += Vector3.right * Input.GetAxis("Horizontal")/4; } if (Input.GetAxis("Vertical") != 0) { transform.position += Vector3.forward * Input.GetAxis("Vertical") / 4; } } |
Nyt meillä on pelaajan liikkuminen, mutta kamera ei seuraa pelaajaa. Tämä korjataan helposti asettamalla kamera pelaajan lapseksi, vetämällä Hierarchy-näkymässä Main Camera-objekti pelaaja-objektin nimen päälle. Nyt kun testaamme peliä, niin kamera seuraa pelaajaa koko ajan. Koska peli on ensimmäisen persoonan peli, niin muutetaan kameran position arvot olemaan (0,0.5,0), jolloin kamera on melkein objektin keskellä, mutta hieman ylempänä.
Nyt meillä on hyvä liikkuminen tehtynä, mutta ei vielä täydellinen, sillä kamera katsoo koko ajan vain yhteen suuntaan. Joten seuraavaksi, siirrytään takaisin koodiin ja laitetaan hiiri muuttamaan kameran katsomissuuntaa että voimme liikkua ympäriinsä.
Kirjoitetaan uusi if-lause, joka on samannäköinen kuin aiemminkin, mutta nyt se etsii "Horizontal" ja "Vertical" akselin sijaan akselin "Mouse Y". Näin saamme hiiren ylös-alas liikkumisen -1 ja 1 väliltä. Emme halua että koko objekti kallistuu kun katsomme ylös ja alas koska tällöin kameran kulma olisi aivan väärä ja se näyttäisi muutenkin oudolta. Haluamme vain että kamera kallistuu, joten sen sijaan että kirjoittaisimme pelkän transformin koodiin, kirjoitamme Camera.main.transform joka etsii automaattisesti pääkameran kentältä ja muuttaa tämä transform arvoja. Tällä kertaa haluamme vain kääntää kameraa, emme siirtää sitä, joten positionin sijaan kirjoitamme .eulerAngles joka tarkoittaa juuri objektin (x,y,z) kääntymisarvoja.
Lisätään eulerAngles-arvoihin Vector3.right*Input.GetAxis("Mouse Y"); jolloin kun siirrämme hiirtä alas pelaaja katsoo ylös, ja kun siirrämme hiirtä alas niin pelaaja ylös. Eli nyt koodi näyttää tältä:
if(Input.GetAxis("Mouse Y") != 0) { Camera.main.transform.eulerAngles += Vector3.right * Input.GetAxis("Mouse Y"); } |
Mikäli haluamme että hiiri toimii toisin päin, että kun hiirtä siirtää ylöspäin pelaaja katsoo ylöspäin ja niin edelleen, (niinkuin minä itse ainakin haluan) vaihdeteen koodiin "+=" sijaan "-=". Nyt hiiren pitäisi toimi oikein.
Tehdään taas kuten aijemmin, ja copypastetetaan juuri tekemämme koodi aijemman alapuolelle, ja muutetaan kaikki "Mouse Y" kohdat olemaan "Mouse X", ja Vector3.right olemaan Vector3.up. otetaan myös Camera.main. kokonaan pois, sillä kun katsomme sivuille, haluamme että koko pelaaja katsoo sivulle, ei vain kamera.
Eli nyt koko koodin tulisi näyttää tältä:
if (Input.GetAxis("Horizontal") != 0) { transform.position += Vector3.right * Input.GetAxis("Horizontal")/4; } if (Input.GetAxis("Vertical") != 0) { transform.position += Vector3.forward * Input.GetAxis("Vertical") / 4; } if(Input.GetAxis("Mouse Y") != 0) { Camera.main.transform.eulerAngles -= Vector3.right * Input.GetAxis("Mouse Y"); } if (Input.GetAxis("Mouse X") != 0) { transform.eulerAngles += Vector3.up * Input.GetAxis("Mouse X"); } |
Jos kokeilemme peli nyt, niin koodi toimii melkein oikein. Melkein. Pelaajahahmo kyllä kulkee eteen ja taakse sekä sivuille, ja katsoo ylös ja alas sekä sivuille, mutta jos katsomme sivuille niin pelaajahahmo jatkaa samaan suuntaan menemistä, eikä koko ajan suoraa niinkuin pelissä haluaisimmekin. Tämä on kuitenkin helposti korjattu. Etsi kohdat jossa muutamme pelaaja hahmon positionia, siellä pitäisi lukea Vector3.right sekä Vector3.forward. Muuta nämä olemaan transform.right ja transform.forward. Koska Vector3 arvot ovat aina samanlaisia, ne siirtävät aina samaan suuntaan, mutta kun otamme samat arvot transformista, se antaa haluamamme suunnan objektista katsottuna, toisin sanoen se palauttaa esimerkiksi aina objektista katsottuna oikeaan menevän arvon. Tämän takia halusimme myös muuttaa koko pelaajaobjektimme eulerAngles-arvoa kun katsomme sivulle, tai muuten tämä ei toimi.
Nyt kun vaihdoimme Vector3:n tilalle transform, pitäisi ohjauksen toimia täydellisesti. Peli on kuitenkin vielä hieman outo, sillä vaikka peli on ensimmäisestä persoonasta, näemme hiiren liikkuvan haluamaamme suuntaan. Tämä saadaan korjattua kirjoittamalla Start() methodiin
Cursor.lockState = CursorLockMode.Locked; |
Tämä piilottaa kursorin näkyvistä aivan pelin alussa, ja lukitsee sen näytön keskelle. Saamme sen vapautettua painamalla esc-näppäintä näppäimistöltä. Mutta kun vapautamme kursorin, emme saa sitä takaisin piiloon, joten lisätään koodiin että saamme klikkaamalla peliä hiirellä kursorin takaisin piiloon.
Kirjoitetaan Update() methodiin uusi if-lauseke, mutta tällä kertaa kirjoitetaan sinne Input.GetMouseButtonDown(0). Tämä saa Inputista tällä kertaa tiedon, onko hiiren vasenta näppäintä painettu. Jos on, niin se suorittaa koodin if-lausekkeen sisällä. Jos haluamme tämän toimivan hiiren vasemmalla näppäimellä, laitetaan sulkuihin 0 sijaan 1, ja jos hiiren keskimmäisellä, niin laitetaan sulkuihin 2. Sijoitetaan if-lausekkeen sisään sama koodin pätkä minkä laitoimme Start()-methodiinkin.
Eli nyt meillä on kaikkein tärkeimmät laitettu, joten eiköhän vihdoin aseteta peliin hyppy-toiminto. Luodaan Update-methodiin vielä yksi if-lauseke, ja tarkistetaan Input.GetButtonDown("Jump"). Kuten akselinkin kanssa, tämä etsii painetaanko yleistä hyppynappia, joka näppäimistöllä on välilyönti, ja ohjaimella joku omansa. Laita if-lausekkeen sisään
GetComponent<Rigidbody>().velocity=Vector3.up*3; |
GetComponent<>()
GetComponent on yksi tärkeimpiä asioita mitä Unitylla ohjelmoinnissa käytämme. GetComponent on lauseke jolla saamme minkä tahansa haluamamme komponentin objektista, kunhan komponentti on objektissa olemassa. Muuten se antaa virheilmoituksen. Haluamamme komponentin nimi laitetaan <> väliin, ja tavalliset sulkeet jätetään aina tyhjäksi.
Mitä haluamme tehdä, on lisätä objektiin ylösmenevää voimaa, niinkuin se hyppäisi ylöspäin. Teemme tämän vaikuttamalla objektin fysiikkaan, ja koska Rigidbody-komponentti tekee kaiken fysiikan objektiin, käytämme getcomponenttia saadaksemme Rigidbodyn. Rigidbody-komponentilla on velocity arvo, joka antaa voimat johon objektin fysiikat tätä liikuttavat. Jos kokeilemme laittaa tämän printtiin, kuten:
print(GetComponent<Rigidbody>().velocity);
niin saisimme luultavasti arvot jotka muistuttavat (0,-1,0), koska fysiikan painovoimat painavat sitä koko ajan alaspäin. Mutta jos asetammekin Velocityyn arvon joka työntää objektia ylöspäin, saamme luotua realistisen tuntuisen hypyn. Nyt koodimme asettaa ylöspäinvoimaa 3, mutta voimme muokata kolmosen tilalle minkä tahansa haluamamme numeron, joka säätää kuinka voimakas hyppy on.
Nyt meillä on hyppy, mutta pelissämme saattaa olla paljon korkeita paikkoja, mitä jos haluamme tuplahypyn? Tämä onnistuu luomalla int muuttuja ennen Start methodia, jonka nimeksi voimme asettaa vaikka "hypyt", ja sen arvoksi 0. Eli siis se näyttää tältä:
int hypyt = 0; void Start () { Cursor.lockState = CursorLockMode.Locked; } |
Koska luomme int muuttujan tuossa kohtaa, emmekä esim Update methodin sisällä, muuttujan arvot pysyvät koko ajan samana kun me sen asetamme. Jos loisimme muuttujan Update methodissa, arvo palaisi nollaksi koko ajan, koska update methodi suoritetaan joka framella, jolloin se palauttaa sille sen perusarvon eli 0. Nyt vain suoraan tekemämme muutokset muuttavat hypyt muuttajaa.
Teimme if-lausekkeen joka tarkistaa onko "Jump" nappia painettu. Mennään takaisin tähän, ja laitetaan kyseinen if-lauseke tarkistamaan myös, ettei hypyt-muuttuja ole esimerkiksi 2. Mehän saamme if-lausekkeen tarkistamaan toisenkin arvon käyttämällä &&, ja saamme tarkistettua ettei hypyt ole 2 kirjoittamalla hypyt!=2. lisäämme myös if-lausekkeen sisäkke hypyt++; joka lisää hypyt-muuttujaan nyt aina yhden lisää kun hyppäämme. Eli nyt, if-lausekkeen tulisi näyttää tältä:
if (Input.GetButtonDown("Jump")&& hypyt!=2) { GetComponent<Rigidbody>().velocity = Vector3.up * 3; hypyt++; } |
Kuten varmaan voitte arvata, nyt pystymme hyppäämään vain kaksi kertaa. Nyt ongelmana on, miten saamme tunnistettua onko pelaajahahmo osunut maahan, jolloin hypyt-muuttuja palaa nollaksi, että voimme taas hyppiä kaksi kertaa.
Tämä onnistuu luomalla Unityn oman metodin OnCollisionStay. Tämän sijoitamme void Update() -lohkon alapuolelle.
void OnCollisionStay(Collision col){} |
OnCollision
Unityssa on törmäyksen tunnistus mekaniikkansa, johon pääsemme käsiksi käyttämällä OnCollision-methodeja. Näitä methodeja on 3 erillaista.
OnCollisionEnter, jota kutsutaan silloin kun objektimme osuu uuteen objektiin. Tätä methodia kutsutaan kerran juuri silloin kun objekti törmää toiseen. Jos objektit pysyvät toisissaan kiinni, ei tätä methodia kutsuta yhtä kertaa enempää.
OnCollisionStay, jota kutsutaan niin kauan kun objekti osuu toiseen objektiin. Tätä methodia kutsutaan niin kauan kun objektit koskettavat toisiaan.
OnCollisionExit, jota kutsutaan silloin kun objektimme erkaantuvat toisistaan. Tätä methodia kutsutaan kerran juuri silloin, kun kaksi objektia irtoavat toisistaan.
Jotta mitään näistä methodeista kutsutaan, on molemmissa objekteissa jotka törmäävät oltava jonkinlainen Collider, ja edes toisessa on oltava Rigidbody-komponentti.
Jokaisessa methodissa annetaan myös Collision muuttuja, jonka voimme nimetä miksi ikinä haluammekaan, mutta itse nimeän sen yleensä "col" nimiseksi. Tämä Collision-muuttuja sisältää kaiken informaation törmäyksestä, joka tapahtui. Tästä saamme esim. toisen objektin, johon tämä objekti jossa koodimme on törmäsi. Saamme tämän helposti kirjoittamalla col.gameObject, tai korvaamalla col miksi ikinä nimesimmekään Collision-muuttujan.
Mutta nyt, luodaan yksinkertaisesti koodi joka törmäyksen tapahtuessa nollaa hypyt-muuttujan. Me lisäsimme jo OnCollisionStay methodin, joka siis suoritetaan aina kun pelaaja-objektimme osuu maahan. Methodin sisään asetetaan vain hypyt=0; joka nollaa hypyt. Nyt koko koodimme näyttää tältä:
using System.Collections; using System.Collections.Generic; using UnityEngine; public class liikkuminen : MonoBehaviour { int hypyt = 0; void Start () { Cursor.lockState = CursorLockMode.Locked; } void Update () { if (Input.GetAxis("Horizontal") != 0) { transform.position += transform.right * Input.GetAxis("Horizontal") / 4; } if (Input.GetAxis("Vertical") != 0) { transform.position += transform.forward * Input.GetAxis("Vertical") / 4; } if (Input.GetAxis("Mouse Y") != 0) { Camera.main.transform.eulerAngles -= Vector3.right * Input.GetAxis("Mouse Y"); } if (Input.GetAxis("Mouse X") != 0) { transform.eulerAngles += Vector3.up * Input.GetAxis("Mouse X"); } if (Input.GetMouseButtonDown(0)) { Cursor.lockState = CursorLockMode.Locked; } if (Input.GetButtonDown("Jump")&& hypyt!=2) { GetComponent<Rigidbody>().velocity = Vector3.up * 5; hypyt++; } } private void OnCollisionStay(Collision col) { hypyt = 0; } } |
3D-mallit Unityyn
Nyt olemme luoneet hyvin yksinkertaisen tasohyppelyn ohjaukset, Mutta pelikenttämme saattaa vaikuttaa hieman tyhjältä, joten lisätään blenderissä tekemämme kenttä peliin. Mikäli et ole vielä tehnyt kenttää, niin mene 3D-mallinnus materiaaleihin mistä löytyy ohjeet tähän.
Avaa project näkymä, ja klikkaa oikealla hiirellä sieltä, valitse ylin eli "Create", ja taas ylin eli "Folder" niin luomme uuden kansion mihin laitamme 3D mallimme. Nimeä kansio siten että tiedät mitä se sisältää, kuten "3D-mallit". Avaa kansio tuplaklikkaamalla siitä. Kun kansio on auki, avaa kansio missä sinulla on 3D-mallit, ja vedä ne kansiosta Project-näkymän päälle. Unitylla saattaa kestää hetki siirtää 3D-mallit, mutta kun se on valmis, näkyvät kaikki tekemäsi 3D-mallit project-näkymässä. Jos olet luonut 3D-malleille materiaalit blenderissä, niin unity luo oman kansion mihin se on lisännyt kaikki käyttämäsi materiaalit.
MeshCollider
Kun 3D-mallintamamme kenttä näkyy project-näkymässä, otamme siitä kiinni, ja vedämme sen Sceneen, jolloin koko 3D-mallinnus lisätään Sceneen. Varmistetaan että mallin position on (0,0,0). Jos katsomme Hierarchy-näkymään, näemme että 3D-mallimme nimi on siellä, ja että sillä saattaa olla nuoli nimen vieressä. Jos painamme nuolesta, avaa se meille välilehden jossa näkyy kaikki 3D-mallinnuksessa olevat objektit. Valitaan nämä kaikki, ja lisätään niihin MeshCollider.
MeshCollider toimii samoin kuin kaikki muutkin Colliderit ja saa objektin törmäämään muihin objekteihin. Erona muihin Collidereihin on se, että MeshCollider saa muotonsa objektilta jossa se on, eli jos meillä on 3D-mallinnettuna jokin hahmo tai muoto, saamme sille täysin samanmuotoisen colliderin tällä. Jos 3D-objektimme on kuitenkin erittäin monimutkainen, saattaa tämä olla pelilke raskas toteuttaa, minkä takia yleensä kannattaa turvautua tavallisiin collaidereihin.
MeshCollider ei toimi tälläisenään Rigidbodyn kanssa, vaan tällöin pitää painaa Convex päälle. Nyt meidän ei tätä tarvitse tehdä koska emme ole rigidbodya objekteihin lisäämässä. Convex luo objektin ympärille yksinkertaisemman kehikon, joka myös ottaa pois kaikki ontot kohdat. Esimerkiksi jos meillä on kuppi, ja laitamme siihen MeshColliderin, toimii se niinkuin kuppi. Mutta jos painamme Convex-kohdan päälle. tulee siihen ikään kuin kansi päälle.
Muuta meidän ei tarvitse 3D-malleille tehdä, ellemme halua muokkailla niitä Unityssa. Emme voi muuttaa suoraa objektien muotoja, mutta jos blender-projektissamme oli useita eri objekteja, voimme siirrellä ja monistaa niitä haluamamme mukaan. Tämä onnistuu painamalla haluamaamme objektia oikealla hiirellä ja valitsemalla duplicate, joka monistaa objektin, tai sitten copy pastella.
Koska lisäsimme kaikkiin objekteihin Mesh Colliderin, nyt jos testaamme peliä, pystymme kulkemaan kentässä, mutta jos olemme törmäämässä objektiin, menemmekin siitä läpi, ellemme pysähdy. Tämä korjataan koodin puolella. Koodimme näyttää siis tältä
using System.Collections; using System.Collections.Generic; using UnityEngine; public class liikkuminen : MonoBehaviour { // Use this for initialization int hypyt = 0; void Start () { Cursor.lockState = CursorLockMode.Locked; } // Update is called once per frame void Update () { if (Input.GetAxis("Horizontal") != 0) { transform.position += transform.right * Input.GetAxis("Horizontal") / 4; } if (Input.GetAxis("Vertical") != 0) { transform.position += transform.forward * Input.GetAxis("Vertical") / 4; } if (Input.GetAxis("Mouse Y") != 0) { Camera.main.transform.eulerAngles -= Vector3.right * Input.GetAxis("Mouse Y"); } if (Input.GetAxis("Mouse X") != 0) { transform.eulerAngles += Vector3.up * Input.GetAxis("Mouse X"); } if (Input.GetMouseButtonDown(0)) { Cursor.lockState = CursorLockMode.Locked; } if (Input.GetButtonDown("Jump")&& hypyt!=2) { GetComponent<Rigidbody>().velocity = Vector3.up * 5; hypyt++; } } private void OnCollisionStay(Collision col) { hypyt = 0; } } |
Lisäsin osalle koodia taustan. Tämä koodin osa liikuttaa pelaaja-hahmoamme. Meidän on siirrettävä se hieman toiseen paikkaan. Lisätään koodiin siis uusi methodi, void FixedUpdate(){}, ja lisätään värjätty koodi sen sisään, jolloin koodi näyttää tältä:
using System.Collections; using System.Collections.Generic; using UnityEngine; public class liikkuminen : MonoBehaviour { // Use this for initialization int hypyt = 0; void Start () { Cursor.lockState = CursorLockMode.Locked; } // Update is called once per frame void Update () { if (Input.GetAxis("Mouse Y") != 0) { Camera.main.transform.eulerAngles -= Vector3.right * Input.GetAxis("Mouse Y"); } if (Input.GetAxis("Mouse X") != 0) { transform.eulerAngles += Vector3.up * Input.GetAxis("Mouse X"); } if (Input.GetMouseButtonDown(0)) { Cursor.lockState = CursorLockMode.Locked; } if (Input.GetButtonDown("Jump")&& hypyt!=2) { GetComponent<Rigidbody>().velocity = Vector3.up * 5; hypyt++; } } private void OnCollisionStay(Collision col) { hypyt = 0; } void FixedUpdate() { if (Input.GetAxis("Horizontal") != 0) { transform.position += transform.right * Input.GetAxis("Horizontal") / 4; } if (Input.GetAxis("Vertical") != 0) { transform.position += transform.forward * Input.GetAxis("Vertical") / 4; } } } |
Ainut ero koodiimme on, että loimme FixedUpdate methodin ja lisäsimme liikkumisen siihen. Nyt hahmomme ei enää mene objektien läpi.
FixedUpdate
FixedUpdate on Update-methodin kaltainen, joka toistuu koko ajan pelin aikana. Toisin kuin Update-methodi joka toistuu joka framella, FixedUpdate toistuu fysiikan kanssa samaan aikaan. Tämä mahdollistaa sen, että nyt fysiikat pystyvät estämään esineiden läpi menemisen kun liikutamme pelaaja-hahmoa.
Nyt pystymme hyppimään pelissä eteenpäin, ja katsomaan kaikkialle. Valitettavasti, nyt meidän on myös mahdollista hypätä kentästä ulos, sekä mahdollisesti rotkoon pääsemättä ulos. Tämä on korjattava.
tRajat
Seinät meidän on helppo kentälle tehdä. Luodaan 4 (tai enemmän jos tarvitsee) objektia peliin, ja asetetaan kaikille BoxColliderit. Skaalataan objekteja niin paljon että saamme tarpeeksi suuret seinät peittämään kaikki ulospääsyt kentän reunoilta.
Samalla tavalla luomme kentälle myös pohjan. Luo uusi objekti, aseta BoxCollider ja skaalaa se niin suureksi että se peittää pohjan. Siirrä sitä niin että saamme sen olemaan juuri ja juuri rotkon pohjan yläpuolella kaikkialla. Voimme käyttää tähän myös useampaa objektia jos haluamme. Lisätään kaikkii objekteihimme myös tagi.
Tag
Tagit on Unityn tapa tunnistaa ryhmä objekteja, ja tehdä niille mitä haluamme. Tätä käytetään lähinnä koodissa tunnistamaan tämän ryhmän. Jokainen objekti on luotaessa "Untagged". Kun painamme tästä, näemme ryhmän Unityn omia tageja mitä se käyttää hyödykseen. Haluamme kuitenkin luoda kerättäville objekteillemme aivan oman taginsa, joten valitse valikosta "Add tag". Tämän pitäisi vaihtaa Inspector-näkymän aivan toisenlaiseksi. Nyt siinä pitäisi näkyä lista, jossa vain lukee "list is empty". Painetaan plussasta hieman tämän alapuolelta, ja nimetään tagimme, vaikka "restart". Palaa takaisin pohjan collidereihin, ja avataan sama tag valikko kuin aikaisemmin, paitsi että tällä kertaa aivan alimpana pitäisi olla juuri lisäämämme tag. Aseta tämä objektimme tagiksi. Nyt pystymme koodissa tarkistamaan osummeko pohjaan, johon emme saisi osua.
Palataan aijemmin tekemäämme koodiin. Luodaan aivan alkuun uusi Vector3 muuttuja, ja nimteään se vaikka "alkukohta" nimellä. Jätetään arvo kuitenkin tyhjäksi. Mennään Start methodiin, ja asetetaan siellä juuri tekemämme muuttujan arvoksi "transform.position". Tämän jälkeen etsitään OnCollisionStay-kohta, ja luodaan sen sisälle uusi if-lause. Kirjoitetaan siihen:
if(col.gameObject.tag=="restart"){ transform.position=alkukohta; } |
Tällä tavoin tarkistamme, onko törmäämämme objektin tag restart. Jos se on, niin pelaaja-objektimme palaa aivan samaan kohtaan kuin se oli pelin alussakin, eli peli ikään kuin restarttaa. Tehdään if-lausekkeelle myös else, johon laitamme OnCollisionStay-kohdassa jo olleen "hyppy=0;". Nyt meillä on rajat peliin asetettuna, ja voimme alkaa lisäilemään muitakin ominaisuuksiia.
Mikäli haluamme, voimme lisätä peliin kerättäviä esineitä. Voimme luoda tälläisiä esineitä blenderissä, ja vain tuoda ne unityyn. Lisää tälläinen kerättävä esine sceneen. Lisätään objektiin MeshCollider, ja painetaan ruksista, jossa lukee "Convex". Tämä aukaisee muut komponentin valinnat meille. Painetaan ruksista jossa lukee "IsTrigger", ja asetetaan se päälle.
Kuten olemme oppineet, että objektit voivat törmätä toisiinsa, on molemmissa oltava Collider-komponentti. Jotta voimme myös tarkistaa koodissa törmäämmekö objektiin, on molemmissa oltava Collider-komponentti. Jos haluamme, etteivät objektit kuitenkaan vaikuta toisiinsa, mutta haluamme koodissa saada tietää mikäli objektit törmäävät, asetetaan tällöin toinen objekteista Triggeriksi. Trigger siis tarkoittaa että emme fyysisesti törmää objektiin, vaan menemme siitä suoraa läpi, mutta tunnistamme koodissa milloin tämä tapahtuu.
Asetetaan kerättävälle objektillemme myös oma taginsa ja nimetään se vaikka "collect" nimellä. Palataan takaisin koodiin, ja luodaan taas yksi Unityn omista methodeista, nimeltä "void OnTriggerEnter(Collider col){}".
OnTrigger
On Trigger-methodit toimivat aivan samalla tavoin kuin On Collision-methoditkin, eli on olemassa OnTriggerEnter, OnTriggerStay ja OnTriggerExit joiden toimintaperjaatteet on samat kuin OnCollisionissakin.
Mitä eroa on OnTrigger ja OnCollision methodeilla on juuri se, että OnTrigger-methodissa toisen objektin Colliderin on oltava "IsTrigger". Myös toinen ero methodeilla on, nimittäin OnCollision antaa aina muuttujan Collision, joka sisältää siis kaikki tiedot törmäyksestä. OnTrigger-methodit sen sijaan antavat Colliderin johon objektimme törmäsi. Collider-muuttujassa on vain Colliderin tiedot, Collision-muuttujassa saamma tarkan sijainnin missä törmäys tapahtui.
Lisäsimme kerättävälle objektille tagin, joten tarkistetaan se samalla tavalla kuin aijemminkin. Jos tag on sama kuin haluamamme, niin kirjoitetaan methodin sisään koodi joka tuhoaa kerättävän objektin. Tämä onnistuu kirjoittamalla:
Destroy(col.gameObject); |
Eli nyt ohjelmamme uusi osa näyttää tältä:
void OnTriggerEnter(Collider col) { if(col.gameObject.tag=="collect"){ Destroy(col.gameObject); } } |
Nyt objektimme tuhoaa aina kerättävän esineen kosketuksesta. Nyt esineet tosin näyttävät tylsiltä vain leijuessaan ilmassa liikkumatta. Tämän voimme korjata tekemällä uuden scriptin. Eli luokaa uusi scripti "scriptit"-kansioon, ja nimetkää se vaikka "turn" nimellä. Lisätään tämä scripti kerättävään esineeseen, ja lähdetään sitten muokkaamaan sitä. Eli kaksoiklikatkaa scripti auki.
Kerättävä esine script
Nyt meille aukeaa toinen scripti joka on yhtä tyhjä kuin ensimmäinenkin aluksi. Tällä kertaa emme tarvitse Start-methodia, eli jos haluamme, voimme poistaa sen kokonaan scriptistä. Luodaan sitten kaksi float-muuttujaa, ja nimetään ne vaikka nimillä "kohta" ja "suunta". Anna kohta-muuttujalle arvo 0, ja suunta-muuttujalle arvo 0.00sk-st5f.
Luodaan Update-methodin sisään if-lauseke, joka tarkistaa onko kohta-muuttujan arvo yhtäsuuri tai suurempi kuin 0.025f. Jos on, annetaan suunta-muuttujan arvoksi -0.005f.
Luodaan if-lausekkeelle else if, joka tarkistaa, onko kohta-muuttujan arvo yhtäsuuri tai suurempi kuin -0.025f. Jos on, niin annetaan suunta-muuttujan arvoksi sittenkin 0.005f.
Tämän jälkeen plussataan kohta-muuttujaan suunta-muuttuja. Sitten transform.position asetetaan Vector3.up*kohta.
using System.Collections; using System.Collections.Generic; using UnityEngine; public class turn : MonoBehaviour { float kohta = 0; float suunta = 0.005f; void Update () { if (kohta >= 0.025f) { suunta = -0.001f; }else if (kohta <= -0.025f) { suunta = 0.001f; } kohta += suunta; transform.position += Vector3.up*kohta; } } |
Mitä koodimme nyt tekee, niin luomme aluksi kaksi muuttujaa, kohta-muuttujaan asetetaan kuinka paljon kerättävän tavaran kohta muuttuu. suunta-muuttujaan asetetaan kuinka nopeasti ja mihin suuntaan tavara tällä hetkellä liikkuu. If-lausekkeet tarkistavat onko kohta-muuttujan arvo muuttunut tarpeeksi, että suuntaa voidaan vaihtaa päinvastaiseen. Tämän jälkeen lisätään kohta-muuttujaan suunta-muuttuja joka muuttaa sitä tiettyyn suuntaan. Lopuksi tämä muutos lisätään objektin tämänhetkiseen kohtaan.
Mitä koodimme tekee, on luo kerättävälle esineelle pienen ylös-alas liikkeen, mikä tekee siitä paljon mielenkiintoisemman näköisen. Mikäli haluamme että objekti liikkuu ylemmäs ja alemmas, muutetaan if-lausekkeiden sulkujen sisällä olevia lukuja suuremmaksi. Mikäli haluamme nopeuttaa liikettä, muutetaan if-lausekkeiden aaltosulkeiden sisällä olevia lukuja suuremmaksi. Samat toimivat päin vastoin jos haluamme hidastaa esinettä.
Lisätään vielä Update-methodin loppuun "transform.eulerAngles += Vector3.up;" . Tämä lisää objektille pyörimisen tietyn akselin ympäri. Voit nopeuttaa tätä pyörimistä kertomalla Vector3.up jollakin, ja hidastaa pyörimistä jakamalla sen. Voimme vaihtaa pyörimissuuntaa laittamalla "+=" sijaan "-=", ja vaihtaa akselia vaihtamalla "Vector3.right" tilalle joko "Vector3.up" tai "Vector3.forward".
Nyt kerättävä asiamme on paljon houkuttelevamman näköinen. Voitte itse miettiä miten saisitte ohjelman laskemaan kuinka monta objektia se on kerännyt. Voit monistaa kerättäviä objekteja niin monta kuin haluatte copy-pastettamalla objektia, tai oikealla hiirellä klikkaamalla siitä Hierarchy-näkymässä, ja painamalla duplicate.
Pelimme ohjaukset ovat hyvät, mutta ne eivät silti ole täydelliset. Tällä hetkellä pystymme nimittäin hyppimään seiniä suoraa ylöspäin jos yritämme. Vaikka tämän voi ottaa ominaisuutena, niin minä näen sen bugina mikä pitää korjata. Tämä onnistuu parantelemalla hieman koodiamme.
Etsitään OnCollisionStay-kohta, jossa meillä on if-lauseke, ja tälle else-lauseke. Elsessä meillä on vain "hypyt=0;", joka restarttaa hyppäämisemme, mikäli vain osumme johkin objektiin. Tämä ei tarkista yhtään missä osumamme objekti on. Tämä korjataan luomalla uusi if-lauseke joka näyttää tältä:
if(Physics.Raycast(transform.position, Vector3.down, 2)) { hypyt=0; } |
Physics.Raycast
Physics.Raycast on tapa tarkistaa onko antamamme pisteen lähellä esineitä tietyssä suunnassa. Physics.RayCast:in sulkeiden sisään voimme laittaa monta eri muuttujaa, sillä se voi toimia monella tavalla.
Mitä me haluamme tehdä on, annamme ensin alkupisteen, jos tarkistus lähtee. Koska haluamme tarkistaa tämän pelaajaobjektin mukaan, laitamme siihen transform.position joka antaa meille pelaaja-objektin tämänhetkisen sijainnin.
Tämän jälkeen annamme suunnan josta haluamme tarkistaa onko siellä objekteja. Tämä onnistuu antamalla sille Vector3.down, joka antaa juuri alaspäin suuntaan menevän arvon.
Tämän jälkeen annamme numeron miltä etäisyydeltä Physics.Raycast katsoo esinettä. Asettamalla 2, tarkistamme suurin piirtein juuri objektin alapuolelta, onko esinettä siinä. Jos objektin alapuolella on esine, niin jatkamme if-lausekkeen aaltosulkeiden sisälle.
Aaltosulkeiden sisälle laitamme "hypyt=0", ja poistamme sen aijemmasta kohtaa. Nyt, jos ohjelma tunnistaa että pelaajan alla on maata, asettaa se hypyt nolllle, niin nyt pystymme hyppimään taas.
Nyt olemme luoneet tasohyppely pelin, jossa on kerättäviä asioita, sekä loistavat ohjaukset. Jos teillä on ehdottaa jotain mitä peliin lisäämme, niin kannattaa selvittää netistä miten tämän voi tehdä, tai kysyä ohjaajiltamme apua
Voimme luoda tasohyppelyyn vielä juoksunapin jos haluamme. Tämä onnistuu luomalla ensin float muuttujan samaan kohtaan kuin hypyt. Sille voidaan antaa nimeksi vaikka "juoksu". Asetetaan sen arvoksi 1. Sitten luodaan Update()-methodiin uusi if-lauseke ja sille else, joka menee näin:
if(Input.GetButton("Fire3"){ }else{ } |
Fire3 tarkoittaa vasenta shift-painiketta, sekä ohjaimessa omaa painiketta. Tästä tulee meille juoksu nappi. Lisätään if-lausekkeen sisään että juoksu onkin 1.5f. Laitetaan else-lausekkeen sisään ettää juoksu onkin taas 1. Eli nyt koodi näyttää tältä:
if(Input.GetButton("Fire3"){ juoksu=1.5f; }else{ juoksu=1; } |
Nyt siirrytään FixedUpdate()-methodiin, jossa liikutamme pelihahmoamme. 2 riviä jotka alkavat sanoilla transform.position, lisää näiden rivien aivan loppuun ennen puolipilkkua *juoksu .
float juoksu = 1; int hypyt = 0; // Use this for initialization void Start() { Cursor.lockState = CursorLockMode.Locked; } // Update is called once per frame void Update() { if (Input.GetAxis("Mouse Y") != 0) { Camera.main.transform.eulerAngles -= Vector3.right * Input.GetAxis("Mouse Y"); } if (Input.GetAxis("Mouse X") != 0) { transform.eulerAngles += Vector3.up * Input.GetAxis("Mouse X"); } if (Input.GetButtonDown("Jump") && hypyt != 2) { GetComponent<Rigidbody>().velocity = Vector3.up * 10; hypyt++; } if (Input.GetButton("Fire3")) { juoksu = 1.5f; }else { juoksu = 1; } } void FixedUpdate() { if (Input.GetAxis("Horizontal") != 0) { transform.position += transform.right * Input.GetAxis("Horizontal") / 6 * juoksu; } if (Input.GetAxis("Vertical") != 0) { transform.position += transform.forward * Input.GetAxis("Vertical") / 6 * juoksu; } } |
Nyt kun pelaamme peliä ja painamme vasenta shift-näppäintä liikkuessamme, kuljemme hieman kovempaa, ikään kuin juoksisimme.
Tämä materiaali on tuotettu Euroopan maaseudun kehittämisen maatalousrahaston tuella