This is an old revision of the document!


Animatii si interactiuni

Cerinte

Puteti porni de la scenele create in Lab 1

Realizati o scena de joc similara cu un joc de Duck Hunt 3D

  1. Mediul:
    1. un teren simplu si ceva vegetatie
  2. Playerul:
    1. este reprezentat de o camera statica in scena
    2. are posiblitatea de a trage inspre inamici in 2 moduri:
      1. left click: lanseaza un proiectil fizic care mere inspre directia une a fost apasat click (cu anumite reguli: fie are o durata de viata, fie pana a nimerit ceva, fie adaugati o forta combinata cu reguli de distrugere etc.)
      2. right click: damage instant - se realizeaza cu un raycast dinspre camera spre inamic si atinge instant locul in care a fost apasat click
  3. Inamicii:
    1. exista inamici 3D animati care se deplaseaza inspre camera incotinuu
    2. inamicii se instantiaza (spawneaza) incontinuu (puteti pune anumite reguli - de ex sa fie max 5 in scena la un moment dat) intr-o zona din scena care sa fie vizibila de catre camera)
    3. inamicii au comportament fizic (coloziuni, sunt afectati de gravitatie etc.)
    4. inamicii au 2 animatii posibile: run si hit (alergat, si o animatie pentru cand au fost doborati/atinsi)
    5. inamicii sunt distrusi in momentul in care i-a nimerit playerul (cu o animatie scurta, apoi dispar din scena)
    6. inamicii se misca incontinuu spre player (camera)
  4. GUI:
    1. In momentul in care au fost doborati 5 inamici, apare in mijlocul ecranului o imagine 2D animata
    2. In momentul in care au fost doborati 10 inamici, apare in mijlocul ecranului o alta imagine 2D animata print-o metoda diferita fata de cea afisata la 5 inamici doborati

Documentatie video


Documentatie extinsa text


Sumar documentatie

Animatii 2D in Unity

In Unity (versiuni dupa 2018) avem doua posibilitati de a face animatii 2D: sprite-sheet si bone-based.

În prima imagine, caracterul este prezentat în mai multe poziții, cu o secvență de poziții pentru diferite acțiuni. Aceasta imagine reprezinta un sprite-sheet, fiind foarte popular in jocurile 2D. Aceasta vă permite să vă imaginați în mod clar modul în care se va mișca în joc.

In a doua imagine, balaurul este împărțit în mai multe părți ale corpului (cap, corp, brațe și așa mai departe), necesitand o tehnică mai recentă de animație 2D, denumită în mod normal animație bazată pe oase (bone-based). După cum sugerează și numele, animația va fi pe bază de os, în care fiecare os din corp poate avea o acțiune sau o animație specifică. Având toate părțile principale ale corpului separat, permite dezvoltatorilor să creeze animațiile direct în editor. Această tehnică nouă de animație este foarte asemănătoare cu cea utilizată în animația 3D.

Sprite-sheet

Puteti descarca imaginea urmatoare ca exemplu: Pentru a folosi sprite-sheet-ul, incarcati imaginea in Unity si configurati Sprite Mode ca Multiple, dupa care accesati Sprite Editor.

In editor accesati meniul de Slice si puteti folosi varianta automata, sau manuala pentru a selecta diferitele stari ale animatiei. Dupa aceasta operatie, veti avea fiecare sprite individual.

Pentru a crea animatii din secvente de sprite-uri, puteti selecta sprite-urile si din folositi functia de Create Animation sau, mai simplu, puteti face drag-and-drop in scena si vi se creaza automat o animatie pe care va trebui sa o salvati.

Pentru sprite-ul oferit animatiile sunt asezate in felul urmator:

  • idle = 0 - 9
  • walk = 10 - 20
  • hadooken = 21 - 31
  • crouch = 32 - 37
  • jump = 38 - 48

Bone-based (Simulated)

Folosirea animatiilor pe baza de oase necesita mai multa munca.

In primul rand, plecand de la o imagine (puteti folosi imaginea de mai sus), trebuie despartita in slice-uri, similar cu procesul de la sprite sheet.

Apoi, aceste sprite-uri trebuie asamblate in scena, astfel incat sa formeze un obiect. Este indicat sa aveti o structura arborescenta. Acest lucru va fi folositor in crearea animatiilor, deoarece atunci cand misc bratul (arm), evident trebuie sa misc si mana (hand).

Pentru ordine puteti folosi urmatoarele valori

  • Dragon: 0
  • Body: 3
  • Head: 4
  • Left Leg: 4
  • Left Upper Arm: 5
  • Left Arm: 4
  • Left Hand: 5
  • Right Leg: 1
  • Right Upper Arm: 2
  • Right Arm: 1
  • Right Hand: 2
  • Tail: 4
  • Tail Tip: 5

Urmatorul pas este crearea animatiilor, care de aceasta data trebuie facuta manual folosind utilitarul de animatie (Window > Animation). Vedeti subcapitolul de mai jos.

Bone-based

Incepand cu versiunea din 2018 (2018.2.0f2), Unity are suport si pentru animatii Bone-based pentru 2D, functionalitate accesibila prin instalarea pachetului de 2D Animation.

Puteti folosi imaginea de mai jos ca exemplu:

Fluxul de lucru:

Se construieste ierarhia de oase în editor (Sprite Editor > Bone Editor).

Se genereaza geometria și se atribuie greutăți ale pielii în editor (Sprite Editor > Geometry And Weight Editor).

Se adauga componenta Sprite Skin pentru a genera oasele.

Mai multe detalii aici:

Urmeaza crearea de animatii pentru obiect folosind oasele.

Crearea de animatii

Crearea animatiilor se poate face folosind utilitarul de animatie (Window > Animation).

Pentru a crea animatia unui obiect, puteti selecta acel obiect si sa creati un animator pentru acesta (in cazul in care nu exista). Cum se foloseste animatorul: Folositi butonul de Record si creati key-frame-uri in care sa se diversi parameri de Transform sau Render. Spre exemplu puteti roti capul dragonului:

Pentru a crea o noua animatie folositi butonul de Create new Clip (spre exemplu pentru Jump):

Animator Controller

Urmatorul pas este sa configuram aceste animatii astfel incat sa le putem folosi, si sa putem tranzitiona intre ele. Pentru acest lucru, vom folosi un Animator Controller. Acest controller functioneaza ca un automat de stari, fiecare stare putand fi o animatie.

Pentru a folosi animatiile se pot adauga cu drag-and-drop in Animator.

Pentru animatii fluente si controlate, trebuie sa definim tranzitii intre acestea. Tranzitiile au mai multe proprietati, printre care timpi de activare, conditii etc.

Conditiile sunt folosite pentru a controla cand are loc o tranzitie. Spre exemplu, putem folosi un parametru de stare, pentru a trece dintr-o animatie in alta, sau alte conditii (spre exemplu daca este pe sol, in aer, primeste damage etc.)

Spre exemplu putem folosi urmatoarea conventie pentru un parametru de stare:

  • ken_idle = 0
  • ken_walk = 1
  • ken_crouch = 2
  • ken_jump = 3
  • ken_hadooken = 4

si punem conditia de tranzitie de la Idle la Walk state=1. Acest lucru îi va spune personajului, dacă este în Idle, pentru a tranzitiona la animația de Walk dacă parametrul de stare se schimbă la 1.

Schimbarea starilor unui Animator

Pentru a schimba starile unui animator, trebuie in primul rand adaugata componenta de Animator pe obiect. Apoi, putem controla parametrii definiti prin functii disponibile in clasa Animator.

const int STATE_IDLE = 0;
const int STATE_WALK = 1;
const int STATE_CROUCH = 2;
const int STATE_JUMP = 3;
const int STATE_HADOOKEN = 4;
 
string _currentDirection = "left";
void Start()
{
        //define the animator attached to the player
        animator = this.GetComponent<Animator>();
}
 
void Update()
{
        if (Input.GetKey ("right")) {
          changeDirection ("right"); //schimba directia personajului
          transform.Translate(Vector3.left * walkSpeed * Time.deltaTime); //muta personajul
          animator.SetInteger ("state", STATE_WALK); //activeaza animatia
 
        }
}
 
void changeDirection(string direction)
     {
 
         if (_currentDirection != direction)
         {
             if (direction == "right")
             {
             transform.Rotate (0, 180, 0);
             _currentDirection = "right";
             }
             else if (direction == "left")
             {
             transform.Rotate (0, -180, 0);
             _currentDirection = "left";
             }
         }
 
     }

Animatii proiectile / tragere

Pentru animatia de tragere, se foloseste in general o stare pentru a actualiza animatia jucatorului, iar proiectilul se genereaza in felul urmator (recomandat):

  • Pe arma (care se afla in scena) se adauga un script care gestioneaza generarea si directia proiectilelor
  • Plecand din locatia armei, se instantiaza (folosind functia Instantiate, studiata deja) un proiectil (prefab de regula, nu trebuie sa existe deja in scena, folosind pozitia armei). Proiectilul are atasat un script ce contine detalii de viteza, damage etc
  • Proiectilul are o miscare de translatie continua pe o directie, pana la atingerea unei limite sau a unui collider cand produce un trigger

Animatii si instantiere in scena

Pentru instantiere se foloseste functia Instantiate(gameobject, position). Pentru a genera continut doar in spatiu vizil pe ecran, se pot defini obiecte care fixeaza acest cadru, si generat random apoi in acest interval. Obiectele generate pot fi scriptate astfel incat sa aiba animatii si miscari continui.

Miscarea inamicilor

Miscarea inamicilor se poate face folosind o componenta de RigidBody2D si aplicand forte sau miscari

rb = GetComponent<Rigidbody2D>();


rb.AddForce (movement * speed);

rb.MovePosition (transform.position + offset * Time.deltaTime);

sau prin tarnslatii efectiv, inspre camera

Vector3 target = Camera.main.transform.position;
var step =  speed * Time.deltaTime; // calculate distance to move
transform.position = Vector3.MoveTowards(transform.position, target.position, step);        

Coliziuni

Pentru detectia coliziunilor se poate folosi un collider2D. Pentru o interactiune usoara, se poate genera un Trigger la evenimentul de coliziune bifand caseta de Trigger din componenta. Dupa care se poate prelua evenimentul si obiectul cu care se face coliziunea.

void OnTriggerEnter(Collider otherObject) {

		score += 10;
		scoreText.text = score.ToString();
		Destroy(otherObject.gameObject);

	}

void OnCollisionEnter(Collider otherObject) {

		score += 10;
		scoreText.text = score.ToString();
		Destroy(otherObject.gameObject);

	}
GameObject Manipulation
/* Create a GameObject */
Instantiate(GameObject prefab);
Instantiate(GameObject prefab, Transform parent);
Instantiate(GameObject prefab, Vector3 position, Quaternion rotation);
/* In Practice */
Instantiate(bullet);
Instantiate(bullet, bulletSpawn.transform);
Instantiate(bullet, Vector3.zero, Quaternion.identity);
Instantiate(bullet, new Vector3(0, 0, 10), bullet.transform.rotation);
 
newobj = Instantiate(objTemplate) as ObjType;
 
//from pregab - prefab must be in Resources folder
newobj1 = Instantiate(Resources.Load("enemy"));
 
// Instantiate the projectile at the position and rotation of this transform
Rigidbody projectile;
Rigidbody clone;
clone = Instantiate(projectile, transform.position, transform.rotation);
 
enemyOrc = Instantiate(Orc) as Enemy;
 
/* Destroy a GameObject */
Destroy(gameObject);
 
/* Finding GameObjects */
GameObject myObj = GameObject.Find("NAME IN HIERARCHY");
GameObject myObj = GameObject.FindWithTag("TAG");
childObject=parentObject.GetChild("child_name");
parentObject.GetChild("child_name").GetComponent<SpriteRenderer>().sprite = image; 
 
/* Accessing Components */
Example myComponent = GetComponent<Example>();
AudioSource audioSource = GetComponent<AudioSource>();
Rigidbody rgbd = GetComponent<Rigidbody>();
GetComponent<SpriteRenderer>().sprite = image; //set image in child component
GetComponent<Text>().text = '123' //set text
 
/* Transforms - can be accessed using the `transform` attribute */
Vector3 objectPosition = gameObject.transform.position;
gameObject.transform.position = new Vector3(posX, posY, posZ);
transform.Translate(Vector3.up * Time.deltaTime, Space.World);
transform.Rotate(Vector3.up * Time.deltaTime, Space.World);
 
/* Activate - can hide or how an element from the scene*/
myObject.SetActive(false); // hide
myObject.SetActive(true); // show
 
GetComponent<BoxCollider>().SetActive(false); // hide component
Vector Quick Reference

X = Left/Right Y = Up/Down Z = Forward/Back

Vector3.right /* (1, 0, 0) */   Vector2.right /* (1, 0) */
Vector3.left /* (-1, 0, 0) */   Vector2.left /* (-1, 0) */
Vector3.up /* (0, 1, 0) */      Vector2.up /* (0, 1) */
Vector3.down /* (0, -1, 0) */   Vector2.down /* (0, -1) */
Vector3.forward /* (0, 0, 1) */
Vector3.back /* (0, 0, -1) */
Vector3.zero /* (0, 0, 0) */    Vector2.zero /* (0, 0) */
Vector3.one /* (1, 1, 1) */     Vector2.one /* (1, 1) */
float length = myVector.magnitude /* Length of this Vector */
myVector.normalized /* Keeps direction, but reduces length to 1 */
Time Variables
/* The time in seconds since the start of the game */
float timeSinceStartOfGame = Time.time;
 
/* The scale at which the time is passing */
float currentTimeScale = Time.timeScale;
/* Pause time */
Time.timeScale = 0;
 
/* The time in seconds it took to complete the last frame */
/* Use with Update() and LateUpdate() */
float timePassedSinceLastFrame = Time.deltaTime;
 
/* The interval in seconds at which physics and fixed frame rate updates are performed */
/* Use with FixedUpdate() */
float physicsInterval =  Time.fixedDeltaTime;
Random values
Random.Range(-10.0f, 10.0f)
Random.Range(0, 8);
Physics Events
/* Both objects have to have a Collider and one object has to have a Rigidbody for these Events to work */
private void OnCollisionEnter(Collision hit) { Debug.Log(gameObject.name + " just hit " + hit.gameObject.name); }
private void OnCollisionStay(Collision hit) { Debug.Log(gameObject.name + " is hitting " + hit.gameObject.name); }
private void OnCollisionExit(Collision hit) { Debug.Log(gameObject.name + " stopped hitting " + hit.gameObject.name); }
 
/* Trigger must be checked on one of the Colliders */
private void OnTriggerEnter(Collider hit) { Debug.Log(gameObject.name + " just hit " + hit.name); }
private void OnTriggerStay(Collider hit) { Debug.Log(gameObject.name + " is hitting " + hit.name); }
private void OnTriggerExit(Collider hit) { Debug.Log(gameObject.name + " stopped hitting " + hit.name); }
 
/* For 2D Colliders just add 2D to the Method name and the Parameter Type */
private void OnCollisionEnter2D(Collision2D hit) { }
private void OnCollisionStay2D(Collision2D hit) { }
private void OnCollisionExit2D(Collision2D hit) { }
private void OnTriggerEnter2D(Collider2D hit) { }
private void OnTriggerStay2D(Collider2D hit) { }
private void OnTriggerExit2D(Collider2D hit) { }
Coroutines
/* Create a Coroutine */
private IEnumerator CountSeconds(int count = 10)
{
  for (int i = 0; i <= count; i++) {
    Debug.Log(i + " second(s) have passed");
    yield return new WaitForSeconds(1.0f);
  }
}
 
/* Call a Coroutine */
StartCoroutine(CountSeconds());
StartCoroutine(CountSeconds(10));
 
/* Call a Coroutine that may need to be stopped */
StartCoroutine("CountSeconds");
StartCoroutine("CountSeconds", 10);
 
/* Stop a Coroutine */
StopCoroutine("CountSeconds");
StopAllCoroutines();
 
/* Store and call a Coroutine from a variable */
private IEnumerator countSecondsCoroutine;
 
countSecondsCoroutine = CountSeconds();
StartCoroutine(countSecondsCoroutine);
 
/* Stop a stored Coroutine */
StopCoroutine(countSecondsCoroutine);
 
/* Coroutine Return Types */
yield return null; // Waits until the next Update() call
yield return new WaitForFixedUpdate(); // Waits until the next FixedUpdate() call
yield return new WaitForEndOfFrame(); // Waits until everything this frame has executed
yield return new WaitForSeconds(float seconds); // Waits for game time in seconds
yield return new WaitUntil(() => MY_CONDITION); // Waits until a custom condition is met
yield return new WWW("MY/WEB/REQUEST"); // Waits for a web request
yield return StartCoroutine("MY_COROUTINE"); // Waits until another Coroutine is completed
Input Quick Reference
if (Input.GetKeyDown(KeyCode.Space)) { Debug.Log("Space key was Pressed"); }
if (Input.GetKeyUp(KeyCode.W)) { Debug.Log("W key was Released"); }
if (Input.GetKey(KeyCode.UpArrow)) { Debug.Log("Up Arrow key is being held down"); }
 
/* Button Input located under Edit >> Project Settings >> Input */
if (Input.GetButtonDown("ButtonName")) { Debug.Log("Button was pressed"); }
if (Input.GetButtonUp("ButtonName")) { Debug.Log("Button was released"); }
if (Input.GetButton("ButtonName")) { Debug.Log("Button is being held down"); }
 
float translation = Input.GetAxis("Vertical") * speed;
float rotation = Input.GetAxis("Horizontal") * rotationSpeed;
 
// Make it move 10 meters per second instead of 10 meters per frame...
translation *= Time.deltaTime;
rotation *= Time.deltaTime;
 
// Move translation along the object's z-axis
transform.Translate(0, 0, translation);
 
// Rotate around our y-axis
transform.Rotate(0, rotation, 0);
 
/* special input events */
// OnMouseDown is called when the user has pressed the mouse button while over the Collider.
// This event is sent to all scripts of the GameObject with Collider or GUI Element. Scripts of the parent or child objects do not receive this event.
// This function is not called on objects that belong to Ignore Raycast layer.
// This function is called on Colliders marked as Trigger if and only if Physics.queriesHitTriggers is true.
void OnMouseDown()
{
    // Destroy the gameObject after clicking on it
    Destroy(gameObject);
}
pjv/laboratoare/2022/02.1666170558.txt.gz · Last modified: 2022/10/19 12:09 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