În cadrul temei 2, veți avea de implementat un joc de curse de mașini parțial. Va trebui să creați pista cursei, controlul mașinii pe pistă, un set de obstacole dinamice ce vor avea un traseu predefinit pe pistă și vor substitui mașinile adversare cu care se realizează întrecerea și un efect de curbură a pistei din poziția mașinii.
Puteți viziona un demo construit pe baza frameworkului de laborator care acoperă cerințele detaliate mai jos.
Pentru a genera o pistă pe care să meargă mașina, este nevoie inițial de o mulțime de puncte care să o definească. Mulțimea este formată din puncte în planul XOZ (2D) și poate fi obținută folosind orice tool (chiar și foaia si pixul). Pentru exemplul următor s-a folosit Geogebra (https://www.geogebra.org/classic).
Menționăm că pista de curse din demo este una complexă. Pentru simplitate, în toate explicațiile ce vor urma se va folosi o pistă generată de un număr mic de puncte.
Geometria pistei de curse trebuie generată prin asamblare din cod. Nu se poate crea geometria într-un software extern și importa în aplicație.
Explicația care urmează este o recomandare de generare a geometriei pistei, dar aceasta poate fi asamblată în orice fel, atât timp cât sunt respectate cele 2 restricții de mai sus.
Generarea pistei de curse se poate realiza conform pașilor de mai jos. O reprezentare vizuală a rezultatelor este prezentată în continuare.
Pentru a genera geometria pistei (o suprafață 2D), este nevoie de 2 mulțimi de puncte: una interioară și una exterioară. Fiecare segment exterior este (aproximativ) paralel cu omologul lui din interior.
Aceste 2 mulțimi se pot obține folosind tool-urile deja discutate, însă este destul de costisitor la nivel de timp. O soluție mai elegantă este prezentată în continuare, unde de la o mulțime de puncte putem genera oricâte mulțimi de puncte paralele cu mulțimea inițială (paralelism la nivel de segmente).
Să presupunem că avem 2 puncte consecutive P1(x1, 0, z1), P2(x2, 0, z2) din mulțimea de puncte care definește traseul:
$$ P1(x1, 0, z1) P2(x2, 0, z2) \\ \vec{D} = P2 - P1 \\ \vec{P} = cross(\vec{D}, \vec{UP}), \vec{UP} = (0, 1, 0) \\ R = P1 + distRoșu * \vec{P} \\ A = P1 - distAlbastru * \vec{P} $$
Astfel, dacă aplicăm același lucru pentru toate segmentele, obținem noile mulțimi de puncte exterioare / interioare:
Având cele 2 mulțimi (exterioară și interioară), putem să construim suprafața pistei. Presupunem că avem punctele exterioare r1, r2, r3 și cele interioare a1, a2, a3. Trebuie să se construiască cele 4 triunghiuri (r1, a1, a2), (r1, a2, r2), (r2, a2, a3). (r2, a3, r3).
Astfel, dacă aplicăm același lucru pentru toate punctele din ambele mulțimi obținem pista.
O posibilă generare mai bună utilizează media perpendicularelor pe 2 segmente consecutive pentru a crea punctele de pe cele două poligoane, interior și exterior.
Pe pistă, pe lângă mașina jucătorului, trebuie să se deplaseze și alte mașini (adversari). Aceștia se deplasează pe un traseu predefinit paralel cu mulțimea generatoare de pistă. Acest traseu se poate obține cu tehnica prezentată mai sus.
Pe lângă pistă se află copăcei cu scop decorativ. Constant pe ecran trebuie să se vadă cel puțin un copac (necesită densitate mare de copaci). Copacii nu trebuie să blocheze pista.
Dintre toate mașinile create pe pistă, pe una dintre ele va trebui să o alegeți ca fiind mașina jucătorului. Aceasta poate fi controlată folosind tastele W, A, S, D, unde W-S reprezintă mișcarea față-spate, iar A-D va schimba orientarea mașinii (detaliat mai jos).
Perspectiva jucătorului va fi una third-person, deci vom avea camera poziționată în spatele mașinii, uitându-se spre aceasta. Pentru un control cât mai fluid, vom dori ca mașina să rămână centrată pe direcția camerei, deci la apăsarea tastelor A-D se va face atât o rotație a mașinii, cât și o rotație a camerei:
Pentru a păstra poziția camerei la aceeași distanță în spatele mașinii, la mișcarea față-spate (W-S), două alternative ar fi:
Deplsarea față-spate (W-S) va implica mișcarea mașinii în funcție de rotația camerei, astfel încât comportamentul dorit să fie asemănător cu exemplul de mai jos:
1) Calculele de deplasare și rotație pot deveni destul de complicate destul de repede, de aceea vă recomandăm să lucrați cu vectori, pornind de la exemplele și teoria din Laboratorul 4.
2) Pentru deplasarea față-spate (W-S), vă recomandăm folosirea vectorilor camerei (în cazul acesta, vectorul forward) pentru a afla direcția de deplasare a mașinii.
Exemplu: Din moment ce camera își schimbă vectorul de forward la rotația pe OY (lab 5), o soluție ar fi folosirea acesteia pentru direcția de deplasare.
Utilizând ambele tehnici (rotația mașinii și deplasarea folosind vectorul forward al camerei), rezultatul final arată așa:
Pentru a verifica dacă mașina se află pe pistă, avem la dispoziție mai multe posibilități. Pentru simplitate, vom considera suficient dacă se verifică doar centrul mașinii să se afle în interiorul pistei. Orice soluție ce oferă o verificare corectă a acestei informații este bună.
Noi recomandăm oricare dintre următoarele 2 opțiuni:
Pentru a avea un comportament asemănător celui din demo-ul video, trebuie ca mașina să nu se blocheze dacă se lovește de exteriorul pistei. Pentru a preveni acest blocaj, o strategie ar fi următoarea:
Exemplu: Dacă se ține apăsat W, trebuie verificat mai întâi dacă cumva poziția nouă obținută în urma deplasării nu face coliziune. Dacă face, atunci inputul trebuie ignorat, altfel executat în mod normal.
Pentru a avea niște interacțiune cu mașiniile inamice, va trebui să le puteți “detecta”. Această detectare presupune o verificare a poziției mașinii jucătorului în raport cu fiecare mașină inamică. Pentru a realiza acest lucru, cel mai simplu mod pe care vi-l recomandăm este o verificare de coliziune sferă-sferă:
În cazul nostru particular, sferele ar avea centrul în centrul mașinii, iar raza acesteia presupune distanță de verificare de coliziune.
În momentul în care este dectată intersecția dintre mașina controlată de jucător și un obstacol dinamic, mașina jucătorului trebuie să se oprească. Orice tastă apasată pentru controlul deplasării sau rotației nu mai trebuie să modifice poziția sau orientarea mașinii. Acest efect de oprire dispare în momentul în care nu mai există intersecție.
În unul din colțurile ecranului (nu contează ce colț, noi am ales dreapta-jos pentru a fi mai ușor de vizualizat dar puteți alege orice poziție), va trebui să randați și un minimap.
În esență, un minimap nu reprezintă altceva decât un alt viewport unde trebuie să randați aceeași scenă iarăși.
Modul în care se poate crea un nou viewport in OpenGL (aveți un exemplu și in scheletul laboratorului 4) este folosind funcția:
glViewport(GLint x, GLint y, GLint width, GLint height)
Pentru a putea vedea de sus scena (cum este prezentat și în exemplul de mai jos), ar trebui să se întâmple următoarele lucruri:
glm::ortho(float left, float right, float bottom, float top, float zNear, float zFar)
Realizeaza curburii terenului se realizează la pasul de desenare a obiectelor din scenă. Acest efect se creează prin modificarea componentei y a coordonatelor pentru toți vertecșii obiectelor din scenă. Procesul este realizat în vertex shader. Componenta y a tuturor vertecșilor se modifică după cum urmează.
$$ Pos_{v_y} = Pos_{v_y} - \|{Pos_{car}-Pos_v}\|^2 \cdot scaleFactor $$
Factorul de scalare este proporțional cu dimensiunea obiectelor din scenă. Pentru demo-ul de mai sus, unde lățimea mașinii este de 1 unitate, a fost folosit un factor de scalare de 0.02.
Deoarece realizarea curburii terenului este un proces ce modifică pozițiile vertecșilor, rezultatul vizual este influențat de densitatea de triunghiuri ale terenului. Cu cât terenul are o densitate mai mare de triunghiuri, cu atât efectul de curbura este mai natural. O densitate mică produce un rezultat vizual mai 'pătrățos'. O comparație între rezultatele celor două poate fi vizualizată mai jos. În stânga este prezentat rezultatul pentru o densitate mică și în dreapta pentru o densitate mare.
În plus, geometria terenului de iarbă poate trece prin geometria pistei pentru o densitate mică de triunghiuri, așa cum se poate vedea mai sus în stânga.
Pentru a elimina aceste artefacte vizuale, este necesară generarea terenului de iarbă și a pistei cu o densitate mare de triunghiuri. De exemplu, o comparație între geometria celor două rezultate de mai sus este după cum urmează. Geometria pistei a fost generată conform descrierii de mai sus.
Atenție! Nerespectarea celor 2 restricții în generarea pistei de curse, 1. să nu existe niciun unghi drept între oricare 2 segmente consecutive, 2. să aibă formă concavă, atrage după sine nepunctarea elementelor ce utilizează informația pistei.
Pentru întrebări vom folosi forumurile de pe moodle. Orice nu este menționat în temă este la latitudinea fiecărui student!
Baremul este orientativ. Fiecare asistent are o anumită libertate în evaluarea temelor (de exemplu, să dea punctaj parțial pentru implementarea incompletă a unei funcționalități sau să scadă pentru hard coding). Același lucru este valabil atât pentru funcționalitățile obligatorii, cât și pentru bonusuri.
Tema va fi implementată în OpenGL și C++. Este indicat să folosiți framework-ul și Visual Studio.