This is an old revision of the document!


Elemente de programare a jocurilor 3D

Controlul caracterelor in 3D

Pentru miscarea si controlul caracterelor in 3D sunt posibile mai multe abordari.

Character Controller

Character Controller este o componenenta ce poate fi adaugata pentru a putea controla caracaterul. Aceasta componenta nu foloseste elemente de fizica in nici nu fel, dar vine atasat cu un collider de baza Capsule Collider care nu permite intrarea in alte collidere. Acest lucru simplifica foarte mult scriptarea caracterului.

Character Controller pune la dispozitie doua metode pentru miscarea caracterului:

  • SimpleMove(Vector3 speed) care va misca caracterul tinand cont de gravitatia definita, dar in care axa Y este ignorata
// Move forward / backward
        Vector3 forward = transform.TransformDirection(Vector3.forward);
        float curSpeed = speed * Input.GetAxis("Vertical");
        controller.SimpleMove(forward * curSpeed);
  • Move(Vector3 motion) care va muta caracterul in functie de paracmetrul motion, care reprezinta miscarea absoluta, astfel ca acceleratii de genul gravitatiei vor trebui implementate manual.
   moveDirection = new Vector3(Input.GetAxis("Horizontal"), 0.0f, Input.GetAxis("Vertical"));
            moveDirection = transform.TransformDirection(moveDirection);
            moveDirection = moveDirection * speed;
 
            if (Input.GetButton("Jump"))
            {
                moveDirection.y = jumpSpeed;
            }
        }
 
        // Apply gravity
        moveDirection.y = moveDirection.y - (gravity * Time.deltaTime);
 
        // Move the controller
        controller.Move(moveDirection * Time.deltaTime);

In cazul functiilor puse la dispozitie de Character Controller, miscarile sunt constranse de coliziuni in mod automat, si fiind destul de permisiva (spre exemplu va putea urca autmat rampe sau scari).

Rigidbody

Componenta de Rigidbody practic determina ca obiectul pe care e plasata sa se comporte ca un corp fizic, care interactioneaza cu mediul si asupra caruia se pot aplica forte fizice. Implicit, componenta Rigidbody este afectata de gravitatia definita ca prametru.

Exista 4 moduri in care se poate aplica o forta:

  • Force: adauga o forta continua si dependenta de masa
  • Acceleration: adauga o acceleratia continua independenta de masa
  • Impulse: adauga o forta instanta si dependenta de masa
  • VelocityChange: adauga o velocitate instanta independenta de masa

Pentru a adauga o forta exista de asemenea mai multe metode, dar cea standard este AddForce(Vector3 force, ForceMode mode = ForceMode.Force).

rb.AddForce(10.0f * Vector3.up);

if (Input.GetButtonDown("Jump") && _isGrounded)
{
  rb.AddForce(Vector3.up * Mathf.Sqrt(JumpHeight * -2f * Physics.gravity.y), ForceMode.VelocityChange);
}

Pentru a misca un caracter folosind inputul, de regula se foloseste functia MovePosition. Aceasta functie va incerca sa mute caracterul la pozitia respectiva, tinand cont de coliziuni. De asemenea, nu va influienta cu nimic procesarile fizice in desfasurare aplicate asupra RigidBody-ului, comenzile de miscare ale caracterului fiind separate oarecum de mecanica de simulare a interactiunilor fizice.

Vector3 dirVector = new Vector3 (Input.GetAxis ("Horizontal"), 0, Input.GetAxis ("Vertical")).normalized;
GetComponent <Rigidbody> ().MovePosition (transform.position + dirVector * Time.deltaTime);

Componenta Rigidbody este una foarte precisa din punct de vedere al coliziunilor, deci ofera mai mult control in acest sens, dar necesita si mai multa programare chiar si pentru lucruri simple, cum ar fi urcatul scarilor sau pe o rampa.

O mentiune importanta este ca Unity foloseste o separatie a procesarii functiilor de update in ceea ce priveste frame-ul. Astfel update-urile de procesare a fizicii sunt separate de update-urile de procesare a frame-ului:

  • exista o functie de FixedUpdate, care se apeleaza la un framerate fix si care se apeleaza inainte de fiecare `update de fizica`. In aceasta metoda este recomandat sa se aplice toate fortele necesare simularii fizice pentru a nu afecta simluarea facandu-o dependenta de framerate
  • functia clasica de Update, care duce la actualizarea si afisarea frame-ului curent: in aceasta functie se poate prelua inputul pentru a fi consistent

O problema generala care apare la preluarea inputului este normalizarea.

 Vector2 dirVector = new Vector2 (Input.GetAxis ("Horizontal"), Input.GetAxis ("Vertical"));

versus

 Vector2 dirVector = new Vector2 (Input.GetAxis ("Horizontal"), Input.GetAxis ("Vertical")).normalized;

Daca inputul nu este normalizat, caracterul se va misca cu o viteza mai mare atunci cand se misca in fi

Raycasting

In spatiul 3D testarea selectarii sau tintirii asupra unui obiect se face in general printr-o tehnica numita Raycasting, care presupune trasarea unei raze de la sursa (in general camera) la destinatie. Acest lucru este util atat pentru object picking (selectarea unui obiect in spatiul 3D cu mouse-ul spre exemplu) cat si pentru tintirea si lansarea de proiectile.

Desi in cazul unui FPS, lansarea de proiectile se poate face si fizic (deci instantiez un proiectil care are damage, si ii imprim o forta cu care sa se deplaseze spre o directie), in general ea se simuleaza prin trasarea unei raze si detectia coliziuniii cu aceasta, si apoi aplicarea de efecte astfel incat impactul sa para real (proiectilul in general nu este vizibil ochiului uman).

Pentru trasarea unei raze de la camera la o tinta se poate folosi functia de Raycast din pachetul de Physics oferit de Unity. Functia Raycast accepta o multitudine de variante de paramtri, in functie nevoia de utilizare, parametrii principali fiind punctul de start si directia. Un exemplu pentru o camera FPS si o tinta intr-un anumit range se poate folosi urmatoarea secventa de cod:

RaycastHit hit;
 
//daca am un obiect pe directia in range
if(Physics.Raycast(fpsCam.transform.position, fpsCam.transform.forward, out hit, range)) { 
  //accesez un script de damage pe targetul meu daca accesta exista
  Enemy orc = hit.transform.GetComponent<Enemy>();
  if(orc!=null) {
    orc.TakeDamage(ammount); //TakeDamage este o functie publica in scriptul de Enemy
 
    enemyHealthBar.SetActive(true); //pot afisa si Healthbar-ul inamicului la plasarea tintei pe acesta
  }
}

Asa cum se observa si in exemplul de mai sus, Racycast-urile se pot folosi in multe moduri. Cateva exemple:

  • aplicarea unei coliziuni
  • selectia unui obiect
  • tintirea unui obiect
  • actionarea unei functii din obiectul tintit
  • si altele

Elemente de GUI in 3D

Similar cu spatiul 2D, se pot folosi atat obiecte plasate in scena, cat si partea de Canvas, in ceea ce priveste interfata grafica.

Astfel, spre exemplu pentru un FPS single player, in general nu avem nevoie de caracter, ci doar de maini, sau eventual doar o arma animata. Pentru a controla usor acest aspect se poate importa un obiect texturat pentru arma, care se va atasa camerei 3D disponibile in scena si se va configura astfel incat sa arate cat mai natural.

Pentru a crea un crosshair sau o tinta se poata adauga un disc sau o imagine cu transparenta in centrul canvas-ului.

Bineinteles, se poate crea un crosshair animat si folosind 4 dreptunghiuri simple.

pjv/laboratoare/05.1542201655.txt.gz ยท Last modified: 2018/11/14 15:20 by alexandru.gradinaru
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0