Perspektywa pierwszoosobowa- animacje
W tym temacie zajmiemy się komponentem animatora, który pozwala kontrolować animacje oraz przejścia pomiędzy nimi w ruchu obiektów 3d. Temat jest rozwinięciem poprzedniego (zobacz)
Kliknij na obrazku aby zobaczyć krótki film dotyczący tego tematu
Animacja postaci
Stosowanie animacji bardziej zaawansowanych wymaga użycia tak zwanych szkieletów- systemu dźwigni z przegubami (odpowiednik stawu układu kostnego). Taki szkielet z siatką, teksturą i animacjami należy przygotować w zewnętrznym programie- na przykład w Blenderze.
Gotowy model wraz teksturami, szkieletem i animacjami eksportujemy do pliku *.fbx
Parametry importu
Po imporcie pliku *.fbx można sprawdzić czy animacje się dobrze zaimportowały. I usunąć te niewłaściwe. Wybrać czy animacja ma się powtarzać w czasie (na przykład bieganie, chodzenie itp.)
Nie zapomnij zapisać zmian klawiszem Apply, który znajduje się w dole otwartej karty importu.
Po imporcie pliku *.fbx na scenę dodajemy pusty obiekt (Create Empty). Obiekt ten będzie rdzeniem postaci. Nadajemy mu dowolną nazwę (ja nadałem dziki). Do obiektu dodajemy system kości i siatki postaci z zaimportowanego pliku fbx. Po utworzeniu wstępnego prefabrykatu w edytorze Unity powinny być widoczny układ kości, których nazwy są takie jakie przypisałeś w Blenderze.
Ten system kości pozwala utworzyć alternatywne animacje z poziomu Unity. System tworzenia animacji z poziomu Unity jest dużo prostszy niż na przykład w Blenderze, ale nie daje takich samych możliwości.
Do rdzenia postaci dodajemy komponent Charakter Controler, skrypt RuchBohatera (skrypt jest z poprzedniego tematu zobacz) i komponent Animator.
Na tę chwilę Animator nie będzie działać, dopóki nie utworzymy kontrolera animatora.
Kontroler animacji
Tworzymy kontrolera animacji dla postaci. Ja przypisałem nazwę Dziki
Do prefabrykatu postaci dodajemy komponent animatora (Animator). Do animatora podpinamy kontrolera animacji.
Klikamy dwa razy szybko w nazwę podpiętego kontrolera animatora i przenosimy się do okna edycji kontroler animatora. Więcej zobacz
Tworzymy węzły stanów animacji.
Dodajemy połączenia pomiędzy węzłami. Węzłom przypisujemy unikatowe tagi oraz klipy animacji, które będą w nich odtwarzane. Prędkości animacji dobierzemy późnej po uruchomieniu sceny i skryptów animacji.
Końcowy układ węzłów animacji przygotowany dla mojej postaci ma poniższy wygląd
Jeszcze raz sprawdź czy wybrane klipy animacji mają przypisaną pętlę czasową. Pętla animacji pozwala wielokrotnie odtwarzać animowany stan na przykład chodzenia.
Kliknij Apply aby uaktualnić
Jeżeli teraz uruchomisz scenę, do której dodasz tworzony prefabrykat, to zobaczysz odtwarzaną animację przypisaną do pierwszego węzła.
Wyzwalacze animacji
Wyzwalacze animacji to układ parametrów, które są zmieniane programowo i pozwalają przełączać aktywny węzeł animacji. Do dyspozycji jest cztery typy wyzwalaczy. My tworzymy jeden typu Int przypisując mu nazwę intStanAnimacji.
Przez wartość tej zmiennej będziemy przełączać aktywność węzłów animacji.
- intStanAnimacji=0, to czekaj
- intStanAnimacji=1, to spacer
- intStanAnimacji=2, to bieg
- intStanAnimacji=3, to czołganie
- intStanAnimacji=4, to skok
Na przykład przejście ze stany CZEKAJ na BIEG edytujesz po kliknięciu myszką w wybrana linię przejścia (standardowo podświetli się na niebiesko)
Takie ustawienia pozwolą na przerwanie bieżącej animacji i przejście do następnej bez czekania aż się skończy odtwarzać bieżąca (ustaw przerwanie na Next State Then Current State). Ustaw warunki przejścia. Tu przyjęliśmy, że bieg gdy intStanAnimacji=2.
Czynność powtórz dla każdej linii przejścia.
Zwróć uwagę na uniwersalność Any States.
Ten układ tak się interpretuje: animacja stanu Skok jest dostępną z każdej innej animacji i po zakończeniu przechodzi do stanu Czekaj
Skrypt animacji
Sposób wywołania przejść pomiędzy animacjami jest dowolny. Poniżej zaprezentuje skrypt, który wykorzystuje typ wyliczeniowy zdefiniowany jak poniżej
Wskazówka:
//typ wyliczeniowy dla animacji
public enum STAN_ANIMACJI {
CZEKAJ,//stan 0
SPACER,//stan 1
BIEG,//stan 2
CZOLGANIE,//stan 3
SKOK//stan 4
}
Używanie typu wyliczeniowego ułatwi orientacje w kodzie, po prostu nazwy kojarzą się z funkcją przejścia animacji. Dodatkowo do opcji projektu dodamy trzy klawisze. Uruchom Input Manager (menu Edit/ Project Settings/ Input Manager). Zwiększ rozmiar tablicy Axes o 3. Zdefiniuj nowe elementy jak poniżej, w których klawisz b to bieg, c to czołganie, s to spacer. Patrz poniżej.
W edytorze Unity utwórz nowy skrypt o nazwie AnimacjaDzikiego (lub każdej innej jak ci odpowiada) i wypełnij poniższym kodem
Wskazówka:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class AnimacjaDzikiego : MonoBehaviour
{
//typ wyliczeniowy dla animacji
public enum STAN_ANIMACJI {
CZEKAJ,//stan 0
SPACER,//stan 1
BIEG,//stan 2
CZOLGANIE,//stan 3
SKOK//stan 4
}
public float wspolczynnikZmianyPredkosci = 1;
Animator animator;
STAN_ANIMACJI stanAnimacji = STAN_ANIMACJI.CZEKAJ;
public STAN_ANIMACJI StanAnimacji
{
get { return stanAnimacji; }
set {
stanAnimacji=value;
//ustwa stan animacji z indeksu aktywnego typu wyliczeniowego
animator.SetInteger("intStanAnimacji", (int)stanAnimacji);
}
}
// Start is called before the first frame update
void Start()
{
animator = GetComponent<Animator>();
}
public void PrzelaczAnimacje(float _constV)
{
if (Input.GetAxis("Spacer") != 0)
{
StanAnimacji = STAN_ANIMACJI.SPACER;
//ustaw predkośc na stała
wspolczynnikZmianyPredkosci = _constV;
}
if (Input.GetAxis("Bieg") != 0)
{
StanAnimacji = STAN_ANIMACJI.BIEG;
//zwiększ predkośc podczas biegu 2.5 razy
wspolczynnikZmianyPredkosci = 2.5f* _constV;
}
if (Input.GetAxis("Czolgaj") != 0)
{
StanAnimacji = STAN_ANIMACJI.CZOLGANIE;
//zmniejsz prędkośc podczas czołgania 2 razy
wspolczynnikZmianyPredkosci = 0.5f* _constV;
}
}
// Update is called once per frame
void Update()
{
}
}
Skrypt dołącz do postaci
Zmiany w skrypcie RuchBohatera
W skrypcie ruchu, który został napisany w poprzednim temacie musimy dokonać kilku zmian aby można sterować animacjami. Zacznij od dodania nowej zmiennej constV
Wskazówka:
//współczynnik prędkości
const float constV = 2.5f;
public float v= constV,
wysSkoku=2.0f;
Kolejne zmiany dotyczą funkcji Skok(), Spadaj(), Ruch(). Poniżej pełna zawartość tego skryptu po modyfikacji.
Wskazówka:
using System.Collections;
using System.Collections.Generic;
using Unity.Android.Gradle.Manifest;
using UnityEngine;
using static AnimacjaDzikiego;
public class RuchBohatera : MonoBehaviour
{
public CharacterController kontroler;
public Transform kamera;
private bool fGrunt;
//przyspieszenie grawitacyjne
public float g = -9.8f;
//współczynnik prędkości
const float constV = 2.5f;
public float v= constV,
wysSkoku=2.0f;
Vector3 wektorPredkosci = Vector3.zero;
public float wygladzenie = 0.1f;
float buforBiezacegoWygladzania;
public void Skok()
{
if (fGrunt && Input.GetAxis("Jump") != 0)
{
//v=Pierwiastek(2gh)
wektorPredkosci.y = Mathf.Sqrt(-2f * g * wysSkoku);
GetComponent<AnimacjaDzikiego>().StanAnimacji
= STAN_ANIMACJI.SKOK;
}
}
void Spadaj()
{
wektorPredkosci.y += g * Time.deltaTime;
if (fGrunt && wektorPredkosci.y < 0)
{
//nie spadaj szybciej niż 54m/s
wektorPredkosci.y = -54f;
}
kontroler.Move(wektorPredkosci * Time.deltaTime);
}
void Ruch()
{
float poziom = Input.GetAxis("Horizontal");
float pion = Input.GetAxis("Vertical");
Vector3 kierunek = new Vector3(poziom, 0, pion).normalized;
//czy ruch, powiedzmy wiecej niż 5 centymetrów
if (kierunek.magnitude >= 0.05f)
{
//obrót postaci
//zwróc kąt w radianach z płaszczyzny 2D- oś X a Z
//zamień radiany na stopnie
float katPatrzenia = Mathf.Atan2(kierunek.x, kierunek.z)
*Mathf.Rad2Deg+
kamera.eulerAngles.y;
//wygladzaj obrot,
//predkosc wygladzania wyslij do bufora
float kat = Mathf.SmoothDampAngle(
transform.eulerAngles.y,
katPatrzenia,
ref wygladzenie,
buforBiezacegoWygladzania
);
transform.rotation = Quaternion.Euler(0, kat, 0);
Vector3 kierunekRuchu = Quaternion.Euler(0, katPatrzenia, 0)
*Vector3.forward;
kontroler.Move(kierunekRuchu.normalized * v * Time.deltaTime);
//jest ruch to domyslnie ustawiaj na spacer
if (GetComponent<AnimacjaDzikiego>().StanAnimacji == STAN_ANIMACJI.CZEKAJ)
{
GetComponent<AnimacjaDzikiego>().StanAnimacji = STAN_ANIMACJI.SPACER;
GetComponent<AnimacjaDzikiego>().wspolczynnikZmianyPredkosci = constV;
}
GetComponent<AnimacjaDzikiego>().PrzelaczAnimacje(constV);
v = GetComponent<AnimacjaDzikiego>().wspolczynnikZmianyPredkosci;
}
else
{
//pozostałe animacje przełacz
//w skrypcie AnimacjaDzikiego
GetComponent<AnimacjaDzikiego>().StanAnimacji = STAN_ANIMACJI.CZEKAJ;
}
}
// Start is called before the first frame update
void Start()
{
//blokuj kursor na srodku ekranu
Cursor.lockState = CursorLockMode.Locked;
//ukryj kursor
//standardowo klawisz Esc przywróci widok kursora
Cursor.visible = false;
}
// Update is called once per frame
void Update()
{
//sprawdzaj czy jestes nad dowolnym podlozem
fGrunt = kontroler.isGrounded;
Ruch();
Skok();
Spadaj();
}
}
Uruchom scenę i sprawdź działanie skryptów.
Postać powinna reagować na bieganie, skakanie i czołganie.