1. Programmeerimise põhimõtted. C#-iga programmeerimise lühitutvustus

Selles peatükis tutvud põhilise programmeerimisega seonduvaga. See võib olla kohati põhjalikum ja isegi mitte seotud sellega, mida Unitys vaja läheb (C# konsooliaplikatsiooni süntaks), seega soovitan selle peatüki kinnistamisele mitte liiga kaua aega kulutada. Sellegipoolest, soovitan neil, kes programmeerimises vähem kogenud või uued C#  keeles või C- või Java-taolistes keeltes, luua konsooliaplikatsiooni, et vahetult rakendada ja kinnistada peatükis õpitut. Kui C#-i kohta tekib küsimusi, soovitan täpsema seletuse jaoks uurida lehekülge: https://www.w3schools.com/cs/index.php.

Konsooliaplikatsiooni loomise juhendid

Muutujate tüübid

Muutuja deklareerimine ja defineerimine

Muutujaid deklareeritakse/defineeritakse muutujatüübi võtmesõnaga ees. 

Näiteks 

float arv = 0.01f; 

int täisarv = 4; 

string sõna = "Mingi sõna...oot...". 

Muutuja defineerimine ja deklareerimine on erinevad tegevused. Deklareerimise käigus tekitatakse kindla tüübiga muutuja, millel ei ole väärtust

float muutuja;

Defineerimise käigus tekitatakse muutuja ja antakse sellele ka väärtus: 

float muutuja = 4.5f;.

Defineerida saab ka varasemalt deklareeritud muutujat: 

float muutuja;

...

muutuja  = 4.5f;

Kui programmis on mingi muutuja juba defineeritud/deklareeritud, ei pea muutuja tüübi võtmesõna sellega edasi kandma.

Arvulised muutujad

int ehk integer ehk täisarv.

double ja float on kaks eri tüüpi ujukomaarve. double saab olla kuni 15 kümnendkohaga ning float kuni 7 kümnendkohaga. Unitys kasutatakse arvuliste muutujatena enamasti float tüüpi muutujat. float tüüpi arvul C# koodis peab olema taga f (nt. float ujukomaarv = 4.5f) kui ei deklareerita täisarvu floatina

Boolean

bool ehk boolean ehk tõene-väär (true/false) tüüpi muutuja. Kasutatakse if-lausetes ja tsüklite tingimustes.

String ja character

string on ASCII karakteritest koosnev muutuja, mida koodis eraldatakse jutumärkidega: string minuTekst = "Tekst";

char on üksikust ASCII sümbolist koosnev muutuja mida eraldatakse ülakomadega '': char minuSümbol = 'c';

Reastused ja järjendid

Kui on tarvis kindlat tüüpi muutujaid kompaktselt avaldada, saab deklareerida järjendid võtmesõnadega int[], float[], bool[] jne.

Lisaks neile on C#-is olemas ka List<T> tüüpi muutuja. Näiteks List<int> on võtmesõnaks täisarvude nimekirja loomisel. Listil on järjendi üle eelis mugavuse poolest: List<T> jaoks töötab palju kompaktseid funktsioone nagu .Add() ja .Count(), mille funktsionaalsust järjendite puhul saab korrata ainult tsüklite kaudu.  

Klassid, objektid ja konstruktorid

https://www.w3schools.com/cs/cs_oop.php

Klassid on iseloodud andmekogude mallid, mis saavad koosneda erinevatest andmetüüpidest.

Objektid on klassi põhjal instantseeritud (instantiated) andmekogud. 

Süntaks

Koodiridade asukoht ja väljanägemine 

(peaaegu) iga koodirida lõpetatakse C#-is ja paljudes teistes programmeerimiskeeltes semikooloniga; 

Enamus koodist on nurgeliste sulgude { } sees klassi või meetodi skoobis. See, mis ei ole nurgeliste sulgude sees on kas viide teegile (kui on võtmesõna using ees see on teegi/ library/namespace-ile ligi pääsemiseks) või vigane kood.

Kommenteerimine

// on C#-is ja paljudes teistes sümbolitekombinatsioon, mis muudab rea kommentaariks, st see ei ole enam loetav kompilaatorile ning sinna võib igasuguseid süntaksiga mitteühtivaid märkmeid kirjutada. /* ... */ on viis "kommentaaristada" mitu rida korraga.

Matemaatilised tehted

Liitmine ja lahutamine käivad sümbolitega + ja - ning neid sümboleid ei tohi kasutada nimedes. 

Korrutamine ja jagamine käivad sümbolitega * ja /.

int a = 1:

int b = 2;

int summa;

summa = a + b;

int korrutis = a*b;

int jagatis = a/b; // väljundis jagatis annaks vastuseks "0", kui see oleks float, siis oleks vastus "0.5"

a jagatud b-ga on eelmise näite puhul 1-st väiksem ja pole täisarv. Sel juhul ümarduvad int-tüüpi muutujad alla ehk nulliks.

Muutujate nimetamine

Iga muutuja nimi peab olema üks sõna või kokkukirjutatud fraas

(Pro Tip: Unitys tasub muutujaid mille nimi oleks muidu mitmeosaline kirjutada järgmiselt: int mitmeosalineFraas; float suurVõiVäikeArv; , sest Unity Editori Inspectori kompileerija teeb need eraldi sõnadeks)

Muutuja peab koosnema tuntumatest ascii karakteritest ja ei tohi alata arvuga ega koosneda tehete sümbolitest.

Järjendi ja reastuse deklareerimine ja defineerimine

Järjendeid defineeritakse järgnevalt: 

int[] täisArvuJärjend = {1, 5, 50}

int esimeneLiige = täisArvuJärjend[0];

int viimaneLiige = täisArvuJärjend[2]; 

Programmeerimises on sellistel järjenditel indeksid, mille kohaselt kohal "0" asub esimene liige. See tähendab, et kui tahta järjendi esimest või kolmandat elementi tuleb kasutada indeksit 0 ja 2:

Reastusi (C#-is List<T>) deklareeritakse ja defineeritakse järgnevalt:

 List<int> reastus = new List<int>();

List<T> on C# teegist eraldi andmeklass ning seda tuleb deklareerida konstruktorina (st meie mõistes lihtsalt selle veidra süntaksiga) nagu eelnevas näites on seda tehtud.

reastus.Add(2); reastus.Add(5);

int sisend = Console.ReadLine() // Console.ReadLine() on konsooliaplikatsiooni API-is sisendit lugev funktsioon

reastus.Add(sisend):

Viimased kaks rida võtavad konsoolist sisendi, mis võiks täisarv olla, ja lisavad selle reastusesse.

Console.WriteLine(); // Console.WriteLine() on konsooliaplikatsiooni API-is väljundit kirjutav funktsioon

Sümbolite (characters) ja sõnede (strings) defineerimine ja deklareerimine ning nendega seonduvad tehted

string sõna1 = "karu";

char ja_märk = '+';

string sõna2 = "jänes";

string fraas = sõna1 + ' ' + ja_märk + ' ' + sõna2;

Console.WriteLine(fraas);

Viimase rea väljundiks "karu + jänes"

if-lause

Kui tingimus on täidetud siis teostub nurksulgudes olev koodilõik. if-lausesse saab panna palju tingimusi. else-lause tähistab kõiki muid võimalusi, mida pole esimeses if-lause tingimuses ( järgnevas näites else tähistab võimalusi x<y ja x=y (x <= y)). On ka else if-lause, mis hõlmab, nagu else lause, kõiki esimese tingimuse vastasvõimalusi, kuid milles saab täpsustada mõnda uut tingimust.

Kui mingi boolean peab olema if-lause tingimuses tõene, piisab (tingimus == true asemel) selle boolean muutuja nime kirjutamisest if-lause tingimusse ( if(tingimus) {...} ). Kui tingimuseks on mingi bool muutuja väär olemine, võib (tingimus == false) asemel hüüumärgi muutuja nime ette kirjutada ( if(!tingimus) {...}). Kui tingimus on mitmeosaline, nagu näiteks "A ja B" või "A või B" siis kasutatakse C#-is ja paljude teiste keelte süntaksis vastavalt sümboleid && ja ||.

Tsüklid

while-tsükkel

while-tsükkel on tsükkel, mis teostab oma sees oleva koodilõigu nii kaua, kui tingimus on kehtiv. See on põhimõtteliselt mitmekordne if-lause. i++; on sama mis i += 1;.

Täisarvuline muutuja i on algselt 0 ning iga kord kui koodilõik teostub, suureneb see ühe võrra. Kokku toimub see viis korda (0-st 4-ni). Kui while-tsükli lauses oleks i <= 5, toimuks see 6 korda.

for-tsükkel

for-tsükkel on tsükkel, mis teostab oma sees oleva koodilõigu nii kaua, kuni oma argumendis tingimus, mis for-tsükli argumendis antud koos muutumisreegliga ehk iteratsiooniga, muutub kehtetuks.

Sellel for-tsüklil on täpselt sama väljund, mis eelnevalt näidatud while-tsüklil.

C# programmi anatoomia (konsooliaplikatsioon)

Näide for-tsüklist reastuste ja järjendite menetlemisel konsooliaplikatsioonis: see programm kirjutab täisarvude järjendi ümber listiks ehk reastuseks ja väljastab selle elemendid foreach-tsükliga. foreach-tsükkel on tsükkel, mis siseneb järjendisse või reastusse võtmesõnaga in ja teostub nii mitu korda, mitu elementi on järjendis. Sellel tsüklil on ligipääs elemendile (selles näites int a).

See näide on konsooliaplikatsooni fail. namespace HelloWorld on nimi, mille panin konsooliaplikatsiooni peamisele failile. Konsooliaplikatsioon teostab namespace HelloWorld alla kuuluvat klassi Program meetodit Main. Maini kõrvale klassi Program skoopi (järgmine alapeatükk on Meetoditest). 

Konsooliaplikatsioon loomise õpetus. (see on link VS Code'i kasutajatele)

Meetodid (funktsioonid)

C#-is meetodid (mujal funktsioonid), on üldistatud kompaktsust tagavad koodilõigud, mis saavad võtta sisendiks parameetreid ja anda väljundiks uusi muutujaid. Neid defineeritakse järgmiselt:

static [andmetüüp] Meetod([andmetüüp1] parameeter1, [andmetüüp2] parameeter2)

{

// toimub mingi koodimine

[andmetüüp] väljastatav = ...;

return väljastatav;

}

Meetodil ei pea olema argumente ning void-tüüpi meetod ei väljasta mingit väärtust. Näide erinevatest meetoditest konsoolaplikatsioonis:

Selle programmi väljund konsoolis on:

töötab meetod 1

1

1.5

OOP: Skoop, hierarhia ja klasside/komponentide instantsid (instances of objects)

https://www.w3schools.com/cs/cs_oop.php

Unity esmaseks kasutamiseks pole otseselt vaja teada, kuidas luua konstruktoreid ja klasse. Küll aga oleks mõistlik saada üldine kokkupuude OOP-st (objektorienteeritud programmeerimisest) ning täpsemalt hierarhiast ja klassidest ning nende omadustest ja Unity C# API-st (application programming interface).

Mida Unity kasutuseks vaja teada? Näide Unity C# API GameObject põhjal ( boldis on read kopeeritud Unity C# skriptist):

GameObject mingiObjekt; 

See on põhiline stseenis oleva objekti külge kinnituv klass/konstruktor

Objektile kuuluvatele alamobjektidele viitamine käib punktiga (.), klass Transform on hierarhiliselt GameObjecti "all"/ GameObjecti kaudu kättesaadav.

Transform mingiObjektiTransform = mingiObjekt.transform; 

Nüüd on meil mingiObjekti Transform. Transformi "all" on muutujad Scale ja Position, mis on Vector3 klassi //suurused ning Rotation, mis on Quaternion klassist.

Lehelt https://docs.unity3d.com/ScriptReference/GameObject.html properties alt paistab, et "properties" (põhimõtteliselt mingi klassipunktiga ligipääsetavad alamobjektid) all on muutujad Tag ja Name (märksõna? ja nimi), mis on kättesaadavad järgnevalt (märksõna ja nimi on Unity Editoris muudetavad string-tüüpi muutujad):

string  märksõna = mingiObjekt.tag;

string nimi = mingiObjekt.name; 

Tulles tagasi transform.positioni juurde, oletame et tahame objekti x-koordinaati teada ja muuta. https://docs.unity3d.com/ScriptReference/Transform.html 

float koordinaatX = mingiObjektiTransform.position.x; //alternatiivselt float koordinaatX  = mingiObject.transform.position.x;

Selleks et x-koordinaati muuta, ei saa teha nii, et mingiObjektiTransform.position.x = uusKoordinaatX;, sest mingiObjektiTransform.position.x  väljastab koopia Vector3 Position x-väärtusest, mis ei ole modifitseeritav suurus. Modifitseeritav on aga suurus Vector3 ehk position ise, seega saab teha järgnevalt:

float uusKoordinaatX = 5f;

mingiObjekt.transform.position = new Vector3(uusKoordinaatX, mingiObjekt.transform.position.y, mingiObjekt.transform.position.z); // ainult x-koordinaat on erinev

Instantsid ehk instantseeritud (ja defineeritud) klassid

Klasside malli koopiaid nimetan edaspidiselt instantsideks (inspireeritud ingl sõnast instance; vabandan estonglishi pärast, kuid paremat alternatiivi ei tea). Instantsid on mittestaatilise klassi (non-static class) deklareeritud ja modifitseeritud koopia. Unitys on näiteks kasutusel Vector3, mis on klass, mis hoiustab kolme float väärtust. Klasse defineeritakse konstruktorina ehk uhiuue instantsi defineerimisel kasutatakse võtmesõna new ning järgitakse kindlat süntaksit:

Vector3 minuVektor = new Vector3(argument_x, argument_y, argument_z);

//võib ka tühjana deklareerida antud klassi (siis väärtused vaikimisi 0):

Vector3 minuTühiVektor = new Vector3();

Lisaks arvude hoiustamisele on sellel klassil omad funktsioonid, mis on punkti ja suffiksiga ligipääsetavad. Üks neist on Vector3 puhul funktsioon magnitude, mis väljustab float väärtuse, mis representeerib nende arvude moodustatud vektori pikkust Pythagorase teoreemi läbi.

float minuVektoriPikkus = minuVektor.magnitude;

Vector3 on mittestaatiline ehk sellel on mitmeid instantse. Sisuliselt tähendab see seda, et Unity mängus osad Vector3 instantsid viitavad kiirustele, osad koordinaatidele ja nii edasi ning pole ühte Vector3-e, mis omab universaalselt ühtesid väärtusi. Sellele vastanduv on C#-is static class. Static class ehk staatiline klass on C#-is objekt, mida ei saa instantseerida ning millel saavad olla seetõttu ühed väärtused. Neile võib saada ligipääseda (kui tegemist on public static classiga) ning nende väärtuseid programmis kasutada ja modifitseerida, aga nende väärtuste modifitseerimine muudab siis selle staatilise klassi väärtuseid kõigi jaoks. Näiteks praegune kellaaeg või skoor (single player mängus) võivad olla asjad, mida hoiustada staatilises klassis.

Näide objekt-orienteeritud programmeerimise instantside olemusest.

OLULINE: value-tüüpi muutujad vs. reference-tüüpi muutujad

Oluline on eristada viitetüüpi ja väärtuse tüüpi muutujaid. Väärtuse tüüpi muutujate puhul, kui ma omistan ühe muutuja väärtuse teisele ja modifitseerin teise väärtust, ei muutu esimese muutuja väärtus vastavalt. Viitetüüpi muutujate puhul, kui ma omistan ühe muutuja väärtuse teisele ja modifitseerin teise väärtust, muutub esimese muutuja väärtus vastavalt.

NB! väljundi all pidasin silmas muutujat obj1.arv.

Kuvatõmmis Scripting API GameObjecti manuaalist GameObject "Properties" sektsioonist

"Properties" pole (tavaliselt) modifitseeritavad suurused, vaid pigem "get-set" väärtused.

OOP: Skoop ja hierarhia kokkuvõte

See sektsioon võis ilmselt tunduda kõige segadusttekitavam, kuid selle mõte oli:

1) tuua näiteid Unity C# teekides/librarytes/API-s olevatest olulisematest klassidest (GameObject, Transform)

2) näidata OOP eripärasid Unity kontekstis

3) julgustada ametliku dokumentatsiooni https://docs.unity3d.com/ScriptReference/ kasutamist

Põhiline Unity C# skriptimise tutvustust tuleb järgnevates peatükkides.