Șef lucrări dr. Ing. Ionuț Cristian Resce anu Septembrie 2017 CRAIOVA ii UNIVERSITATEA DIN CRAIOVA FACULTATEA DE AUTOMATICĂ, CALCULATOARE ȘI… [604437]

UNIVERSITATEA DIN CRAIOVA
FACULTATEA DE AUTOMATICĂ, CALCULATOARE ȘI
ELECTRONICĂ

DEPARTAMENTUL DE AUTOMATICĂ, ELECTRONICĂ ȘI
MECATRONICĂ

PROIECT DE DIPLOMĂ
Alexandru – Ion Oprean

COORDONATOR ȘTIINȚIFIC
Șef lucrări dr. Ing. Ionuț Cristian Resce anu

Septembrie 2017
CRAIOVA

ii

UNIVERSITATEA DIN CRAIOVA
FACULTATEA DE AUTOMATICĂ, CALCULATOARE ȘI
ELECTRONICĂ

DEPARTAMENTUL DE AUTOMATICĂ, ELECTRONICĂ ȘI
MECATRONICĂ

Realizarea unei camere labirint în Realitate Virtuală
Alexandru – Ion Oprean

COORDONATOR ȘTIINȚIFIC
Șef lucrări dr. Ing. Ionuț Cristian Resceanu

Septembrie, 2017
CRAIOVA

iii
DECLARAȚIE DE ORIGINALITATE

Subsemnatul ALEXANDRU – ION OPREAN , student: [anonimizat], Calculatoare și Electronică a Universit ății din
Craiova, certific prin prezenta că am luat la cunoșt ință de cele prezentate mai jos și că î mi asum, în
acest context, originalita tea proiectului meu de licență :
 cu titlul Realizarea unei camere labirint în Realitate Virtuală ,
 coordonată de Șef lucrări dr. Ing. Ionuț Cristian Resceanu ,
 prezentată în sesiunea Septembrie, 2017 .
La elaborarea proiectului de licență, se consideră plagiat una dintre următoarele acțiuni:
 reproducerea exactă a cuvinte lor unui alt autor, dintr -o altă lucrare, în limba română sau prin
traducere dintr -o altă limbă, dacă se omit ghilimele și referința precisă,
 redarea cu alte cuvinte, reformularea prin cuvinte proprii sau rezumarea ideilor din alte
lucrări , dacă nu se ind ică sursa bibliografică,
 prezentarea unor date experimentale obținute sau a unor aplicații realizate de alți autori fără
menționarea corectă a acestor surse,
 însușirea totală sau parțială a unei lucrări în care regulile de mai sus sunt respectate, dar ca re
are alt autor.
Pentru evitarea acest or situații neplăcute se recomandă:
 plasarea într e ghilimele a citatelor directe și indicarea referinței într -o listă corespunzătoare la
sfărșitul lucrării,
 indicarea în text a reformulării unei idei, opinii sau te orii și corespunzător în lista de referințe
a sursei originale de la care s -a făcut preluarea,
 precizarea sursei de la care s -au preluat date experimentale, descrieri tehnice, figuri, imagini,
statistici, tabele et caetera ,
 precizarea referințelor poate fi omisă dacă se folosesc informații sau teorii arhicunoscute, a
căror paternitate este unanim cunoscută și acceptată.

Data , Semnătura candidat: [anonimizat] ,

iv

UNIVERSITATEA DIN CRAIOVA
Facultatea de Automatică, Calculatoare și Electronică

Departamentul de Automat ică, Electronică și Mecatronică
Aprobat la data de
…………………
Șef de departament,
Prof. dr. ing.
Emil PETRE

PROIECTUL DE DIPLOMĂ

Numele și prenume le student: [anonimizat]/ -ei:
Alexandru -Ion Oprean

Enunțul temei:

Realizarea unei camere labirint în Realitate Virtuală

Datele de pornire:

Unity3D, C#, Realitate Virtuala

Conținutul proiectului :

1. Introducere
2. Tehnologii folosite ( Limbajul C# si Unity3D )
3. Proiectarea unei aplica ții în realitate virtual ă
4. Prezentarea aplica ției
5. Concluzii

Material grafic obligatoriu:
Imagini și capturi de ecran

Consultații:
Periodice
Conducătorul științific
(titlul, nume și prenume, semnătura): Șef lucrări dr. Ing. Io nuț Cristian Resceanu

Data eliberării temei :

Termenul estimat de predare a
proiectului :

Data predării proiectului de către
student și semnătura acestuia:

v

UNIVERSITATEA DIN CRAIOVA
Facultatea de Automatică, Calculatoare și Electronică

Depar tamentul Automat ică, Electronică și Mecatronică

REFERATUL CONDUCĂTORULUI ȘTIINȚIFIC

Numele și prenumele candida tului/ -ei: Alexandru – Ion Oprean
Specializarea: Ingineria Sistemelor Multimedia
Titlul proiectului : Realizarea unei camere labirint în R ealitate Virtuală
Locația în care s -a realizat practica de
documentare (se bifează una sau mai
multe din opțiunile din dreapta): În facultate □
În producție □
În cercetare □
Altă locație: [se detaliază ]

În urma analizei lucrării candidatului au fost constatate următoarele:
Nivelul documentării Insuficient
□ Satisfăcător
□ Bine
□ Foarte bine

Tipul proiectului Cercetare
□ Proiectare
□ Realizare
practică □ Altul
[se detaliază ]
Aparatul matematic utilizat Simplu
□ Mediu
□ Complex
□ Absent

Utilitate Contract de
cercetare □ Cercetare
internă □ Utilare
□ Altul
[se detaliază ]
Redactarea lucrării Insuficient
□ Satisfăcător
□ Bine
□ Foarte bine

Partea grafică, desene Insuficient ă
□ Satisfăcătoare
□ Bună
□ Foarte bună

Realizarea
practică Contribuția autorului Insuficientă
□ Satisfăcătoare
□ Mare
□ Foarte mare

Complexitatea
temei Simplă
□ Medie
□ Mare
□ Complexă

Analiza cerințelor Insuficient
□ Satisfăcător
□ Bine
□ Foarte bine

Arhitectura Simplă
□ Medie
□ Mare
□ Complexă

Întocmirea
specificațiilor
funcționale Insuficientă
□ Satisfăcătoare
□ Bună
□ Foarte bună

vi
Implementarea Insuficientă
□ Satisfăcătoare
□ Bună
□ Foarte bună

Testarea Insuficientă
□ Satisfăcătoare
□ Bună
□ Foarte bună

Funcționarea Da
□ Parțială
□ Nu

Rezultate experimentale Experiment propriu
□ Preluare din bibliografie

Bibliografie Cărți
Reviste
Articole
Referințe web

Comentarii
și
observații

În concluzie, se propune:

ADMI TEREA PROIECTULUI
□ RESPINGEREA PROIECTULUI

Data, Semnătura conducătorului științific,

vii
REZUMATUL PROIECTULUI
Aceasta tem ă își dore ște să exemplifice f oarte u șor cum în viitor realitatea virtual ă poate s ă aduc ă un
plus educa ției teoretice prin transpunerea elevului sau a studentului într-o dimensiune în care
simularea practic ă nu are limite. Utilizarea acestei aplica ții își doreste dezvoltarea capacit ății de
gândire a utilizatorului prin rezolvarea unor ghi citori (puzzle) și transpunerea acestuia într-o
dimensiune virtual ă în care el este protagonistul pove știi din cadrul aplica ției.

Obiective:
– Obiectivul principal este acela de a dezvolta capacitatea de gândire și rezolvare a
unor probleme logice prin transpunerea acestuia într-o lume virtual ă;
– Un alt obiectiv este crearea unei experien țe autentice și cât mai real ă prin
integrarea utilizatorului într-o poveste al c ărei protagonist este;
– Un ultim obiectiv , dar probabil cel mai important din punct de vedere tehnologic,
este împingerea limitelor pe care realitatea virtual ă le are la momentul actual prin
implementarea unor algoritmi unici de optimizare;

În cadrul celor 5 capitole existente se vor prezenta te hnologiile folosite, crearea unei arhitecturi de
realitate virtual ă și prezentarea componentelor aplica ției.
Aplica ția este realizat ă în realitate virtual ă folosind un dispozitiv mobil, ceea ce dup ă sine atrage
limit ări ca timp de rulare, putere de procesa re limitat ă, randare dubl ă pe un procesor grafic foarte slab
în compara ție cu dispozitivele de realitate virtual ă folosite pe un computer personal.
De asemenea aplica ția are integrat suport pentru mai multe limbi, ad ăugarea unei noi limbi f ăcându-se
prin t raducerea cuvintelor existente într-un fi șier de tip Excel, aplica ția urm ând ca apoi s ă adauge
limba automat. Majoritatea set ărilor, inclusiv a pove știi pe care utilizatorul o are în cadrul aplica ției,
se poate realiza tot prin schimbarea con ținutului unor fișiere de tip text, întreaga aplica ție fiind
construit ă cu scopul de a fi updatat ă și de c ătre non -programatori.

viii
CUPRINSUL
1 INTRODUCERE ………………………….. ………………………….. ………………………….. ………… 12
1.1 SCOPUL ………………………….. ………………………….. ………………………….. …………………. 12
1.2 MOTIVAȚIA ………………………….. ………………………….. ………………………….. ……………. 12
2 TEHNOLOGII FOLOSITE ………………………….. ………………………….. …………………….. 13
2.1 PLATFORMA UNITY 3D ………………………….. ………………………….. …………………………. 13
2.1.1 De ce Unity3D? ………………………….. ………………………….. ………………………….. ….. 14
2.1.2 Realitatea virtuală în Unity3D ………………………….. ………………………….. ………….. 15
2.2 LIMBAJUL C# ………………………….. ………………………….. ………………………….. …………. 17
2.2.1 Relatia dintre C# și arhitectura .NET ………………………….. ………………………….. … 18
2.2.2 Principalele caracte ristici ale arhitecturii .NET ………………………….. ………………. 19
3 PROIECTAREA UNEI APL ICAȚII ÎN REALITATE VIRTUALĂ ………………….. 21
3.1 ARHITECTURA APLICAȚIE I ………………………….. ………………………….. …………………….. 21
3.1.1 MVC și MVVM ………………………….. ………………………….. ………………………….. ……. 21
3.1.2 Singleton ………………………….. ………………………….. ………………………….. ……………. 22
3.1.3 Crearea interfeței unei aplicații de realitate virtuală ………………………….. ……….. 23
3.1.4 Dezvoltarea logică a unei aplicații de realitate virtuală ………………………….. …… 26
3.1.5 Crearea legăturilor dintre inter față și logica aplicației ………………………….. ……. 32
3.1.6 Exportarea unui proiect de Realitate Virtuală ………………………….. …………………. 34
3.2 OPTIMIZAREA APLICAȚIEI ………………………….. ………………………….. …………………….. 37
3.2.1 Optimizare memorie ………………………….. ………………………….. ………………………… 38
3.2.2 Optimizarea proceselor ………………………….. ………………………….. ……………………. 39
3.2.3 Optimizarea proce selor de randare ………………………….. ………………………….. …… 41
4 PREZENTAREA APLICAȚI EI ………………………….. ………………………….. ………………. 44
4.1 INTRODUCERE ………………………….. ………………………….. ………………………….. ………… 44
4.2 CAMERA ………………………….. ………………………….. ………………………….. ………………… 44
4.3 POVE STEA DIN SPATELE JOC ULUI ………………………….. ………………………….. …………… 46
4.4 REZOLVAREA GHICITORIL OR ………………………….. ………………………….. …………………. 47
4.4.1 Statuia de porțelan ………………………….. ………………………….. ………………………….. . 48
4.4.2 Cele 2 vaze și Martin Luther King Jr. ………………………….. ………………………….. … 49

ix
4.4.3 Cartea de istorie ………………………….. ………………………….. ………………………….. …. 50
4.4.4 Lampa ascunsă ………………………….. ………………………….. ………………………….. …… 52
4.5 POTENȚIALUL DE DEZVOL TARE ………………………….. ………………………….. ……………… 52
5 CONCLUZII ………………………….. ………………………….. ………………………….. ………………. 54
6 BIBLIOGRAFIE ………………………….. ………………………….. ………………………….. ………… 55
7 REFERINȚE WEB ………………………….. ………………………….. ………………………….. ……… 56
A. CODUL SURSĂ ………………………….. ………………………….. ………………………….. ………….. 57
C. CD / DVD ………………………….. ………………………….. ………………………….. ……………………. 87

x
LISTA FIGURILOR
FIGURA 1 – Editorul Unity ………………………….. ………………………….. ………………………….. ………………………… 11
FIGURA 2 – Comparație între Unity și Unreal ………………………….. ………………………….. ………………………….. .12
FIGURA 3 – Vedere camer ă în realitate virtual ă ………………………….. ………………………….. ………………………… 13
FIGURA 4 – Setări export Unity ………………………….. ………………………….. ………………………….. ………………….. 14
FIGURA 5 – Selectare setare pentru realitate vir tuală ………………………….. ………………………….. …………………. 15
FIGURA 6 – Prezentare generală Vis ual Studio ………………………….. ………………………….. …………………………. 15
FIGURA 7 – Procesul de compilare î n .NET C# ………………………….. ………………………….. …………………………. 17
FIGURA 8 – Schemă MVC ………………………….. ………………………….. ………………………….. …………………………. 19
FIGURA 9 – Schemă MVVM ………………………….. ………………………….. ………………………….. ……………………… 19
FIGURA 10 – Clasă tip Singleton ………………………….. ………………………….. ………………………….. ………………… 20
FIGURA 11 – Prezentare cameră și mediu înconjurător ………………………….. ………………………….. ………………. 21
FIGURA 12 – Prezentare interior cameră ………………………….. ………………………….. ………………………….. ……… 22
FIGURA 13 – Optimizare grafică folosind Occlusion Cull ing ………………………….. ………………………….. ……… 23
FIGURA 14 – Schemă arhitectură pentru mișcare ………………………….. ………………………….. ………………………. 24
FIGURA 15 – Schemă cu arhitectura jocului ………………………….. ………………………….. ………………………….. ….25
FIGURA 16 – Sincr onizarea jocului cu GameManager ………………………….. ………………………….. ……………….. 26
FIGURA 17 – Înregi strarea ferestrelor de tip p op-up într -o listă de așteptare ………………………….. ………………. 27
FIGURA 18 – Salvarea ș i încărcarea datelor ………………………….. ………………………….. ………………………….. …..28
FIGURA 19 – Legăturile obiectelor din joc cu logica ………………………….. ………………………….. ………………….. 29
FIGURA 20 – Crearea de setări predefinite în Unity ………………………….. ………………………….. …………………… 30
FIGURA 21 – Setări export Android ………………………….. ………………………….. ………………………….. …………….. 31
FIGURA 22 – Alegerea setărilor corecte pentru crearea unui ex port pentru realitate virtuală …………………….. 32
FIGURA 22.1 – Alegerea versiunii corecte pentru crearea unui export pentru realitate virtuală ………………….. 33
FIGURA 23 – Folosirea scenelor multiple ………………………….. ………………………….. ………………………….. …….. 34

xi
FIGURA 35 – Pagină sta tistici Unity ………………………….. ………………………….. ………………………….. ……………. 35
FIGURA 36 – Prezentare Texture Packer ………………………….. ………………………….. ………………………….. ……… 36
FIGURA 37 – Aplicarea opț iunii statice asupra obiectelor ………………………….. ………………………….. …………… 36
FIGURA 38 – Schemă optimizare grafică ………………………….. ………………………….. ………………………….. ……… 37
FIGURA 39 – Model ran dare cameră joc dupa optimizare ………………………….. ………………………….. …………… 38
FIGURA 40 – Exemplu cod optimizare cameră în funcție de procesorul grafic ………………………….. …………… 39
FIGURA 41 – Prezentare cameră ………………………….. ………………………….. ………………………….. …………………. 40
FIGURA 42 – Cifrul de la ușa de ieșire din clădir e ………………………….. ………………………….. ……………………… 41
FIGURA 43 – Prezentare poveste (partea – 1) ………………………….. ………………………….. ………………………….. ..42
FIGURA 43.1 – Prezentare poveste (partea – 2) ………………………….. ………………………….. …………………………. 42
FIGURA 43.2 – Prezentare poveste (par tea – 3) ………………………….. ………………………….. …………………………. 43
FIGURA 43.3 – Prezentare poveste (partea – 4) ………………………….. ………………………….. …………………………. 43
FIGURA 44 – Budha de porțelan, scoatere în evidență de selecția utili zatorului ………………………….. ………….. 44
FIGURA 44.1 – Descoperirea primei cifre după rotația statuii la 360 de grade ………………………….. ……………. 44
FIGURA 45 – Cea de-a doua cifră în tablou ………………………….. ………………………….. ………………………….. …..45
FIGURA 46 – Prezentare c arte interactivă din bibliotecă ………………………….. ………………………….. …………….. 46
FIGURA 46.1 – A treia cifră pe cartea de istorie ………………………….. ………………………….. ………………………… 46
FIGURA 47 – Lampa î mpreuna cu cea de -a patra cifra ………………………….. ………………………….. ……………….. 47
FIGURA 48 – Privire clădire de ansamblu ………………………….. ………………………….. ………………………….. …….. 48

1 INTRODUCERE
1.1 Scopul
Realizarea acestei aplica ții s-a făcut cu scopul de a crea o exp erien ță unică pentru utilizator,
care în momentul imersiunii sale în realitatea virtual ă acesta s ă intre în pielea personajului at ât de
bine, încât realizarea ghicitorilor s ă nu devin ă un exerci țiu obosi tor, ci din contr ă să fie o experien ță
plăcută sub forma unui joc.
1.2 Motivația
În anul 2016 a fost primul an fiscal al realit ății virtual e, iar încasările au fost de 1.8 miliarde
de dolari (o sum ă mică în compara ție chiar și cu pia ța jocurilor pe dispozitive mobile). De și este la
început și înainteaz ă greu din cauza costurilor foarte mari at ât a componentelor scumpe c ât și a
produc ției de aplica ții care este mult mai scump ă decât în cadrul unei aplica ții normale, cu timpul
realitatea virtual ă își va atinge po tențialul, iar ceea ce o dat ă se credea c ă este doar o idee va fi posibil
prin intermediul aplica țiilor de acest tip.
Motiva ția principal ă a fost aceea de a experimenta ceea ce înseamn ă crearea unei aplica ții de
realitate virtual ă. Contruirea unei astfel d e aplica ții este cu adevarat o provocare, procedeele de
programare și arhitectur ă sunt total diferite, modul de interac țiune este unul unic, iar tot ceea ce se
realizeaz ă trebuie s ă fie compatibil cu limit ările tehnologiei.
După experimentare, o dorin ță și mai mare a fost aceea de a vedea care sunt limitele acestei
tehnologii: care este puterea maxim ă de procesare, care este timpul maxim de a sta în lumea virtual ă
fără a apărea oboseala ochilor, care este num ărul maxim de vertecsi pe care procesorul grafic îl poate
randa și nu în ultimul r ând cum se face navigarea într-o astfel de lume. Dup ă ce s-au atins limitele din
aceste puncte de vedere, urm ătorul obiectiv a fost acela de a depa și aceste limite. Asfel am creat o
serie de proceduri și algoritmi care s ă mă ajute în dep ășirea acestor limite (vezi: Procedee de
optimizare).

13
2 TEHNOLOGII FOLOSITE
2.1 Platforma Unity3D
Unity 3D este o platform ă folosit ă pentru crearea de jocuri 3D, dar cu care se pot creea f ără
nici o problem ă și jocuri 2D. Acesta este special ă pentru faptul c ă poate fi folosit ă de persoane care nu
au foarte multe cuno ștințe de program are, însă și de cei cu experiență .
Platforma este dezvoltat ă fiind folosit limbajul de programare C++, iar ca limbaje de scripting
aceasta folose ște nativ C# și Uni ty Script (asem ănător cu Javascript), la care va renun ța încep ând din
acest an. Pe l ângă acestea se pot instala aditional s ă suporte limbaje precum Lua. În orice caz
platforma are poten țialul de a folosi orice tip de limbaj de programare care suport ă compi lare nativ ă în
C++.

FIGURA 1 – Editorul Unity
Platforma are suport pentru importarea de modele 3D din programe precum 3ds Max sau
Maya. Poate s ă creeze anima ții atât 2d c ât și 3d foarte u șor. De asemenea suportul pentru simularea
principiilor fizicii ce exist ă și în lumea real ă este foarte mare, acest ă platform ă permi țând foarte u șor
simularea gravita ției, masei unui obiect sau coliziuni de diferite feluri.

14
Platforma ofer ă suport pentru crearea de jocuri și aplica ții foarte complexe av ând suport
pentru Au dio, Anima ții, Fizic ă, Re țelistic ă, programare de tip shader etc. Fiind ideal pentru o
prototipare rapid ă, dar și de lucrul în echipe foarte mari unde fiecare persoan ă poate lucra la partea de
care are nevoie.
2.1.1 De ce Unity3D?
Crearea de platforme pentru joc uri a început s ă ia amploare pe la jum ătatea anilor '90 când
majoritatea firmelor mari de jocuri (Ubisoft, Blizzard) au început s ă-și produc ă propriile platforme.
După anul 2000, o dat ă cu apari ția a mai multe firme foarte mici care lansau jocuri (firme de tip Indie)
au ap ărut și companii specializate în crearea unor astfel de platforme, ceea ce a dus la explozia pe
piața jocurilor de tip Indie.
Deși exist ă peste 300 de platforme de acest gen, cel mai mare competitor al Unity este Unreal.
Fiecare din cele 2 platforme are particularit ățile sale dup ă cum le vom enumera mai jos:
– Particularit ăți Unity:
o Folose ște C# pentru partea de programare;
o Este folosit în special pentru jocuri de PC mici sau jocuri de mobile;
o Excelent pentru o prototipare rapid ă;
o Ușor de fol osit chiar și pentru programatorii începători;
– Particularit ăți Unreal:
o Folose ște C++ pentru partea de programare;
o Este folosit în special pentru jocuri Triple AAA și rareori pentru mobile;
o Creat pentru produc ție, partea de prototipare fiind destul de greoa ie;
o Folosit de programatori cu experien ță;

15

FIGURA 2 – Comparație între Unity și Unreal
Din c âte putem observa Unity este platforma cea mai potrivit ă în momentul în care dorim s ă
experiment ăm cu anumite tehnologii cum ar fi Realitatea Virtual ă folosit ă pe device -uri mobile, mai
ales c ă aceasta are o multitudine de instrumente pentru verificarea performan ței pe dispozitivele
mobile care ne las ă din timp s ă observ ăm unde exist ă sau pot exista probleme.
2.1.2 Realitatea virtual ă în Unity3D
Spre deosebire de alte pl atforme de creare de jocuri, Unity are integrat poten țialul de a
exporta pe orice platform ă de realitate virtual ă (Oculus, GTC Vice, Cardboard, Daydream etc.). În
general dureaz ă aproximativ 3 luni de la apari ția unei platforme de realitate virtual ă până în momentul
în care Unity o suport ă, iar dac ă este o platform ă dezvoltat ă de o companie mare cum ar fi Facebook
cu Oculus, atunci în general Unity lanseaz ă suportul în acela și timp cu platforma.

FIGURA 3 – Vedere cameră în realitate virtuală

16
În principiu, a realiza un proiect de realitate virtual ă cu ajutorul platformei Unity ține mai
mult de con știentizarea limit ărilor și de anumite procedee ce trebuie incluse în programare. Pentru a
exporta un proiect de realitate virtual ă trebuie urma ți cațiva pa și foar te simpli și anume în primul r ând
alegerea platformei.

FIGURA 4 – Setări export Unity
Pentru a folosi un proiect pe o platform ă precum Oculus sau HTC Vive se men ține ca
platform ă PC-ul, pe c ând pentru dispozitivele mobile se alege platforma pe care se do rește exportarea
proiectului (Android sau iOS).
După alegerea platformei un alt pas care mai trebuie urmat este acela ca în set ările de export
să-i spunem c ă dorim un export de tip realitate virtual ă. De obicei Unity nu instaleaz ă pachetele

17
pentru toate p latformele și uneori trebuie instalate pachete adi ționale, spre exemplu în cadrul
platformei Android trebuie instalate Android SDK (software development kit) și de asemenea pentru
aplica ții pentru Google Cardboard trebuie importate pachetele adi ționale pen tru acest export.

FIGURA 5 – Selectare setare pentru realitate virtuală

2.2 Limbajul C#
Lansat publicului în iunie 2000 și oficial în prim ăvara anului 2002, C# este un limbaj de
programare care combin ă facilit ăți testate de -a lungul timpului cu inova ții de ultim moment. Creatorii
acestui limbaj au fost o echip ă de la firma Microsoft condus ă de Anders Hejlsberg.

FIGURA 6 – Prezentare generală Visual Studio

18
Deși limbajul este creat de Microsoft, acesta nu este destinat doar platformelor Microsoft.
Compilatoa re C# exist ă și pentru alte sisteme precum Linux sau Macintosh. Creat ca instrument de
dezvoltare pentru arhitectur ă .NET, limbajul ofer ă o modalitate facil ă și eficient ă de a scrie programe
pentru sistemul Windows, internet, componente software etc. C# de rivă din dou ă dintre cele mai de
succes limbaje de programare: C și C++. De asemenea, limbajul este o “rud ă” apropiat ă a limbajului
Java. Pentru o mai bun ă înțelegere a limbajului C# este interesant de remarcat care este natura
relațiilor acestuia cu celel alte trei limbaje men ționate mai sus. Pentru aceasta, vom plasa mai întâi
limbajul C# în contextul istoric determinat de cele trei limbaje.
Deși Java a rezolvat cu succes problema portabilit ății, exist ă unele aspecte care ii lipsesc. Una dintre
acestea est e interoperabilitatea limbajelor diferite, sau programarea în limbaj mixt (posibilitatea
codului scris într-un limbaj de a lucra în mod natural cu codul scris în alt limbaj). Interoperabilitatea
limbajelor diferite este esen țială la realizarea sistemelor s oftware de dimensiuni mari.
Ca parte a ansamblului strategiei .NET, dezvoltat ă de Microsoft, la finele anilor ’90 a fost
creat limbajul C#. C# este direct înrudit cu C, C++ și Java. “Bunicul” limbajului C# este C -ul. De la
C, C# mo ștenește sintaxa, multe d in cuvintele cheie și operatorii. De asemenea, C# construie ște peste
modelul de obiecte definit în C++. Rela ția dintre C# și Java este mai complicat ă. Java deriv ă la randul
său din C și C++. Ca și Java, C# a fost proiectat pentru a produce cod portabil. Li mbajul C# nu deriv ă
din Java. Între C# și Java exist ă o rela ție similar ă celei dintre “veri”, ele deriv ă din acela și stramos,
dar deosebind u-se prin multe caracteristici importante.
Limbajul C# con ține mai multe facilit ăți inovatoare, dintre care cele mai importante se refer ă
la suportul încorporat pentru componente software. C# dispune de facilit ăți care implementeaz ă direct
elementele care alc ătuiesc componentele software, cum ar fi propriet ățile, metodele și evenimentele.
Poate cea mai important ă facilit ate de care dispune C# este posibilitatea de a lucra într-un mediu cu
limbaj mixt.
2.2.1 Rela ția dintre C# și arhitectura .NET
C# are o legatur ă deosebit ă cu mediul s ău de rulare, arhitectura .NET. Pe de o parte, C# a fost
dezvoltat pentru crearea codului pentru arhitectura .NET, iar pe de alt ă parte bibliotecile utilizate de
C# sunt cele ale arhitecturii .NET.
Ce este arhitectura .NET ?
Arhitectura .NET define ște un mediu de programare care permite dezvoltarea și execu ția
aplica țiilor indiferent de platform ă. Aceasta permite programarea în limbaj mixt și oferă facilit ăți de
securitate și portabilitate a programelor. Este disponibil ă deocamdat ă pentru platformele Windows.

19
Legat de C#, arhitectura .NET define ște dou ă entități importante și anume biblioteca de clase .NET și
motorul comun de programare sau Common Language Runtime (CLR). C# nu are o bibliotec ă de
clase proprie ci utilizeaz ă direct biblioteca de clase .NET. De exemplu, c ând se ruleaz ă un program
care efectueaz ă opera ții de intrare -ieșire, cum ar fi afi șarea unui text pe ecran, se utilizeaz ă biblioteca
de clase .NET.
Motorul comun de programare (CLR) se ocup ă de execu ția programelor C#. El asigur ă de
asemenea programarea în limbaj mixt, securitatea și portabilitatea programelor. Atunci c ând este
compilat un program C#, sau un program în limbaj mixt, rezultatul compil ării nu este un cod
executabil. În locul acestuia, se produce un fi șier care con ține un tip de pseudocod numit limbaj
intermediar sau pe scurt IL (Intermediate Language). Acest fișier IL poate fi copiat în orice calculator
care dispune de .NET CLR. Prin intermediul unui compilator denumit JIT (Just In Time), motorul
comun de p rogramare transform ă codul intermediar în cod executabil. Procesul de conversie decurge
astfel: atunci c ând un program .N ET este executat, CLR activeaz ă compilatorul JIT. Compilatorul JIT
converte ște IL în cod executabil pe m ăsura ce fiecar e parte a programului este nece sară. În concluzie,
orice program compilat p ână în format IL poate rula în orice mediu pentru care CLR est e
implementat. În acest fel arhitectura .NET asigur ă portabilitatea.

FIGURA 7 – Procesul de compilare în .NET C#
2.2.2 Principalele caracteristici ale arhitecturii .NET
Independența de procesor și de platformă
Codul CIL este independent de sistemul de operare și de procesor. De aceea, în scrierea
aplicațiilor nu trebuie să fiți preocupați de caracteristicile hardware sau software ale sistemului. Spre
surpriza multor programatori, aplicațiile .NET pot fi dezvoltate și executate pe sisteme de operare
non-Microso ft, (Mac OS X, numeroase distribuții Linux și Solaris, ca să numim numai câteva).

20
Managementul automat al memoriei
Alocar ea și eliberarea memoriei nu mai este o problemă care trebuie să -i preocupe pe
programatori, datorită mecanismului automat de garbage c ollection.
Interoperabilitatea limbajelor
Este un fapt comun ca diversele componente ale unei aplicații să fie scrise în limbaje diferite,
suportate de către platforma .NET.
Securitate
.NET furnizează un model comun de securitate, valabil pentru toate apli cațiile, care include
un mecanism unificat de tratare a excepțiilor . O excepție este un eveniment neprevăzut, care întrerupe
execuția unui program, atunci când de pildă, se execută o intrucțiune ilegală.
Portabilitate
Un program scris pentru platforma .NE T poate rula fără nici o modificare pe oricare sistem pe
care platforma este instalată. Caracteristicilor de mai sus li se adaugă și altele, care ies însă din cadrul
acestei lucrări.

21
3 PROIECTAREA UNEI APL ICAȚII ÎN REALITATE
VIRTUAL Ă
3.1 Arhitectura ap licației
Deși exist ă diferite principii și arhitecturi prezente în programarea jocurilor, în cadrul
aplica ției mele am preferat s ă plec de la c âteva principii software și să dezvolt și adaptez în func ție de
cerin țele Unity. Platforma a fost creat ă inițial pentru o prototipare rapid ă, astfel folose ște limbajul C#
ca limbaj de scripting și nu ca limbaj orientat pe o biecte. Pentru a folosi principiile program ării
orientate pe obiecte trebuie adaptat.
3.1.1 MVC și MVVM
MVC (Model View Controller) și MVVM (Model View ViewModel) sunt dou ă arhitecturi
folosite în crearea de software model care se bazeaz ă ca obiectele s ă aibă cât mai pu ține depend ințe,
iar codul s ă fie modularizat și abstractizat suficient de mult încât putând fi adaugat ă sau scoase p ărți
întregi din soft . MVC este folosit în principal în Java și C++ și se bazeaz ă pe crearea a c ât mai pu ține
dependi țe între obiecte prin separarea interfe ței de logica aplica ției, iar legatura dintre cele dou ă este
făcută de un controller.

FIGURA 8 – Schemă MVC
MVVM este a rhitectura dezvoltat ă de Microsoft special pentru C# și platforma .NET. În
cadrul acesteia logica și interfa ța sunt separate la fel, dar legatura dintre cele dou ă se face printr -un
procedeu automatizat numit „data binding”, Microsoft av ând un suport foarte mare pentru acest
procedeu, ajut ând dezvoltatorii s ă nu mai piard ă timp f ăcând ei de fiecare dat ă aceste leg ături, ci doar
anumite declara ții care mai apoi compilatorul le cite ște și le leag ă automat.

22

FIGURA 9 – Schemă MVVM
Deși Unity nu poate folosi na tiv nici unul dintre aceste procedee, prin dezvoltarea unor clase
de tip Singleton, se poate implementa o arhitectur ă care are la baz ă MVC sau MVV M.
3.1.2 Singleton
În programarea tradi țional ă folosirea unei clase de tip Singleton este considerat ă o practic ă
incorect ă și evitat ă pe cât de mult posibil. În Unity, datorit ă claselor de tip Monobehavior care au
metode de tip Start() sau Update() care pornesc în mod asincron este singura practica care asigur ă că
jocul sau aplica ția va rula în ordinea în care programat orul dore ște.
De obicei într-o aplica ție dezvoltat ă în Unity exist ă un manager al aplica ției și un manager de
scene. P ână la Unity 5, at ât managerul de aplica ție cât și managerii de scene erau clase de tip
Singleton, dar dupa Unity 5, c ând o scen ă putea av ea ca obiecte copil alte scene și managerul de scene
nu trebuia s ă facă doar trecerea de la o scen ă la alta, ci și încărcări și desc ărcări de sub -scene și de alte
componente s -a renun țat la aceast ă practic ă și a rămas doar managerul de aplica ție care este Singleton
și în concordan ță cu care toate celelalte obiecte se sincronizeaz ă. De asemenea pe tot parcursul
aplica ției managerul aplica ției trebuie s ă ruleze f ără întrerupere, distrugerea acestuia însemn ând
oprirea for țată a aplica ției.

23

FIGURA 10 – Clasă tip Singleton
3.1.3 Crearea interfe ței unei aplica ții de realitate virtual ă
Când vorbim de interfa ță în Unity ne referim la tot ceea ce ține de partea grafic ă, de la scena
grafic ă care poate s ă conțină o multitudine de componente grafice, p ână la interfa ța utili zatorului, cea
prin care acesta comunic ă cu aplica ția (ex. Ecrane de tip pop -up, meniuri etc.). În cazul unei aplica ții
de realitate virtual ă, scena grafic ă este realizat ă într-un mediu 3D, softurile cele mai folosite fiind 3Ds
Max sau Maya, în cazul aplic ației mele, scena grafic ă a fost creat ă în 3Ds Max. Camera propriu -zisă
fiind creat ă inițial, iar elementele din ea, importate din cadrul anumitor asset -uri pe care Unity le ofer ă
gratuit în cadrul magazinului lor.
În cadrul aplica țiilor de realitate virtu ală folosite pe telefon, o scen ă complet ă nu poate avea
mai mult de un milion de vertec și pentru a rula f ără probleme. În cadrul acestei aplica ții, scena
complet ă (camera + mediul înconjur ător) depa șesc 5 milioane de vertec și. În mod normal nu ar fi
posibi l să ruleze pe un telefon, dar prin implementarea unor algoritmi de optimizare camera nu ajunge
să vadă mai mult de 800.000 vertec și la un moment dat.
Nevoia de o optimizare foarte bun ă pe aplica țiile care ruleaz ă într-un mediu de realitate
virtual ă vine d in cauza procesorului grafic care este foarte slab în compara ție cu ceea ce exist ă pe un
calculator personal și de asemenea împărțirea ecranului în 2 p ărți face ca FOV -ul să fie mai mic, dar

24
procesorul grafic trebuie s ă randeze dublu, ceea ce înseamn ă ca un astfel de joc trebuie s ă fie la
jumate din c ât poate duce un telefon mobil pe tot ecranul, de aici și calitatea slab ă pe care o au
jocurile în realitate virtuală în compara ție cu jocurile normale.
Un alt element foarte important în crearea p ărții grafice dintr -un joc este acela de a face
design -ul scenei. În cadrul cre ării design -ului trebuie luat ă în considerare foarte mult optimizarea.
După cum voi explica în cadrul capitolului „Optimizarea proceselor de randare”, prin crearea unui
algoritm numit Occlus ion Culling în domeniile de specialitate camera nu va mai vedea toat ă scena
deși FOV -ul este mult mai mic, ci va vedea exact vertec și pe care camera îi vede un offset care îi va
da timp suficient s ă proceseze vertec și care vor fi genera ți în partea în care utilizatorul își mișcă capul.

FIGURA 11 – Prezentare cameră și mediu înconjurător
Chiar și dup ă acest tip de optimizare care poate de multe ori s ă scadă numărul de vertec și
văzuți de camer ă cu până la 60%, mai exist ă un procedeu care poate s ă scadă acest num ăr cu încă 10-
15%. Acest procedeu const ă în crearea de col țuri și părți înguste care face ca anumit e părți dintr -o
scena (în cazul nostru camera) să nu fie vizibile direct.

25

FIGURA 12 – Prezentare interior cameră
În mod normal pentru acest tip de des ign, în industria de jocuri exist ă un post special care se
nume ște Level Designer, al e cărui atribu ții sunt s ă creeze o scen ă în care imersiunea s ă fie cât mai
reală și de asemenea s ă ia în considerare limit ările at ât ale jocului c ât și cele de tip hardwar e.
O diferen ță major ă între o aplica ție normal ă și una de realitate virtual ă este interac țiunea
utilizatorului cu jocul. În cadrul ecranelor de tip pop -up, Unity creaz ă un Canvas care mai apoi
randeaz ă totul în 2D pe ecran deasupra scenei 3D, ceea ce rezol vă o mul țime de probleme din punct
de vedere al pozi ționării și adâncimii elementelor, platforma av ând un suport foarte bun pentru
aceast ă parte. În cadrul aplica țiilor de realitate virtual ă aceste ferestre trebuie s ă fie în cadrul scenei
3D, iar pozi ționarea și adâncimea sunt 2 elemente care nu se mai fac automat. Atunci c ând se
genereaz ă o fereastr ă de tip pop -up trebuie avut grij ă să nu treac ă prin pere ți sau alte elemente sau s ă
nu apar ă cu întârziere, pentru c ă player -ul poate s ă treac ă mai departe și să nu-l observe.

26

FIGURA 13 – Optimizare grafică folosind Occlusion Culling
Setarea unor astfel de ferestre este mult mai greoaie și din cauz ă că tot timpul trebuie s ă
pastreze o referin ță către camera activ ă (o practic ă foarte comun ă în industria jocuri lor este aceea de a
folosi c âte un tip de camer ă pentru fiecare tip de element), iar dac ă îl rand ăm pe o camera care nu mai
este activ ă, atunci fereastra nu va mai fi randat ă pe ecran.
În concluzie, Unity spre deosebire de alte platforme de dezvoltare jocu ri ofer ă foarte mult
suport și foarte multe module care fac dezvoltarea un ui proces mult mai u șor și rapid. Dar chiar și cu
acest suport, încă sunt foarte multe p ărți pe care dezvoltatorul trebuie s ă le fac ă manual și de care
trebuie s ă țină cont în moment ul în care se apuc ă de o astfel de aplica ție.
3.1.4 Dezvoltarea logic ă a unei aplica ții de realitate virtual ă
În primul r ând, o diferen ță dintre aplica țiile normale și cele dezvoltate pentru realitatea
virtual ă sunt c ă nu suport ă elemente 2D. În general în jocur ile pe mobile sau chiar și cele pe PC,
exist ă procedee prin care se pot folosi imagini 2D care s ă dea impresia c ă sunt tridimensionale. Un
procedeu care de asemenea ajut ă foarte mult în optimizarea unei aplica ții. În realitate virtual ă,
elementele 2D se vo r vedea ca elemente 2D și pot fi folosite în puține cazuri (ex. Tablouri).
O alt ă diferen ță major ă este partea de control. Controlul unei aplica ții în realitate virtual ă se
face complet diferit și necesit ă un stil de programare specific platformei.

27

FIGUR A 14 – Schemă arhitectură pentru mișcare
Rota ția utilizatorului o face aplica ția utilizant Giroscopul telefonului. Cu privire la mers exist ă
mai multe procedee folosite în realitate virtual ă. Primul este mersul continuu folosit în general în
jocuri de tip „Endless Runner” în care utilizatorul are o traiectorie pe care o urm ărește și nu se poate
abate de la ea. În cazul acestor aplica ții distan ța până la care utilizatorul trebuie s ă vadă trebuie s ă fie
destul de mare pentru a avea timp s ă ia decizii în ce di recție o ia în cazul unui obstacol sau unui traseu
multiplu.
O alt ă form ă de mi șcare este teleportarea utilizatorului între diferite puncte. De exemplu un
utilizator trebuie s ă parcurg ă o distan ță medie și are anumite puncte pe scena pe care dac ă focuseaz ă
camera îl va trimite acolo. Spre exemplu într-un joc de tip „Tower Defence”, utilizatorul are mai
multe turnuri și uitându-se către un turn mai mult de 3 secun de îl va teleporta în acel turn. Acest tip de
deplasare este foarte bun în momentul în care util izatorul trebuie s ă se mute între anumite puncte care
se repet ă și nu este nevoit mai apoi s ă facă o altă deplasare.
Cea mai complex ă form ă de mi șcare, este mi șcarea f ăcută cu ajutorul unui joystick. Acest tip
de mi șcare ofer ă libertate utilizatorului, dar în acela și timp îl limiteaz ă pe utilizator de a achizi ționa
partea de hardware necesar ă, ceea ce de foarte multe ori poate s ă fie destul de scumpa.

28
După cum se poate observa în imaginea de mai sus totul este bazat pe clasa
CrossPlatformInputManager. De ce cross platform? Înainte de a testa în mediul de realitate virtual ă,
dezvoltarea și testarea ini țială se face pe un calculator personal, de aceea partea de mi șcare și nu
numai trebuie s ă aibă suport și pentru a fi testat cu succes pe calculatorul personal . Dup ă calculatorul
personal trebuie testat dacă suport ă platforma mobil ă pentru care este dezvoltat, de aceea trebuie
dezvoltat ă mișcarea și pentru platforma de mobil.
În cadrul aplica ției noastre în primul r ând s -a implementat partea de input pentru un
TouchPad care este baza mi șcării libere. Dup ă care pe aceast ă bază s-a construit inputul pentru
joystick. În cealalat ă parte putem vedea c ă la baz ă stă clasa VirstualInput în care sunt declarate
mișcările de baz ă cu cele 6 grade de libertate. Apoi din aceas tă clasă a fost construit ă StandaloneInput
care ia intrarea pentru tastatura de pe calculatorul personal, la care în MobileInput s -a adăugat partea
de touch pentru mobile. TiltInput este o clas ă de baz ă pentru mi șcarea automat ă despre care am vorbit
mai de vreme și care poate fi implementat ă foarte u șor în acest proiect pe baza acestei clase.
Pe lângă mișcări, au trebuit definite și interac țiunea utilizatorului cu mediul înconjur ător,
interac țiune care se face prin existen ța unor butoane virtuale în camera noastr ă.
Am observat mai sus cum crearea doar a mi șcării și interac țiunii caracterului cu lumea
înconjuratoarea poate s ă fie foarte complex, mai ales c ă lucrăm pe o platform ă pe care nu se pot face
teste pe calculatorul personal și astfel trebuie dezvoltat ă o arhitectur ă care s ă susțină toate platformele
pe care vom testa.
Dacă la începutul acestei lucr ări spuneam c ă folosim Singleton pentru a sincroniza tot ceea ce
exist ă în joc, de ce nu sincronizam și player -ul cu mediul înconjur ător? R ăspunsul este foar te simplu
și anume c ă player -ul doar interac ționeaz ă cu mediul înconjur ător și nu face parte din el, ceea ce
înseamn ă că vor rula asincron, neav ând nici un fel de dependin ță.

29

FIGURA 15 – Schemă cu arhitectura jocului
Putem observa în diagrama UML de mai sus cum clasa GameManager este o clas ă de tip
Singleton, care este în centrul jocului nostru. Restul obiectelor au dependin țe către aceast ă clasă sau
moștenesc alte obiecte care au astfel de dependin țe.
Game Manager
Este o clas ă de tip Singleton care sin cronizeaz ă toate celelalte obiecte. De ce ar fi nevoie de
sincronizare? În primul r ând, una din clasele speciale ale Unity, Monobehaviour, ruleaz ă metodele
într-un mod asincron (acest lucru se întampl ă din cauz ă că limbajul C# este folosit ca limbaj de
scripting și nu ca limbaj orientat pe obiecte). Astfel metoda Start() poate fi apelat ă în dou ă scripturi
diferite în cadrul a dou ă rulări în mod diferit. Astfel în primul script va fi rulat ă metoda prima la prima
rulare, iar la cea de -a doua rulare va fi rula tă a 2-a, ne știind la a 3 -a sau a 4 -a rulare care va fi ordinea.
Din cauza asta c ând un script are dependin țe către un alt script se creaz ă alte metode (ex. OnStart())
pe care managerul le apeleaz ă în ordinea dorit ă din cadrul metodei Start().

FIGURA 16 – Sincronizarea jocului cu GameManager

30
Popups Manager
În cadrul oric ărui joc existe ferestre de tip Pop-up, iar cu c ât jocul devine mai complex cu at ât
exist ă mai multe ferestre și posibilitatea ca dou ă ferestre s ă apară în acela și timp, ceea ce poate crea
atât probleme vizuale c ât și de logic ă. Astfel în cadrul jocurilor se creaz ă un manager de pop -up-uri
care ține o list ă acestora și se bazeaz ă pe principiul primul intrat, ultimul ie șit. De asemenea
managerul este cel care decide ce fereastr ă trebuie s ă apară și ce date trebuie s ă conțină. În cadrul
aplica ției noastre fiind doar un tip de astfel de fereas tră, logica este foarte u șoară și simpl ă, dar cu c ât
adăugăm mai multe tipuri, lucrurile se complic ă și un astfel de manager salveaz ă dezvoltatorii de
foarte multe probleme și ore de lucru în plus.

FIGURA 17 – Înregistrarea ferestrelor de tip pop -up într -o listă de așteptare
Keypad Manager
Cu privire la Keypad manager este o clas ă care face legatura dintre func ționalitatea de baz ă și
un asset Unity luat d in Asset Store. De cele mai multe ori în jocuri nu toate componentele sunt
realizate de c ătre programator. Exist ă cazuri în care dezvoltarea unei componente dureaz ă foarte mult
și nu aduce o foarte mare valoare proiectului. În acest caz exist ă diverse maga zine unde astfel de
componente sunt g ăsite fie gratis fie la un pre ț mic și scute ște programatorul de la 2 -3 zile p ână la 2-3
luni în dezvoltarea unei componente. Acest lucru s -a întamplat și în cazul keypad -ului. Aceast ă
component ă nu are vreun sistem de programare, ci doar partea grafic ă care este destul de complex ă și
ar fi durat foarte mult pentru un non -grafician s ă o realizeze. De asemenea Keypad manager -ul are
rolul de a lua intrarea de la taste și de a le trimite mai departe c ătre DisplayManager car e mai apoi le
va afi șa pe ecran.
Object Manager
Aceasta este o clas ă care face diferen țierea între obiectele cu care utilizatorul poate s ă
interac ționeze și cele statice. De asemenea are grij ă să transmit ă unui obiect c ând utilizatorul
interac ționeaz ă cu el și care este modul de interac țiune.
Localization Manager

31
Pentru aceast ă aplica ție am creat și suport pentru limbi multiple. Astfel aceast ă clasă este
responsabil ă cu luarea informa țiilor necesare pentru joc în limba în care utilizatorul dore ște. Acest
lucru se realizeaz ă prin crearea unor fi șiere care au aceea și informa ție, dar sunt în limbi diferite ( în
cadrul acestei aplica ții este vorba de rom ână și englez ă. Apoi în func ție de set ările pe care utilizatorul
le alege managerul ia informa țiile necesare.
Repository
Clasele de tip repository sunt clase care au ca responsabilitate scrierea și citirea din fi șiere.
Atât în dezvoltarea software c ât și cea de jocuri, citirea de set ări, localiz ări și alte date se face din
fișiere sau baze de date. Astfel exist ă o clasă de tip repository care se ocup ă ca citirea și scrierea de
date s ă funcționeze conform func ționalit ătii și design -ului. Aceste clase sunt nivelul cel mai de jos
înainte de citirea de date și de obicei sunt prezente pe tot parcursul aplica ției, închide rea aplica ției face
ca aceste obiecte s ă se închid ă și să fie distruse ultimele.

FIGURA 18 – Salvarea și încărcarea datelor
Display Manager
Este clasa care afi șează cifrele pe ecran și trimite datele mai departe c ătre partea de logic ă
care le va valida.

32
După cum putem vedea întreaga arhitectur ă este bazat ă pe principiul de dependin țe cât mai
puține, unul din principiile de baz ă ale program ării pe obiecte, ceea ce face ca codul s ă fie mai u șor de
ințeles, iar posibilitatea de a avea probleme mult mai mic ă.
3.1.5 Crearea leg ăturilor dintre interfa ță și logica aplica ției
În aplica țiile software tradi ționale care folosesc MVC sau MVVM exist ă un controller care
face leg ătura dintre logic ă și interfa ță, de asemenea în acest controlor se face și programarea vizual ă.
Prin programare vizual ă înțelegem tot ceea ce este cod și este folosit pentru interfa ță (ex. Schimbarea
unui font, schimbarea culorii unui buton, pozi ții de elemente grafice etc.). De multe ori dorim ca
interfa ța aplica ției să răspund ă la anumite interac țiuni ale utilizatorului astfel aceste schimb ări se fac
program ând doar partea grafic ă să răspund ă la interac țiunile utilizatorului, iar aceast ă parte se face în
interiorul controller -ului.
În programarea de jocuri exist ă două tipuri de interac țiuni ale utili zatorului cu partea grafic ă a
jocului. Una este interac țiunea juc ătorului cu meniul s ău partea 2D care se face la fel ca la aplica țiile
software. În cadrul aplica ției de realitate virtual ă aceste interac țiuni lipsesc, pentru c ă și ferestrele din
cadrul men iului sunt integrate în spa țiul 3D.
O alt ă legătură și cea prezent ă în cadrul aplica ției mele de realitate virtual ă este cea de
interac țiune a utilizatorului cu mediul 3D. Pentru astfel de interac țiuni de obicei se folosesc script -uri
care se leagă la întreaga arhitectur ă (ex. DisplayManager) sau pur și simplu scripturi de sine st ătătoare
care pur și simplu r ăspund la interac țiunea cu utilizatorul.
Aceste leg ături le face Unity foarte u șor (Drag&Drop) și automat în cadrul componentelor
unui obiect:

33

FIGURA 19 – Legăturile obiectelor din joc cu logica
În imaginea de mai sus se poate observa foarte u șor cu clasa CifreDiscoverability are în
componen ța sa mai multe obiecte care nu sunt parte din ierarhia GameManager. Ce înseamn ă asta?
Dacă la momentul cre ării obiectului GameManager de c ătre procesor, celelalte obiecte sunt existente,
un script poate lua referin țe la ele și poate s ă le foloseasc ă după cum dore ște.
Acest lucru se poate face prin mai multe metode, dar cele mai folosite sunt folosirea
operatorului d e accesibilitate public în câmpul care dorim s ă îl facem vizibil în inspector, dar acest
procedeu va da acces la acest obiect și celorlalte componente din afara obiectului nostru ceea ce nu
este foarte corect din punct de vedere al program ării orientate pe obiect.
Un alt procedeu și cel mai folosit este folosirea operatorului de accesibilitate private sau
protected, dar se poate folosi declara ția [SerializeField] ceea ce are ca rol s ă facă câmpul respectiv
vizibil în inspector și să țină valoarea respectiv ă într-o zon ă de memorie separat ă care permite
serializarea sau salvarea acelei informa ții.

34

FIGURA 20 – Crearea de setări predefinite în Unity
În cadrul script -urilor care r ăspund singure la interac țiunea utilizatorului cu obiectele pe care
acestea sunt p lasate putem observa c ă în figura de mai sus RotatableObject are un set de parametri
care chiar și o persoan ă non-programator poate s ă îi seteze, iar obiectul va r ăspunde în func ție de
setarea parametrilor seta ți. Astfel de script -uri independente sunt foa rte folosite în cadrul jocurilor
mici, exist ând jocuri precum Flappy Bird care poate s ă fie construit doar pe baz ă de script -uri
independente fiecare ac ționând singur la interac țiunea utilizatorului cu fiecare parte a jocului, numai
că în jocurile mai mari aceste script -uri trebuie generalizate foarte mult și interac țiunea trebuie
sincronizat ă cu restul mediului înconjurator.
3.1.6 Exportarea unui proiect de Realitate Virtual ă
În momentul în care proiectul este gata pentru prezentare sau lansare în magazinul Goog le, se
va face exportul special pentru Android.

35

FIGURA 21 – Setări export Android
Din meniul File se merge la Build Settings, unde se selecteaz ă scenele care vor s ă fie
adăugate în export și platforma dorit ă, în cazul nostru Android. P ână la rularea și crearea exportului,
trebuie f ăcute c âteva set ări specifice Android și proiectelor de realitate virtual ă.
Pentru realitate virtual ă, va trebui s ă selectă m că dorim un build special, iar pe l ângă Unity și
componentele sale va trebuie s ă avem și SDK -urile(Sof tware Development Kit) pentru realitate
virtual ă, în cazul nostru Cardboard.

36

FIGURA 22 – Alegerea setărilor corecte pentru crearea unui export pentru realitate virtuală
O alt ă setare foarte important ă este alegerea versiunii de Android minime și maxime p e care
aplica ția va rula. Android 4.4 este prima versiune care a suportat realitatea virtual ă, un export la o
versiune mai mic ă de Andoid 4.4 ar duce la mai multe incompatibilit ăți. În cur ând Unity și Google și-
au ar ătat inten ția de a cre ște versiunea de l a 4.4 la 5.0 pentru o stabilitate mai mare și posibilitatea de a
adăuga con ținut nou.

37

FIGURA 22.1 – Alegerea versiunii corecte pentru crearea unui export pentru realitate virtuală
De asemenea Unity are o componen ță foarte interesant ă în care se declar ă un tip de
componen ță în func ție de platforma pe care o țintim, iar în momentul în care face export ul la
„Scripting Define Symbols ” vom trece componen ța definită și va compila din clasa în care exist ă
aceast ă component ă doar platforma țintită.
3.2 Optimizarea Ap licației
Atât în dezvoltarea software c ât mai ales în dezvoltarea jocurilor care este un procedeu
iterativ și creativ, planul ini țial nu o s ă semene cu rezultatul. Astfel în principiu în fiecare etap ă a
dezvolt ării exist ă și un procedeu de optimizare. Încă de la g ândirea arhitecturii se încearc ă să se
calculeze care sunt resursele necesare și care sunt resursele minime și maxime, iar pe baza acestor
cerin țe se dezvolt ă inițial componentele grafice și cele de anima ție. Apoi în timpul dezvolt ării acolo
unde e ste posibil se face optimizare de cod, iar c ând se apropie de final, se vede c ât de mult se

38
încadreaz ă în cerin țele ini țiale și dac ă nu este un rezultat bun, se mai face o optimizare ulterioar ă în
care în general se renun ță la calitatea grafic ă și la anumi te componente care nu sunt foarte importante
dar consum ă foarte mult.
3.2.1 Optimizare memorie
Programarea jocurilor fiind un procedeu iterativ de multe ori se creaz ă foarte multe materiale
sau cod și la final se folosesc doar o parte din acestea (undeva la 40% pentru un joc de mobil).
Optimizarea de memorie este printre cele mai simple procedee. În primul r ând se elimin ă toate
componentele at ât grafice c ât și de cod care nu sunt folosite. De asemenea se mai poate c âștiga spa țiu
în momentul în care folosim anumit e componente grafice s ă le reducem din rezolu ție și astfel se
reduce și din m ărimea pe care acestea o ocup ă. De exemplu în cadrul acestui proiect, în momentul
când se va face exportarea final ă, pe l ângă componentele grafice pe care nu le mai folosesc, voi scoate
și componentele de mi șcare pe care le folosesc pentru a rula teste pe calculatorul personal.
Aceste procedee de optimizare sunt folosite c ând dorim ca jocul s ă ocupe c ât mai pu țin loc în
memoria fizic ă. Dac ă vorbim de memoria RAM atunci exist ă cu to tul alte procedee.
În principiu memoria RAM trebuie optimizat ă în momentul în care o scen ă este foarte mare și
trebuie încărcată în mem orie. De și nu a fost nevoie de o astfel de optimizare pentru aplica ția prezent ă,
vom discuta pe scurt despre care sunt pr incipiile și procedeele acestei optimiz ări.

FIGURA 23 – Folosirea scenelor multiple
Începând cu Unity 5, s -a indrodus principiul de scene multiple, adic ă o scen ă poate s ă conțină
alte scene, astfel pentru un joc care are un mediu înconjur ător imens se ac tiveaz ă o sub -scenă la un

39
moment dat, celelalte fiind dezactivate, ceea ce înseamn ă consum de memorie mult mai mic. Dar
pentru ca memoria consumata s ă scadă considerabil, texturile și componentele 3D trebuie încărcate în
atlase specifice. Dac ă, de exemplu, pentru 10 scene avem una activ ă, dar un singur atlas, aceasta
înseamn ă că deși e o scen ă activ ă, memoria folosit ă este aproximativ aceea și ca și cum am avea 10
scene active. Astfel fiecare scen ă trebuie s ă aibă atlasele sale pentru a nu se încărca în memo rie
componente de care jocul nu are nevoie în acel moment. Acest procedeu este foarte complex și
implementarea unui astfel de sistem ia foarte mult timp.
3.2.2 Optimizarea proceselor
Unity ține cont at ât de ceea ce procesorul încarc ă atât la începutul jocului ( în momentul în
care scena se încarc ă), dar și ceea ce încarc ă în timpul rul ării jocului. De ce trebuie diferen țiate?
Există foarte multe elemente care pot fi încărcate în memorie în momentul în care jocul este pornit și
nu vor mai trebui desc ărcate p ână la oprirea acestuia, iar procesarea acestora în timp real poate crea
probleme pentru procesoare, mai ales cele ale procesoarelor de mobil, care sunt foarte slabe în
compara ție cu cele ale calculatoarelor personale.

FIGURA 35 – Pagină statistici Unity
În partea de „Batches” avem c âte apeluri sunt c ătre procesor la momentul respectiv, pe c ând
în „Saved by batching” sunt informa țiile încărcate în memorie la începutul jocului și care nu mai sunt
necesare pentru reprocesare.
Pentru optimizarea informa țiilor pe c are procesorul le proceseaz ă exist ă mai multe procedee.
În primul r ând crearea de atlase pentru texturi și materiale. Astfel dac ă avem 20 de texturi într-o
scenă, se vor adau ga 20 de apeluri c ătre procesor, dar dac ă folosim un atlas și punem cele 20 de
texturi in el, vom avea doar un apel c ătre procesor.

40

FIGURA 36 – Prezentare Texture Packer
Deși Unity are propriul modul care poate fi folosit pentru crea ția de atlase, am preferat s ă
folosesc o aplica ție extern ă Unity numit ă Texture Packer, pentru c ă este mult mai robust, iar
algoritmul pentru a șezarea texturilor este mult mai bun.
O alt ă modalitate este aceea de a seta obiectele asupra c ărora nu ac ționeaz ă fizica ca obiecte
statice.

FIGURA 37 – Aplicarea opțiunii statice asupra obiectelor
Dacă în prima f ază erau peste 600 de apeluri c ătre procesor și la „Saved by batched” erau
aproximativ 200, acum putem observa c ă aproximativ 120 de apeluri de la procesarea în timp real s -au
mutat la procesarea la pornirea jocului. Acest lucru s -a intamplat în momentul în care camera am
setat-o ca fiind static ă. Trebuie avut grij ă ca obiectele asupra c ărora aplic ăm anima ții sau fizica de
mișcare sau rota ție să nu fie statice pentru c ă atunci nu vor mai r ăspunde la interac țiune. Aceasta este
o metod ă simpl ă pe care Unity o face automat, programatorul sau artistu l trebuie doar s ă selecteze
obiectele statice ca fiind statice, astfel tot ceea ce se încarc ă în timp real, va trece la început.

41
Un alt procedeu de care trebuie ținut cont încă de la începutul aplica ției este acela d e a folosi
cât mai pu țin metode de Update(). De aceea orice interac țiune dintre utilizator și mediul înconjur ător
se face pe baz ă de evenimente cum ar fi OnMouseOver() sau OnGazeOver(). Exis tă și componente
care au nevoie s ă răspund ă în timp real și folose sc Update() dar sunt foarte pu ține obiecte de acest
gen. Dac ă nu avem fizic ă și elemente de fizic ă care au nevoie s ă se schimbe per frame, atunci trebuie
evitate metodele de tip Update().
3.2.3 Optimizarea proceselor de randare
De cele mai multe ori în spatele j ocurilor logica este costisitoare ca resurs ă, dar cea mai
costisitoare resurs ă este partea grafic ă. Întotdeauna grafica jocurilor video a fost cu un pas înaintea
hardware -ului dezvoltat de divers e companii. De aceea în cadrul optimiz ărilor f ăcute jocurilor cele
mai masive sunt pe partea grafic ă.
Unul din cele mai interesante concepte la ora actual ă pe partea de optimizare grafic ă este un
procedeu numit „Occlusion Culling”. Acesta const ă în randarea doar a elementelor pe care camera le
vede, ascunz ând celela lte elemente.
În Unity camera randeaz ă doar ceea ce vede în timp real, dar restul elementelor sunt desenate
în scen ă astfel sunt create și randate și ele, ceea ce înseamna c ă GPU -ul încarc ă și proceseaz ă toată
scena la un moment dat, ceea ce pentru jocuril e medii și mari acest lucru este pur și simplu prea mult,
chiar și pentru un calculator personal.
În cadrul procedeului de Occlusion Culling, camera trimite c ătre procesorul grafic ceea ce
randeaza și restul elementelor dintr -o scen ă nu mai sunt desenate și astfel încărcate în memorie.

42

FIGURA 38 – Schemă optimizare grafică
Deși procedeul pare foarte simplu c ând este explicat, acesta are în spate un algoritm foarte
avansat și greu de înțeles. Unity are dezvoltat un algoritm de baz ă pentru Occlusion Culling dar acesta
are o mic ă problem ă, aceea c ă lucreaz ă la scar ă foarte mar ă. Adica algoritmul creaz ă un perimetru în
jurul scenei și de acolo randeaz ă fiecare obiect pe care îl vede camera, dar pentru obiectele care au
copii pe care nu îi vede de asemenea îi randeaz ă.
Astfel am dezvoltat o libr ărie în C++ și am integrat -o în Unity, care atunci c ând se activeaz ă
acest algoritm din Unity va lua în considerare toat ă scena, dar va ține cont în fiecare copil al scenei de
vertec și pe care acesta îi are vizibili de c ătre camer ă. Astfel dac ă vom da un offset de 0 pentru partea
de randare atunci procesorul grafic va randa doar ceea ce camera vede. O astfel de setare va avea și
părți rele, deoarece procesorul grafic încarc ă doar ceea ce vede camera, la o întoarcere brusc ă acesta
nu va avea puterea necesar ă să proceseze tot ceea ce camera vede și vor exista bug -uri grafice.

43

FIGURA 39 – Model randare cameră joc dupa optimizare
Astfel pentru un echilibru între performan ță și o procesare corect ă, am dezvoltat o limitare
care calculeaz ă puterea de procesare a GPU -ului și randeaz ă suficient c ât să existe o cre ștere a
performan ței puternic ă, dar în orice punct și oric âte mi șcări ar face utilizatorul s ă nu existe probleme
grafice:

FIGURA 40 – Exemplu cod optimizare cameră în f uncție de procesorul grafic

44
4 PREZENTAREA APLICA ȚIEI
4.1 Introducere
Pentru a rula aplicația este necesar un dispozitiv cu o versiune minimă de Android OS de 6.0.
Aplicația este dezvoltată pe rezoluția qHD(quad HD 2560×1440) dar se adaptează în fun cție de
rezoluția dispozitivului folosit (ex 1920×1080, 1280×800, 800×420). Aplicația este configurat ă pentru
a fi folosită exclus iv în modul landscape .
Ca și permisiuni generale aplica ția are nevoie de accesul la fi șierele utilizatorului, deorece
scrierea și citirea din diverse fi șiere de configurare au nevoie de permisiunea de a accesa aceste
fișiere. Aplica ția nu de ține nici o pagin ă de tip meniu, deoarece acestea nu pot fi integrate în cadrul
aplica țiilor de realitate virtual ă, astfel orice fereastr ă pentru interac țiunea utilizatorului cu jocul se face
în interiorul jocului și este setat ă în spa țiul 3D.
4.2 Camera
Interiorul camerei din care utilizatorul trebuie s ă scape are o structur ă foarte simpl ă și
elemente relativ pu ține în compara ție cu o camer ă normală. Am abordat acest stil deoarece aceast ă
aplica ție este realizat ă pentru un public relativ t ânăr (de la 7 ani p ână la vârsta de 18 ani) având scopul
de a-și dezvolta capabilit ățile de g ândire logic ă și de rezolvare a unor ghicitori ca dificultate simpl u
spre mediu. De asemenea testarea și jucarea pe un dispozitiv de realitate virtual ă pentru o perioada
lungă poate da st ări de ame țeală și nu este recomandat, de aceea jocul este f ăcut s ă fie completat în
aproximativ 5 -10 minute în func ție de v ârstă.

45

FIGURA 41 – Prezentare cameră
Scoaterea în eviden ță prin coloristic ă sau diverse eviden țieri ale obiectelor cu care se poate
interac ționa este o metod ă pasiv ă de a ajuta utilizatorul în rezolvarea ghicitorilor și terminarea jocului.
De asemenea folosirea unor lunimi calde și a unui mediu înconjurator verde (ref. Spa țiul
verde în afara camerei) are rolul de a face utilizatorul s ă se simt ă cât mai relaxat pentru a se putea
concentra pe realizarea obiectivelor.

FIGURA 42 – Cifrul de la ușa de ieșire din clădire

46
Colorarea cifrelor descoperite și a ordinii cifrelor în ecranul cifrului este la fel o tehnic ă prin
care îl las pe utilizator s ă știe care este ordinea în care trebuie s ă introduc ă cifrul, mai ales c ă poate
descoperi fiecare cifr ă în oricare ordine și la 4 cifre num ărul de combina ții este de 24 dac ă cunoa ște
cele 4 cifre și 10000 daca nu le cunoa ște.
4.3 Povestea din spatele jocului
Un juc ător devine mult mai pasionat de un joc dac ă acesta are o poveste și mai ales o poveste
în centrul c ăreia el se afla și pe care el o poate controla. Astfel pentru un sentiment de apartenen ță la
lumea virtual ă am hot ărât și dezvoltarea unei pove ști.
În cadrul pove știi din spatele jocului nostru, utilizatorul devine un detectiv care c ăutând
dovezi c ă cel pe care îl suspecteaz ă i-a ucis partenerul, acesta este blocat în casa acestuia, trebuind s ă
rezolve o serie de ghicitori (puzzle -uri) într-un timp limit ă, altfel un gaz otr ăvitor va fi eliberat.

FIGURA 43 – Prezentare poveste (partea – 1)
Comunicarea pove știi se face la începu t prin ferestre de tip pop -up. Personajul principal a fost
ales ca fiind detectiv tocmai pentru a da utilizatorului sentimentul de importan ță și să-i arate c ă el este
personajul principal, orice se întâmplă în aceast ă poveste ține numai și numai de el.

47

FIGURA 43.1 – Prezentare poveste (partea – 2)
Utilizatorul este apoi informat unde se afl ă si ce se întâmplă. Blocarea în cas ă și timpul limit ă
până la eliberarea gazului otr ăvitor are rolul de a -l face pe utilizator s ă trăiască cu emo ție să cread ă că
nu ar e nici o sc ăpare, dup ă care este informat c ă exist ă 4 cifre ascunse în cas ă care îi pot da libertatea
și il pot sc ăpa de la moarte, ceea ce îi dă utilizatorului speran ță.

FIGURA 43.2 – Prezentare poveste (partea – 3) FIGURA 43.3 – Prezent are poveste (partea – 4)
4.4 Rezolvarea ghicitorilor
În continuare voi prezenta care sunt ghicitorile, cum pot fi acestea rezolvate.

48
4.4.1 Statuia de por țelan
Prima dintre ghicitori este cuprins ă în Budha de por țelan. Pe masa imediat cum începem
putem observa c ă există pe mas ă o statuie a lui Budha. Dac ă merge m deasupra ei vom vedea c ă are un
contur de jur imprejur ceea ce ne las ă să știm c ă este un obiect cu care putem interac ționa. La un tap
pe statuie aceasta se rote ște 90 de grade. Iar rotirea acesteia la 360 de grade va face ca televizorul de
pe perete s ă înceap ă o anima ție de mi șcare c ătre dreapta care va descoperi prima cifr ă.

FIGURA 44 – Budha de porțelan, scoatere în evidență de selecția utilizatorului

49

FIGURA 44.1 – Descoperirea primei cifre după rotația statuii la 360 de grade
4.4.2 Cele 2 vaze și Martin Luther King Jr.
Următoarea ghicitoare ține de cele 2 vaze care au însemnul întelepciunii pe ele. Rotirea
vazelor p ână în momentul în care însemnul este cu fa ța către utilizator va face ca cele 2 lampi s ă-și
schimbe orientarea c ătre tabloul cu Martin Luther King Jr. Luminile s ă porneasc ă și să descopere cel
de-al 2-lea num ăr.

50

FIGURA 45 – Cea de -a doua cifră în tablou
4.4.3 Cartea de istorie
Cel de -al treilea puzzle se regase ște în bibliotec ă într-una din c ărți. Pent ru o g ăsire u șoară
numai 2 c ărti au fost create astfel încât utilizatorul s ă poată interac ționa cu ele, celelalte fiind obiecte
statice. În func ție de dificultatea pe care o dorim putem activa toat ă biblioteca ca interac ționabil ă,
astfel g ăsirea c ărții car e ne trebuie fiind mult mai grea.

51

FIGURA 46 – Prezentare carte interactivă din bibliotecă

FIGURA 46.1 – A treia cifră pe cartea de istorie

52
4.4.4 Lampa ascuns ă
Dacă primele 3 cifre sunt destul de u șor de g ăsit, cea de -a patra este mult mai complicat ă. De
ce? În primul r ând am vrut s ă scot utilizatorul din zona de confort și de asemenea am vrut s ă măresc
dificultatea. Astfel cel de -al 4-lea num ăr este de fapt lampa pe l ângă care trecem spre bibliotec ă.
Lampa nu are nici un fel de semn care s ă ne ajute s ă ne dăm seama c ă este un obiect cu care se poate
interac ționa, astfel utilizatorul va trebui s ă facă mai multe încerc ări până să o găseasc ă.

FIGURA 47 – Lampa împreuna cu cea de -a patra cifra
Interac țiunea cu aceasta o aprinde și pe tavanul camerei va fi dezval uită cea de -a 4-a cifr ă.
Menționez c ă toate din cele 4 cifre pot fi descoperite în orice ordine.
4.5 Poten țialul de dezvoltare
În principiu întreaga aplica ție fie ea chiar în realitate virtual ă a fost creat ă cu gândul de a fi
dezvoltat ă pe viitor. De la camer ă, care de fapt este o cas ă cu dou ă etaje (celelalte camere și etajul 1
au fost închise pentru c ă nu intr ă în scopul prezentei lucr ări). Dar prin crearea unor ghicitori într-o
întreag ă casă, timpul de joc cre ște relativ și aplica ția trebuie împărțită pe niv ele, la care se adaug ă o
salvare a progresului destul de deas ă.

53

FIGURA 48 – Privire clădire de ansamblu
De asemenea interac țiunea cu orice tip de obiect se poate foarte u șor cu func ționalitatea deja
existent ă sau se pot crea interac țiuni mult mai comple xe bazate pe cele ce deja exist ă. De asemenea
ghicitorile care deja exist ă pot fi schimbate sau ad ăugate altele foarte u șor, func ționalitatea de baz ă
deja fiind existent ă.
O astfel de aplica ție pe l ângă dezvoltarea capacit ăților logice poate fi adaptat ă foarte u șor într-
o sal ă de curs virtual ă sau într-un laborator virtual în care un student poate face experimente care sunt
periculoase sau greu de realizat cu aparatura curent ă.
Folosirea unei aparaturi de realitate virtual ă devine din ce în ce mai viabil ă, mai ales c ă deja a
început o ieftinire u șoara din partea unor companii cum ar fi HTC. Astfel dec ât achizi ționarea unei
aparaturi care cost ă câteva sute de mii de dolari, se poate ob ține aparatur ă de realitate virtual ă care
simuleaz ă foarte bine ceea ce și aparatura original ă realizeaz ă.

54
5 CONCLUZII
Aceast ă aplica ție a fost creat ă pentru a ajuta persoanele p ână în 18 ani în dezvoltarea g ândirii
logice și descurcarea în situa ții limit ă prin crearea unui mediu relaxant, de joc.
În final putem spune c ă realizarea unei aplica ții de realitate virtual ă este o încercare destul de
mare pentru cineva care nu a mai realizat ceva asem ănător. Crearea unei astfel de aplica ții consum ă
foarte mult timp, mai ales că tot ceea ce ține de interac țiunea utilizatorului cu med iul virtual și cu
programarea mediului virtual ține specific de platforma pe care se face dezvoltarea.
De asemenea existen ța unei libert ăți în modul în care utilizatorul interac ționează cu mediul
virtual consum ă de asemenea foarte mult timp, deoarece de mu lte ori trebuie încercate toate
posibilit ățile și aleas ă cea care se pliaz ă cel mai bine pe aplica ția dezvoltat ă. Alegerea de exemplu
între mi șcarea prin joystick și teleportarea între diverse puncte a fost o alegere bazat ă pe testarea
aplica ției de c ătre persoane din afar ă și adunarea de feedback.
De foarte multe ori încercarea de a crea ceva nou în realitate a virtual ă se arat ă foarte
complex ă numai prin faptul c ă aceast ă tehnologie este abia la început și documenta ția și suportul
pentru ea este foarte lim itat. De foarte multe ori pot spune c ă m-am lovit de probleme la care chiar cei
care dezvolt ă platformele pentru crearea jocurilor de realitate virtual ă nu cunosc r ăspunsul pentru că
nu au avut acea problem ă, iar intreb ările pe forumuri sau grupurile de sp ecialitate r ămân ner ăspunse.
Optimizarea aplica ției a fost probabil cea mai costisitoare parte din punct de vedere al
timpului, dar prin realizarea unei optimiz ări foarte complexe am vrut s ă arăt că jocurile și aplica țiile în
cadrul realit ății virtuale au un poten țial enorm și pot dep ăși limitele pe care în general companiile care
creaz ă platformele pentru crearea jocurilor le dau. Scopul principal al acestei lucr ări a fost s ă ating
limitele unei aplica ții de realitate virtual ă și mai apoi prin diferite pro cedee de optimizare s ă le
depășesc.
În concluzie putem spune c ă realizarea acestei aplica ții a fost cea mai mare încercare de p ână
acum și a reu șit să mă ajute s ă îmi dezvolt at ât percep ția despre arhitectura unui joc în general și unui
joc în realitate vi rtuala în special, dar și să învăț noi procedee de programare și optimizare.

55
6 BIBLIOGRAFIE

 Introduction to Game Design, Prototyping, and Development, by Jeremy Gibson,
2016

 Game Programming Patterns, by Robert Nystrom, 2014

 Head First C#, 3rd Edition, by O’Reilly Media, 2013

 Head First Desing Patterns, by O’Reilly Media, 2009

 Learning C# by Developing Games with Unity 3D, by Terry Norton, 2013

 Learning C# by Developing Games with Unity 5.x, by Greg Lukosek, 2016

 The C# Player' s Guide, by Robert Whitaker, 2015

 Unity in action ,by Joseph Hocking, 2015

 Unity Shaders and Effects Cookbook, by Kenny Lammers , 2013

 Unity 5.x Cookbook, by Matt Smith, 2015

56
7 REFERINȚE WEB
 Advanced Unity: 3D Game Programming –
https://www.linkedin.com/learning/advanced -unity -3d-game -programming/next –
steps?u=2118522
 C#: Delegates, Events and Lambdas – https://www.linkedin.com/learning/c -sharp –
delegates -events -and-lambdas/using -anonymous -delegates?u=2118522
 Game Development Foundations: Game -Related Math –
https://www.linkedin.com/learning/game -development -foundations -game -related –
math/next -steps?u=2118522
 Get started with Google VR in Unity o n Android –
https://developers.google.com/vr/unity/get -started
 Google VR SDK for Unity – https://developers.google.com/vr/unity/
 Learning OpenGL – https://www.linkedin.com/learning/learning -opengl/rotating –
translating -and-scaling?u=2118522
 Stackoverflow – https://stackoverflow.com/
 Unity 5: 2D Advanced UI – https://www.linkedin.com/learning/unity -5-2d-advanced –
ui/next -steps?u=2118522
 Unity 5: 3D Essential Training – https://www.linkedin.com/learning/unity -5-3d-
essential -training/setting -up-occlusion -culling?u=2118522
 Unity 5: Buil d a Character Dialogue System –
https://www.linkedin.com/learning/unity -5-build -a-character -dialogue –
system?u=2118522
 Unity Forum – https://forum.unity3d.com/
 Unity Live online training courses – https://unity3d.com/learn/live -training
 Unity User Manual (2017.1) – https://docs.unity3d.com/Manual/index.html
 Unity: Materials and Lighting – https://www.linkedin.com/learning/unity -materia ls-
and-lighting/adjusting -the-proximity -and-look?u=2118522
 Virtual Reality – https://unity3d.com/fr/learn/tutorials/topics/virtual -reality
 Virtual Reality Overview for Developer s – https://www.linkedin.com/learning/virtual –
reality -overview -for-developers/next -steps?u=2118522

57
A. CODUL SURSĂ
public class GameManager : MonoBehaviour {

public PopupsManager popupsManager;

public static GameManager gameManager;

private Repository repository;
private int currentStoryLine;
private int lastStory;
private string currentStory;

public int CurrentSt oryLine
{
get { return currentStoryLine; }
set {
if(currentStoryLine < lastStory)
currentStoryLine = value;
}
}

public string CurrentStory
{
get { return repository.StoryLines[cur rentStoryLine]; }
set { currentStory = value; }
}

public int LastStory
{
get { return lastStory; }
}

private void Awake()
{
if (gameManager == null)

//if not, set instance to this
gameManager = this;

//If instance already exists and it's not this:
else if (gameManager != this)

//Then destroy this. This enforces our singleton pattern, meaning there
can only ever be one instance of a GameManager.
Destroy(gameObject);

//Sets this to not be destroyed when reloading scene
DontDestroyOnLoad(gameObject);

popupsManager = new PopupsManager ();
popupsManager.OnAwake();
repository = new Repository ("en");
CurrentStoryLine = repository.CurrentStoryLine;
CurrentStory = repository.StoryLines[currentStoryLine];
lastStory = repository.StoryLines.Count -1;
}

58

private void Start()
{
popupsManager.OnStart();
}

private void Update()
{
popupsManager.OnUpdate();
}

private void OnDestroy ()
{
repository.CurrentStoryLine = currentStoryLine;
repository.SaveData();
}
}

public class DisplayManager : MonoBehaviour {

[SerializeField]
private GameObject firstDigit;
[SerializeField ]
private GameObject secondDigit;
[SerializeField ]
private GameObject thirdDigit;
[SerializeField ]
private GameObject fourthDigit;
[SerializeField ]
private Door door;

SevenSegmentDriver [] digits;

private string reset = "8";
// Use this for initialization
void Start () {
digits = new SevenSegmentDriver [4];
digits[0] = firstDigit.GetComponent< SevenSegmentDriver >();
digits[1] = secondDigit.GetComponent< SevenSegmentDriver >();
digits[2] = thirdDigit.GetComponent< SevenSegmentDriver >();
digits[3] = fourthDigit.GetComponent< SevenSegmentDriver >();
}

public void Reset()
{
for(int i = 0; i < digits.Le ngth; i++)
{
digits[i].Data = reset;
}
}

public void Submit()
{
door.StartCoroutine( "Move");
}

public void KeyPress( string value)
{
for(int i = 0; i < digits.Length; i++)

59
{
if(digits[i].Data == reset)
{
digits[i].Data = value;
break;
}
}
}
}

public class PopupsManager {

private List<BasePopup > popupList = new List<BasePopup >();
// Use this for i nitialization
public void OnAwake() { }

public void OnStart () {

}

// Update is called once per frame
public void OnUpdate () {

}

public void RegisterPopup( BasePopup popup)
{
popupList.Add(popup);
popupList[ 0].Show();
}
}
public class KeypadManager : MonoBehaviour {

[SerializeField ]
private DisplayManager displayManager;

public void KeyPress( string value)
{
displayManager.KeyPress(value);
}

}
public class InteractableOb ject : MonoBehaviour {

private GameObject clone = null;
[SerializeField ]
private Vector3 ObjectScale;

private void OnMouseOver ()
{
if (clone == null) {
clone = Instantiate(gameObject, gameObject.transform.position,
gameObject.transform.rotation, gameObject.transform);
Destroy(clone.GetComponent< InteractableObject >());
Destroy(clone.GetComponent< BoxCollider >());
Destroy(clone.GetComponent< CapsuleCollider >());
Destroy(cl one.GetComponent< Rigidbody >());

60
clone.transform.localScale = ObjectScale;
clone.GetComponent< Renderer >().material.shader =
Shader.Find("Custom/Anotherhighlight" );
}
}

private void OnMouseExit ()
{
clone.GetComponent< Renderer >().material.shader = Shader.Find("Diffuse" );
Destroy(clone);
}

}
public class MovableObject : InteractableObject
{
[SerializeField ]
protected float speed;
[SerializeField ]
protected float mass;

protected bool move = false;
protected Transform target;
protected Rigidbody rigidBody;

private void Start()
{
rigidBody = gameObject.AddComponent< Rigidbody >();
rigidBody.mass = mass;
rigidBody.useGravity = true;
rigidBody.freezeRotation = true;
}
private void OnMouseUp ()
{
move = true ? true : false;
rigidBody.useGravity = false;
rigidBody.isKinematic = true;
}

protected virtual void FixedUpdate ()
{
if (move)
{
target =
Camera.main.gameObject.transform.GetChild( Camera.main.gameObject.transform.childCount
– 1);
transform.position = Vector3.Lerp(transform.position, target.position,
Time.fixedDeltaTime * speed);
if (Input.GetMouseButtonDown(0))
{
rigidBody.useGravity = true;
rigidBody.isKinematic = false;
move = false;
}
}
}
}

61
public class RotatableObject : InteractableObject {

[SerializeField ]
private bool rotateX;
[SerializeField ]
private bool rotateY;
[SerializeField ]
private bool rotateZ;
[SerializeField ]
private float degrees;
[SerializeField ]
private float rotationSpeed;

private bool rotate = false;
private float initialRotation;
private int rotated = 0;

public int Rotated
{
get { return rotated; }
}

private void OnMouseUp ()
{
if (rotateZ)
{
StartCoroutine(RotateMe( Vector3.forward * degrees, rotationSpeed));
rotate = false;
}
if (rotateY)
{
StartCoroutine(RotateMe( Vector3.up * degrees, rotationSpeed));
rotate = false;
}
if (rotateX)
{
StartCoroutine(RotateMe( Vector3.right * degrees, rotationSpeed));
rotate = false;
}
}

IEnumerator RotateMe( Vector3 byAngles, float inTime)
{
var fromAngle = transform.rotation;
var toAngle = Quaternion.Euler(transform.eulerAngles + byAngles);
for (var t = 0f; t < 1; t += Time.deltaTime / inTime)
{
transform.rotation = Quaternion .Slerp(fromAngle, toAngle, t);
yield return null;
}
rotated++;
}
}

public class BookObject : MovableObject {

62

protected override void FixedUpdate()
{
if (move)
{
target =
Camera.main.gameObject.transform.GetChild( Camera.main.gameObject.transform.childCount
– 1);

transform.position = Vector3.Lerp(transform.position, target.position,
Time.fixedDeltaTime * speed);
Quaternion targetRotation = new Quaternion (target.rotation.x,
target.rotation.y – 1f, target.rotation.z, target.rotation.w);
//transform.rotation = Quaternion.Euler(0, 180, 0);
transform.rotation = Quaternion .Lerp(transform.rotation, targetRotation,
Time.fixedDeltaTime * speed);
if (Input.GetMouseButtonDown(0))
{
rigidBody.u seGravity = true;
rigidBody.isKinematic = false;
move = false;
}
}
}
}
public class LampAnimation : MonoBehaviour {

[SerializeField ]
private GameObject light;
private bool playAnimation = false;

private void Start()
{
light.SetActive( false);
}

public void PlayAnimation( string direction)
{
if(direction == "left" && !playAnimation)
{
Quaternion rotation = Quaternion .Euler(-40f, -90f, 90f);
transform.rotation = rotation;
light.SetActive( true);
playAnimation = true;
}
else if(direction == "right" && !playAnimation)
{
Quaternion rotation = Quaternion .Euler(-40f, 90f, -90f);
transform.rotation = rotation;
light.SetActive( true);
playAnimation = true;

}
}
}
public class LampObject : MonoBehaviour {

63
[SerializeField ]
private GameObject light;
[Serializ eField]
private GameObject spotLight;
// Use this for initialization
void Start () {
light.SetActive( false);
spotLight.SetActive( false);
}

private void OnMouseUp ()
{
light.SetActive( true);
spotLight.SetAc tive(true);
}

}
public class TvAnimation : MonoBehaviour {

private bool playAnimation;
private float speed = 1f;
private Vector3 target;
// Use this for initialization
void Start () {
playAnimation = false;
target = new Vector3(transform.position.x – 0.8f, transform.position.y,
transform.position.z);
}

// Update is called once per frame
void FixedUpdate () {
if (playAnimation)
{
transform.position = Vector3.Lerp(transform.position, target ,
Time.fixedDeltaTime * speed);
playAnimation = false;
}
}

public void PlayTvAnimation()
{
playAnimation = true;
}
}
public class BasePopup : MonoBehaviour {

[SerializeField ]
protected Text title;
[SerializeField ]
protected WritableText subtitle;
[SerializeField ]
protected GameObject ok;
[SerializeField ]
protected GameObject next;
protected GameManager gameManager;
protected PopupsManager popupsManager;

64

protected virtual void Awake()
{

}

protected virtual void Start()
{
gameManager = GameManager .gameManager;
popupsManager = gameManager.popupsManager;
Display( false);
}

public virtual void Show()
{
Display(true);
}

public virtual void Hide()
{
Display( false);
Clear();
}

protected void Display( bool state)
{
gameObject.SetActive(state);
}

public virtual void Clear()
{

}

}
public class StoryPopup : BasePopup {

protected override void Start()
{
base.Start();
popupsManager.RegisterPopup( this);
}

public override void Show()
{
base.Show();
title.text = "Story";
subtitle.text = gameManager.CurrentStory;
ok.SetActive( false);
transform.position = new Vector3(Camera.main.transform.position.x,
Camera.main.transform.position.y, -2f);
}

public override void Hide()
{
base.Hide();

65
}

public override void Clear()
{
base.Clear();
title.text = string.Empty;
subtitle.text = string.Empty;
}

public void NextStory()
{
if(gameManager.LastStory == gameManager.CurrentStoryLine)
{
ok.SetActive( true);
next.SetActive( false);
}
gameManager.CurrentStoryLine += 1;
subtitle.text = gameManager.CurrentStory;
}
}
public class WritableText : Text {

private string textToWrite;
private IEnumerator coroutine;

public override string text
{
get
{
return base.text;
}

set
{

coroutine = WriteText( value);
if(gameObject.activeSelf)
StartCoroutine(coroutine);
}
}

private IEnumerator WriteText( string textToWrite)
{
base.text = "";
foreach (var c in textToWrite)
{
base.text += c;
yield return new WaitForSeconds (0.2f);
}
}

}
public class CifreDiscoverability : MonoBehaviour {

66
[SerializeField ]
private GameObject statue;
[SerializeField ]
private GameObject tv;
[SerializeField ]
private GameObject leftVase;
[SerializeField ]
private GameObject rightVase;
[SerializeField ]
private GameObject rightLamp;
[SerializeField ]
private GameObject leftLamp;
[SerializeField ]
private GameObject spotLight;
[SerializeField ]
private GameObject secondNumber;

private Transform statueInitialPosition;
private RotatableObject statureRotated;
private TvAnimation tvAnimation;
private RotatableObject leftVaseRotated;
private RotatableObject rightVaseRotated;
private LampAnimation leftLampAnimat ion;
private LampAnimation rightLampAnimation;

// Use this for initialization
void Start () {
statueInitialPosition = statue.transform;
statureRotated = statue.GetComponent< RotatableObject >();
leftVaseRotated = leftVase .GetComponent< RotatableObject >();
rightVaseRotated = rightVase.GetComponent< RotatableObject >();
tvAnimation = tv.GetComponent< TvAnimation >();
leftLampAnimation = leftLamp.GetComponent< LampAnimation >();
rightLampAnimation = r ightLamp.GetComponent< LampAnimation >();
spotLight.SetActive( false);
secondNumber.SetActive( false);
}

// Update is called once per frame
void LateUpdate () {
if(statureRotated.Rotated == 4)
{
tvAnimation.PlayTvA nimation();
}

if(leftVaseRotated.Rotated == 1 || rightVaseRotated.Rotated == 3)
{
leftLampAnimation.PlayAnimation( "left");
rightLampAnimation.PlayAnimation( "right");
spotLight.SetActive( true);
secondNumber.SetActive( true);
}
}
}
public class Repository {

private Dictionary <int, string> storyLines;
private string storyLinePath = @"Assets \StoryFiles \Story_";
private string saveDataPath = @"Assets \StoryFiles \SavedDat a.csv";

67
private string filePath = ".csv";
private string language;
private int currentLine = 0;

public Repository( string language)
{
this.language = language;

storyLines = new Dictionary <int, string>();
ReadStoryLines();
LoadData();
}

public Dictionary <int, string> StoryLines{
get { return storyLines; }
}

public int CurrentStoryLine
{
get { return currentLine; }
set { currentLine = value; }
}

private void ReadStoryLines()
{
language = language.Insert(language.Length, filePath);
var directory = Environment .CurrentDirectory;
storyLinePath = storyLinePath.Insert(storyLinePath.Length, language);
var path = Path.Combine(directory, storyLinePath);
using (var reader = new StreamReader (path))
{
while (!reader.EndOfStream)
{
var line = reader.ReadLine();
var values = line.Split( ',');
int key = int.Parse(values[0]);
storyLines.Add(key, values[1]);
}
}
}

public void SaveData()
{
var directory = Environment .CurrentDirectory;
var path = Path.Combine(directory, saveDataPath );
using (var writer = new StreamWriter (path))
{
writer.WriteLine(currentLine);
}
}

public void LoadData()
{
var directory = Environment .CurrentDirectory;
var path = Path.Combine(directory, s aveDataPath);
using (var reader = new StreamReader (path))
{
CurrentStoryLine = int.Parse(reader.ReadLine());
}
}

68

}
public abstract class VirtualInput
{
public Vector3 virtualMousePosition { get; private set; }

protected Dictionary <string, CrossPlatformInputManager .VirtualAxis >
m_VirtualAxes =
new Dictionary <string, CrossPlatformInputManager .VirtualAxis >();
// Dictionary to store the name relating to the vi rtual axes
protected Dictionary <string, CrossPlatformInputManager .VirtualButton >
m_VirtualButtons =
new Dictionary <string, CrossPlatformInputManager .VirtualButton >();
protected List<string> m_AlwaysUseVirtual = new List<string>();
// list of the axis and button names that have been flagged to always use
a virtual axis or button

public bool AxisExists( string name)
{
return m_VirtualAxes.ContainsKey(name);
}

public bool ButtonExists( string name)
{
return m_VirtualButtons.ContainsKey(name);
}

public void RegisterVirtualAxis( CrossPlatformInputManager .VirtualAxis axis)
{
// check if we already have an axis with t hat name and log and error if we
do
if (m_VirtualAxes.ContainsKey(axis.name))
{
Debug.LogError( "There is already a virtual axis named " + axis.name +
" registered." );
}
else
{
// add any new axes
m_VirtualAxes.Add(axis.name, axis);

// if we dont want to match with the input manager setting then revert
to always using virtual
if (!axis.matchWithInputManager)
{
m_AlwaysUseVirtual.Add(axis.name);
}
}
}

public void RegisterVirtualButton( CrossPlatformInputManager .VirtualButton
button)
{
// check if already have a buttin with that name and log an error if we do
if (m_VirtualButtons.ContainsKey(button.name))

69
{
Debug.LogError( "There is already a virtual button named " +
button.name + " registered." );
}
else
{
// add any new buttons
m_VirtualButtons.Add(button.name, button);

// if we dont want to match to the input manager then always use a
virtual axis
if (!button.matchWithInputManager)
{
m_AlwaysUseVirtual.Add(button.name);
}
}
}

public void UnRegisterVirtualAxis( string name)
{
// if we have an axis with that name then remove it from our dic tionary of
registered axes
if (m_VirtualAxes.ContainsKey(name))
{
m_VirtualAxes.Remove(name);
}
}

public void UnRegisterVirtualButton( string name)
{
// if we have a b utton with this name then remove it from our dictionary
of registered buttons
if (m_VirtualButtons.ContainsKey(name))
{
m_VirtualButtons.Remove(name);
}
}

// returns a reference to a nam ed virtual axis if it exists otherwise null
public CrossPlatformInputManager .VirtualAxis VirtualAxisReference( string name)
{
return m_VirtualAxes.ContainsKey(name) ? m_VirtualAxes[name] : null;
}

public void SetVirtualMousePositionX( float f)
{
virtualMousePosition = new Vector3(f, virtualMousePosition.y,
virtualMousePosition.z);
}

public void SetVirtualMousePositionY( float f)
{
virtualMousePosition = new Vector3(virtualMousePosition.x, f,
virtualMousePosition.z);
}

70

public void SetVirtualMousePositionZ( float f)
{
virtualMousePosition = new Vector3(virtualMousePosition.x,
virtualMousePosition.y, f);
}

public abstract float GetAxis( string name, bool raw);

public abstract bool GetButton( string name);
public abstract bool GetButtonDown( string name);
public abstract bool GetButtonUp( string name);

public abstract void SetButtonDown( string name);
public abstract void SetButtonUp( string name);
public abstract void SetAxisPositive( string name);
public abstract void SetAxisNegative( string name);
public abstract void SetAxisZero( string name);
public abstract void SetAxis( string name, float value);
public abstract Vector3 MousePosition();
}
[RequireComponent (typeof(Image))]
public class TouchPad : MonoBehaviour , IPointerDownHandler , IPointerUpHandler
{
// Options for whi ch axes to use
public enum AxisOption
{
Both, // Use both
OnlyHorizontal, // Only horizontal
OnlyVertical // Only vertical
}

public enum ControlStyle
{
Absolute, // operates from teh center of the image
Relative, // operates from the center of the initial touch
Swipe, // swipe to touch touch no maintained center
}

public AxisOption axesToUse = AxisOption .Both; // The options for the
axes that the still will use
public ControlStyle controlStyle = ControlStyle .Absolute; // control
style to use
public string horizontalAxisName = "Horizontal" ; // The name given to
the horizontal axis for the cross platform input
public string verticalAxisName = "Vertical" ; // The name given to the
vertical axis for the cross platform inp ut
public float Xsensitivity = 1f;
public float Ysensitivity = 1f;

Vector3 m_StartPos;
Vector2 m_PreviousDelta;
Vector3 m_JoytickOutput;
bool m_UseX; // Toggle for using the x axis
bool m_UseY; // Toggle for using the Y axis

71
CrossPlatformI nputManager .VirtualAxis m_HorizontalVirtualAxis; //
Reference to the joystick in the cross platform input
CrossPlatformInputManager .VirtualAxis m_VerticalVirtualAxis; //
Reference to the joystick in the cross platform input
bool m_Dragging;
int m_Id = -1;
Vector2 m_PreviousTouchPos; // swipe style control touch

#if !UNITY_EDITOR
private Vector3 m_Center;
private Image m_Image;
#else
Vector3 m_PreviousMouse;
#endif

void OnEnable ()
{
CreateVirtualAxes();
}

void Start()
{
#if !UNITY_EDITOR
m_Image = GetComponent<Image>();
m_Center = m_Image.transform.position;
#endif
}

void CreateVirtualAxes()
{
// set axes to use
m_UseX = (axesToUse == AxisOption .Both || axesToUse ==
AxisOption.OnlyHorizontal);
m_UseY = (axesToUse == AxisOption .Both || axesToUse ==
AxisOption .OnlyVertical);

// create new axes based on axes to use
if (m_UseX)
{
m_HorizontalVirtualAxis = new
CrossPlatformInputManager .VirtualAxis (horizontalAx isName);

CrossPlatformInputManager .RegisterVirtualAxis(m_HorizontalVirtualAxis);
}
if (m_UseY)
{
m_VerticalVirtualAxis = new
CrossPlatformInputManager .VirtualAxis (verticalAxisName);

CrossPlatformInputManager .RegisterVirtualAxis(m_Verti calVirtualAxis);
}
}

void UpdateVirtualAxes( Vector3 value)
{
value = value.normalized;
if (m_UseX)
{
m_HorizontalVirtualAxis.Update(value.x);

72
}

if (m_UseY)
{
m_VerticalVirtualAxis.Update(value.y);
}
}

public void OnPointerDown( PointerEventData data)
{
m_Dragging = true;
m_Id = data.pointerId;
#if !UNITY_EDITOR
if (controlStyle != ControlStyle.Absolute )
m_Center = data.position;
#endif
}

void Update()
{
if (!m_Dragging)
{
return;
}
if (Input.touchCount >= m_Id + 1 && m_Id != -1)
{
#if !UNITY_EDITOR

if (controlStyle == ControlStyle.Swipe)
{
m_Center = m_PreviousTouchPos;
m_PreviousTouchPos = Input.touches [m_Id].position;
}
Vector2 pointerDelta = new Vector2(Input.touches[m_Id].position.x –
m_Center.x , Input.touches[m_Id].position.y – m_Center.y).normalized;
pointerDelta.x *= Xsensitivity;
pointerDelta.y *= Y sensitivity;
#else
Vector2 pointerDelta;
pointerDelta.x = Input.mousePosition.x –
m_PreviousMouse.x;
pointerDelta.y = Input.mousePosition.y –
m_PreviousMouse.y;
m_PreviousMouse = new Vector3(Input.mousePosition.x,
Input.mousePosition.y, 0f) ;
#endif
UpdateVirtualAxes( new Vector3(pointerDelta.x,
pointerDelta.y, 0));
}
}

public void OnPointerUp( PointerEventData data)
{
m_Dragging = false;
m_Id = -1;
UpdateVirtualAxes( Vector3.zero);
}

73
void OnDisable ()
{
if (CrossPlatformInputManager .AxisExists(horizontalAxisName))

CrossPlatformInputManager .UnRegisterVirtualAxis(horizontalAxisName);

if (CrossPlatformInputManager .AxisExists(verticalAxisName))

CrossPlatformInputManager .UnRegisterVirtualAxis(verticalAxisN ame);
}
}
public class Joystick : MonoBehaviour , IPointerDownHandler , IPointerUpHandler ,
IDragHandler
{
public enum AxisOption
{
// Options for which axes to use
Both, // Use both
OnlyHorizontal, // Only horizontal
OnlyVertical // Only vertical
}

public int MovementRange = 100;
public AxisOption axesToUse = AxisOption .Both; // The options for the
axes that the still will use
public string horizontalAxisName = "Horizontal" ; // The name given to
the horizontal axis for the cross p latform input
public string verticalAxisName = "Vertical" ; // The name given to the
vertical axis for the cross platform input

Vector3 m_StartPos;
bool m_UseX; // Toggle for using the x axis
bool m_UseY; // Toggle for using the Y axis
CrossPlatf ormInputManager .VirtualAxis m_HorizontalVirtualAxis; //
Reference to the joystick in the cross platform input
CrossPlatformInputManager .VirtualAxis m_VerticalVirtualAxis; //
Reference to the joystick in the cross platform input

void OnEnable ()
{
CreateVirtualAxes();
}

void Start()
{
m_StartPos = transform.position;
}

void UpdateVirtualAxes( Vector3 value)
{
var delta = m_StartPos – value;
delta.y = -delta.y;
delta /= MovementRange;
if (m_UseX)
{
m_HorizontalVirtualAxis.Update( -delta.x);
}

if (m_UseY)

74
{
m_VerticalVirtualAxis.Update(delta.y);
}
}

void CreateVirtualAxes()
{
// set axes to use
m_UseX = (axesToUse == AxisOption .Both || axesToUse ==
AxisOption .OnlyHorizontal);
m_UseY = (axesToUse == AxisOption .Both || axesToUse ==
AxisOption .OnlyVertical);

// create new axes based on axes to use
if (m_UseX)
{
m_HorizontalVirtualAxis = new
CrossPlatformInputManager .VirtualAxis (horizontalAxisName);

CrossPlatformInputManager .RegisterVirtualAxis(m_HorizontalVirtualAxis);
}
if (m_UseY)
{
m_VerticalVirtualAxis = new
CrossPlatformInputManager .VirtualAxis (verticalAxisName);

CrossPlatformInputManager .RegisterVirtualAxis(m_VerticalVirtual Axis);
}
}

public void OnDrag(PointerEventData data)
{
Vector3 newPos = Vector3.zero;

if (m_UseX)
{
int delta = ( int)(data.position.x – m_StartPos.x);
delta = Mathf.Clamp(delta, – MovementRange,
MovementRange);
newPos.x = d elta;
}

if (m_UseY)
{
int delta = ( int)(data.position.y – m_StartPos.y);
delta = Mathf.Clamp(delta, -MovementRange, MovementRange);
newPos.y = delta;
}
transform.position = new Vector3(m_StartPos.x + newPos.x,
m_StartPos.y + new Pos.y, m_StartPos.z + newPos.z);
UpdateVirtualAxes(transform.position);
}

public void OnPointerUp( PointerEventData data)
{
transform.position = m_StartPos;
UpdateVirtualAxes(m_StartPos);
}

75

public void OnPointerDown( PointerEventData data) { }

void OnDisable ()
{
// remove the joysticks from the cross platform input
if (m_UseX)
{
m_HorizontalVirtualAxis.Remove();
}
if (m_UseY)
{
m_VerticalVirtualAxis.Remove();
}
}
}
}
public static class CrossPlatform InputManager
{
public enum ActiveInputMethod
{
Hardware,
Touch
}

private static VirtualInput activeInput;

private static VirtualInput s_TouchInput;
private static VirtualInput s_HardwareInput;

static CrossPlatformInputManager()
{
s_TouchInput = new MobileInput ();
s_HardwareInput = new StandaloneInput ();
#if MOBILE_INPUT
activeInput = s_TouchInput;
#else
activeInput = s_HardwareInput;
#endif
}

public static void SwitchActiveInputMethod( ActiveInputMethod
activeInputMethod)
{
switch (activeInputMethod)
{
case ActiveInputMethod .Hardware:
activeInput = s_HardwareInput;
break;

case ActiveInputMethod .Touch:
activeInput = s_TouchInput;
break;
}
}

76
public static bool AxisExists( string name)
{
return activeInput.AxisExists(name);
}

public static bool ButtonExists( string name)
{
return activeInput.ButtonExists(name);
}

public static void RegisterVirtualAxis( VirtualAxis axis)
{
activeInput.RegisterVi rtualAxis(axis);
}

public static void RegisterVirtualButton( VirtualButton button)
{
activeInput.RegisterVirtualButton(button);
}

public static void UnRegisterVirtualAxis( string name)
{
if (name == null)
{
throw new ArgumentNull Exception ("name");
}
activeInput.UnRegisterVirtualAxis(name);
}

public static void UnRegisterVirtualButton( string name)
{
activeInput.UnRegisterVirtualButton(name);
}

// returns a reference to a named virtual axis if it exists otherw ise
null
public static VirtualAxis VirtualAxisReference( string name)
{
return activeInput.VirtualAxisReference(name);
}

// returns the platform appropriate axis for the given name
public static float GetAxis( string name)
{
return GetAxis(name, false);
}

public static float GetAxisRaw( string name)
{
return GetAxis(name, true);
}

// private function handles both types of axis (raw and not raw)

77
private static float GetAxis( string name, bool raw)
{
return activeInput. GetAxis(name, raw);
}

// – Button handling –
public static bool GetButton( string name)
{
return activeInput.GetButton(name);
}

public static bool GetButtonDown( string name)
{
return activeInput.GetButtonDown(name);
}

public static bool GetButtonUp( string name)
{
return activeInput.GetButtonUp(name);
}

public static void SetButtonDown( string name)
{
activeInput.SetButtonDown(name);
}

public static void SetButtonUp( string name)
{
activeInput.SetButton Up(name);
}

public static void SetAxisPositive( string name)
{
activeInput.SetAxisPositive(name);
}

public static void SetAxisNegative( string name)
{
activeInput.SetAxisNegative(name);
}

public static void SetAxisZero( string name)
{
activeInput.SetAxisZero(name);
}

public static void SetAxis( string name, float value)
{
activeInput.SetAxis(name, value);
}

78

public static Vector3 mousePosition
{
get { return activeInput.MousePosition(); }
}

public static void SetVirtualMousePositionX( float f)
{
activeInput.SetVirtualMousePositionX(f);
}

public static void SetVirtualMousePositionY( float f)
{
activeInput.SetVirtualMousePositionY(f);
}

public static void SetVirtualMousePositionZ( float f)
{
activeInput.SetVirtualMousePositionZ(f);
}

// virtual axis and button classes – applies to mobile input
// Can be mapped to touch joysticks, tilt, gyro, etc, depending on
desired implementation.
// Could also be implemented by other in put devices – kinect, electronic
sensors, etc
public class VirtualAxis
{
public string name { get; private set; }
private float m_Value;
public bool matchWithInputManager { get; private set; }

public VirtualAxis( string name)
: this(name, true)
{
}

public VirtualAxis( string name, bool matchToInputSettings)
{
this.name = name;
matchWithInputManager = matchToInputSettings;
}

// removes an axes from the cross platform input system
public void Remove()
{
UnRegisterVirtualAxis(name);
}

// a controller gameobject (eg. a virtual thumbstick) should
update this class
public void Update(float value)

79
{
m_Value = value;
}

public float GetValue
{
get { return m_Value; }
}

public float GetValueRaw
{
get { return m_Value; }
}
}

// a controller gameobject (eg. a virtual GUI button) should call the
// 'pressed' function of this class. Other objects can then read the
// Get/Down/Up state of this button.
public class VirtualButton
{
public string name { get; private set; }
public bool matchWithInputManager { get; private set; }

private int m_LastPressedFrame = -5;
private int m_ReleasedFrame = -5;
private bool m_Pressed;

public VirtualButton(string name)
: this(name, true)
{
}

public VirtualButton( string name, bool matchToInputSettings)
{
this.name = name;
matchWithInputManager = matchToInputSettings;
}

// A controller gameobject should call this functi on when the
button is pressed down
public void Pressed()
{
if (m_Pressed)
{
return;
}
m_Pressed = true;
m_LastPressedFrame = Time.frameCount;
}

// A controller gameobject should call this function when the
button is r eleased
public void Released()
{

80
m_Pressed = false;
m_ReleasedFrame = Time.frameCount;
}

// the controller gameobject should call Remove when the button
is destroyed or disabled
public void Remove()
{
UnRegisterVirtualButton (name);
}

// these are the states of the button which can be read via the
cross platform input system
public bool GetButton
{
get { return m_Pressed; }
}

public bool GetButtonDown
{
get
{
return m_LastPressedFrame – Time.frameCount == -1;
}
}

public bool GetButtonUp
{
get
{
return (m_ReleasedFrame == Time.frameCount – 1);
}
}
}
}
}
public class ButtonHandler : MonoBehaviour
{

public string Name;

void OnEnable()
{

}

public void SetDownState()
{
CrossPlatformInputManager .SetButtonDown(Name);
}

public void SetUpState()
{
CrossPlatformInputManager .SetButtonUp(Name);

81
}

public void SetAxisPositiveState()
{
CrossPlatformInputManager .SetAxisPositive(Name);
}

public void SetAxisNeutralState()
{
CrossPlatformInputManager .SetAxisZero(Name);
}

public void SetAxisNegativeState()
{
CrossPlatformInputManager .SetAxisNegative(Name);
}

public void Update()
{

}
}
}
public class AxisTouchButton : MonoBehaviour , IPointerDownHandler , IPointerUpHandle r
{
// designed to work in a pair with another axis touch button
// (typically with one having -1 and one having 1 axisValues)
public string axisName = "Horizontal" ; // The name of the axis
public float axisValue = 1; // The axis that the value ha s
public float responseSpeed = 3; // The speed at which the axis touch
button responds
public float returnToCentreSpeed = 3; // The speed at which the button
will return to its centre

AxisTouchButton m_PairedWith; // Which button this one is paired with
CrossPlatformInputManager .VirtualAxis m_Axis; // A reference to the
virtual axis as it is in the cross platform input

void OnEnable ()
{
if (!CrossPlatformInputManager .AxisExists(axisName))
{
// if the axis doesnt exist create a new on e in cross
platform input
m_Axis = new
CrossPlatformInputManager .VirtualAxis (axisName);
CrossPlatformInputManager .RegisterVirtualAxis(m_Axis);
}
else
{
m_Axis =
CrossPlatformInputManager .VirtualAxisReference(axisName);
}
FindPair edButton();
}

82
void FindPairedButton()
{
// find the other button witch which this button should be paired
// (it should have the same axisName)
var otherAxisButtons = FindObjectsOfType( typeof(AxisTouchButton ))
as AxisTouchButton [];

if (otherAxisButtons != null)
{
for (int i = 0; i < otherAxisButtons.Length; i++)
{
if (otherAxisButtons[i].axisName == axisName &&
otherAxisButtons[i] != this)
{
m_PairedWith = otherAxisButtons[i];
}
}
}
}

void OnDisable()
{
// The object is disabled so remove it from the cross platform
input system
m_Axis.Remove();
}

public void OnPointerDown( PointerEventData data)
{
if (m_PairedWith == null)
{
FindPairedButton();
}
// update the axis and record that the button has been pressed
this frame
m_Axis.Update( Mathf.MoveTowards(m_Axis.GetValue, axisValue,
responseSpeed * Time.deltaTime));
}

public void OnPointerUp( PointerEventData data)
{
m_Axis.Update( Mathf.MoveTowards(m_Axis.Ge tValue, 0, responseSpeed
* Time.deltaTime));
}
}
}
public class MobileInput : VirtualInput
{
private void AddButton( string name)
{
// we have not registered this button yet so add it, happens in the
constructor
CrossPlatformInputManager .RegisterVirtualButton( new
CrossPlatformInputManager .VirtualButton (name));
}

83
private void AddAxes( string name)
{
// we have not registered this button yet so add it, happens in the
constructo r
CrossPlatformInputManager .RegisterVirtualAxis( new
CrossPlatformInputManager .VirtualAxis (name));
}

public override float GetAxis( string name, bool raw)
{
if (!m_VirtualAxes.ContainsKey(name))
{
AddAxes(name);
}
return m_VirtualAxes[name].GetValue;
}

public override void SetButtonDown( string name)
{
if (!m_VirtualButtons.ContainsKey(name))
{
AddButton(name);
}
m_VirtualButtons[name].Pressed();
}

public override void SetButtonUp( string name)
{
if (!m_VirtualButtons.ContainsKey(name))
{
AddButton(name);
}
m_VirtualButtons[name].Released();
}

public override void SetAxisPositive( string name)
{
if (!m_VirtualAxes.ContainsKey(name))
{
AddAxes(name);
}
m_VirtualAxes[name].Update(1f);
}

public override void SetAxisNegative( string name)
{
if (!m_VirtualAxes.ContainsKey(name))
{
AddAxes(name);
}
m_VirtualAxes[name]. Update(-1f);
}

public override void SetAxisZero( string name)

84
{
if (!m_VirtualAxes.ContainsKey(name))
{
AddAxes(name);
}
m_VirtualAxes[name].Update(0f);
}

public override void SetAxis( string name, float value)
{
if (!m_VirtualAxes.ContainsKey(name))
{
AddAxes(name);
}
m_VirtualAxes[name].Update(value);
}

public override bool GetButtonDown( string name)
{
if (m_VirtualButtons.ContainsKey(name))
{
return m_VirtualButtons[name].GetButtonDown;
}

AddButton(name);
return m_VirtualButtons[ name].GetButtonDown;
}

public override bool GetButtonUp( string name)
{
if (m_VirtualButtons.ContainsKey(name))
{
return m_VirtualButtons[name].GetButtonUp;
}

AddButton(name);
return m_VirtualButtons[name].GetButtonUp;
}

public override bool GetButton( string name)
{
if (m_VirtualButtons.ContainsKey(name))
{
return m_VirtualButtons[name].GetBu tton;
}

AddButton(name);
return m_VirtualButtons[name].GetButton;
}

public override Vector3 MousePosition()
{
return virtualMousePosition;
}
}

85
}
public class StandaloneI nput : VirtualInput
{
public override float GetAxis( string name, bool raw)
{
return raw ? Input.GetAxisRaw(name) : Input.GetAxis(name);
}

public override bool GetButton( string name)
{
return Input.GetButton(name);
}

public override bool GetButtonDown( string name)
{
return Input.GetButtonDown(name);
}

public override bool GetButtonUp( string name)
{
return Input.GetButtonUp(name);
}

public override void SetButtonDown( string name)
{
throw new Exception (
" This is not possible to be called for standalone input. Please check
your platform and code where this is calle d");
}

public override void SetButtonUp( string name)
{
throw new Exception (
" This is not possible to be called for standalone input. Please check
your platform and code where this is called" );
}

public override void SetAxisPositive( string name)
{
throw new Exception (
" This is not possible to be called for standalone input. Please check
your platform and code where this is called" );
}

public override void SetAxisNegative( string name)
{
throw new Exception (
" This is not possible to be called for standalone input. Please check
your platform and code where this is called" );
}

86
public override void SetAxisZero( string name)
{
throw new Exception (
" This is not possible to be called for standalone input. Please check
your platform and code where this is called" );
}

public override void SetAxis( string name, float value)
{
throw new Exception (
" This is not possible to be called for standalone input. Please check
your platform and code where this is called" );
}

public override Vector3 MousePosition()
{
return Input.mousePosition;
}
}
}

87
B. CD / DVD
Autorul atașează în această anexă obligatorie , versiunea electronică a aplicației, a acestei
lucrări, precum și prezentarea finală a tezei .

Similar Posts