Animace a animační křivky (pá 15. 12. 2023) [prez]

Dnešním ultimátním cílem bude postavit překážkovou dráhou s pohyblivými prvky.

Fáze 0 – prerekvizity

Fáze 1 – základní objekty

Vytvořte si klasický scénář – kapsulový hráč s first-person kamerou na ploše. Vaše hierarchie hierarchie nechť vypadá následovně:

Podoba by poté měla být příbližně následující:

obrázek první podoby

Hráče potřebujeme nyní rozhýbat – vytvoříme dva skripty:

Obsahy těchto souborů nebudou překvapivé – není to nic, co jsme tu dosud neviděli.

PlayerMovement.cs
using UnityEngine;

public class PlayerMovement : MonoBehaviour
{
    float Speed = 5f;
    private void Update()
    {
        if (Input.GetKey(KeyCode.UpArrow) || Input.GetKey(KeyCode.W))
        {
            this.transform.position += Speed * Time.deltaTime * new Vector3(
                Camera.main.transform.forward.x,
                0,
                Camera.main.transform.forward.z
            );
        }

        if (Input.GetKey(KeyCode.DownArrow) || Input.GetKey(KeyCode.S))
        {
            this.transform.position -= Speed * Time.deltaTime * new Vector3(
                Camera.main.transform.forward.x,
                0,
                Camera.main.transform.forward.z
            );
        }

        if (Input.GetKey(KeyCode.LeftArrow) || Input.GetKey(KeyCode.A))
        {
            
            this.transform.position -= Speed * Time.deltaTime * new Vector3(
                Camera.main.transform.right.x,
                0,
                Camera.main.transform.right.z
            );
        }

        if (Input.GetKey(KeyCode.RightArrow) || Input.GetKey(KeyCode.D))
        {
            this.transform.position += Speed * Time.deltaTime * new Vector3(
                Camera.main.transform.right.x,
                0,
                Camera.main.transform.right.z
            );
        }
    }
}
Klikněte do bloku pro jeho zkopírování
CameraMovement.cs
using UnityEngine;

public class CameraMovement : MonoBehaviour
{
	float Sensitivity = 10f;
	void Update()
	{
		float dx = Input.GetAxis("Mouse X");
		transform.eulerAngles += Vector3.up * dx * Sensitivity;

		float dy = Input.GetAxis("Mouse Y");
		transform.eulerAngles += Vector3.left * dy * Sensitivity;
	}
}
Klikněte do bloku pro jeho zkopírování

Následně tyto dva skripty adekvátně přiřadíme našim objektům:

Checkpoint 1 – funkční ovládání hráče

Zkontrolujte si, že vám vše funguje – měli byste se moci pohybovat volně pomocí kláves WASD nebo šipek. Dopomůže vám kostka Target jakožto referenční bod.

Fáze 2 – výstavba překážkové dráhy

Nyní vytvoříme samotnou překážkovou dráhu. Pro lehkou manipulovatelnost ji dáme do nového objektu. Přesuneme do ní též naší kostku Target. Dráhá by měla vypadat přibližně následovně:

obrázek druhé

Překážky budeme chtít dle všeho identické, použijeme proto Prefab. Ale z důvodů, který bude vyjasněn později budeme vyžadovat, aby měli všechny tyto překážky souřadnice [0,0,0]. Obalíme je tedy prázdným GameObjectem, kterému pak dáváme souřadnice, které chceme. Naše dráha bude prozatím koridor, v němž se naskýtají mezi hráčem a cílem čtyři identické překážky. Po tomto rozšíření může vypadat strutura následovně:

Hráči přidejte Rigidbody a nastavte mu v něm velké úhlové tření (Angular drag) a uzamkněte mu pohyb a rotaci v ose y. Postraním stěnám dejte také Rigidbody a naprosto uzamkněte jejich pozici a rotaci.

Nyní už nám chybí poslední krok před animacemi – potřebujeme překážky nastavit tak, aby při kolizi s hráčem byl hráč poslán zpět na start.

Přidejte proto hráči (tj. GameObjektu Player) Capsule collider. V prefabu překážek dejte přímo kvádru ještě tag Obstacle a Box Collider. Následně vytvořte skript PlayerCollisionHandler.cs s následujícím obsahem:

PlayerCollisionHandler.cs
using UnityEngine;

public class PlayerCollisionHandler : MonoBehaviour
{
	Rigidbody rb;

	void Start()
	{
		rb = GetComponent<Rigidbody>();
	}

	public void OnCollisionEnter(Collision collision)
	{
		if (collision.gameObject.CompareTag("Obstacle"))
		{
			// posuň hráče do výchozí polohy 
			transform.position = new Vector3(START_X, START_Y, START_Z);
			
			// vyresetuj jeho hybnost
			rb.velocity = Vector3.zero;
			rb.angularVelocity = Vector3.zero;
		}
	}
}
Klikněte do bloku pro jeho zkopírování

Tento skript jednoduše resetuje hráče na původní pozici, narazí-li do objektu s tagem Obstacle. START_X, START_Y, START_Z nastavte na původní pozici hráče ve vaší implementaci této překážkové dráhy.

Checkpoint 2 – interakce hráče s okolím

Zkontrolujte si, že hráč se může hýbat a narážet do stěn (nesmí jimi procházet). Zároveň se po kolizi s nimi náhodně neroztáčí. Také zkontrolujte, že narazíte-li do překážky, nikoliv do jedné ze stěn, tak jste resetováni zpátky na původní pozici

Fáze 3 – konečně animujeme!

Zkontrolujte si, že hráč se může hýbat a narážet do stěn (nesmí jimi procházet). Zároveň se po kolizi s nimi náhodně neroztáčí. Také zkontrolujte, že narazíte-li do překážky, nikoliv do jedné ze stěn, tak jste resetováni zpátky na původní pozici

Vyberme nyní nejpřednější překážku (na samotný kvádr, nikoliv jeho obal) a otevřme editor animací z {Window → Animation → Animation}. Otevře se nám nové okno, v němž poklikáme Create. Tím si v assetech vytvoříme novou animaci, pojmenujme ji napříkad ObstacleMovement.

popis UI + vysvětlení nutnosti wrapperů, keyframes, křivek, …

Pokud jsme již s animací spokojeni, odebereme ji z původního objektu, na kterém jsme ji testovali a přidáme ji do našeho prefabu. Jdeme tedy do editoru prefabu, přidáme komponent Animation a v tomto komponentě vybereme naší animaci ObstacleMovement. S prefaby se však chovají animace trochu specificki, musíme proto samotnou aplikaci ještě opravit, a to následovně. V okně s assety na ni poklikáme a v inspektoru se pomocí třech teček překlikneme do Debug mode. Zde nastavíme Wrap mode na Loop a zaškrtneme tlačítko Legacy mode.

Nyní by nám již hra měla fungovat!

Checkpoint 3 – kontrola funkčnosti hry

Zkontrolujte si, zda vám hra funguje! Nejasnosti?