Unity3d QuickTip – czyli szybkie porady, rozwiązania częstych problemów i sztuczki w Unity3d!

Dzisiejszy odcinek: Jak dodać do First Person Controllera możliwość kucania i biegania

Uwaga! Jest to poradnik typu QuickTip. Zatem skupia się on na osiągnięciu założonego celu. Zatem zakładamy że użytkownik zna na tyle program Unity3d, aby samodzielnie wykonać najprostsze czynności, jak np. dodanie modelu kostki do sceny czy dodanie modelowi jakiegoś komponentu. Jeżeli brakuje Ci tej podstawowej wiedzy, zapraszam do tutoriala:
Unity Tutorial – Podstawy

Nie jestem fanem wynajdywania koła od nowa. Dlatego, nie będziemy bawić się w pisanie całego sterowania postacią, tylko dla opcji dodania kucania i biegania, a wykorzystamy First Person Controller. Do sceny musimy dodać tylko 3 elementy. Jakiegoś cuba, który będzie podłożem (powinien być dość długi, żeby sprawdzić bieganie), oraz drugiego cuba, którego zawiesimy w pewnej wysokości, by sprawdzić kucanie. U mnie jeden ma pozycję (0, 0, 0), a drugi (0, 2.5, 0). Do wszystkiego dodajemy wspomniany wcześniej First Person Controller, ze standardowych assetów. Do cubów, można dodać jakieś kolory, by się lepiej odróżniały. No i oczywiście dla lepszego naświetlenia sprawy, można dodać Directional Light.

Jeżeli interesuje cię dokładne poznanie kontrolera postaci, zapraszam do tego: tutoriala.

Aby zacząć zabawę, tworzymy sobie nowy skrypt. U mnie jest to skrypt C# o nazwie: CharacterControl.cs. Dodajemy skrypt do naszego First Person Controllera i od razu go uruchamiamy do edycji.

Bieganie

Dodanie tego elementu to sprawa trywialna, ponieważ chodzi w sumie tylko o zwiększenie maksymalnej szybkości postaci. Zaczynamy od deklaracji zmiennych pomocniczych:

public float walkSpeed = 10.0f;
public float runSpeed = 20.0f;

private CharacterController chCont;
private CharacterMotor chMotor;

Nie ma tutaj chyba za bardzo czego tłumaczyć. Zmienne są publiczne, żeby można było sobie dostosować wartości z poziomu Unity. Zmienne prywatne pozwolą nam odpowiednio: Dostać się do skryptu sterowania postaci, oraz do parametrów postaci.

void Start () {
	chCont = GetComponent<CharacterController>();
        chMotor = GetComponent<CharacterMotor>();
}

W funkcji start inicjujemy zmienne, by móc z nich korzystać.

void FixedUpdate () {

	float speed = walkSpeed;

	if(chCont.isGrounded && Input.GetKey(KeyCode.LeftShift)) {
		speed = runSpeed;
	}	

        chMotor.movement.maxForwardSpeed = speed;
}

Warte odnotowania jest to, że kod wywołujemy w funkcji FixedUpdate, zamiast Update. Nie będę się teraz zagłębiał w różnice między nimi, istotne jest to, że FixedUpdate wywołuje się z równą częstotliwością, niezależnie od ilości klatek na sekundę i odbywa się to przed wywołaniem funkcji Update.

Sam kod raczej jest dość prosty. Podstawiamy pod zmienną lokalną speed, prędkość marszu, czyli prędkość domyślną. Następnie sprawdzamy czy gracz naciska lewy Shift, oraz czy stoi na ziemi. Drugą daną pobieramy ze skryptu CharacterController, dzięki wcześniejszym deklaracjom. Na koniec, w CharacterMotorze, zmieniamy maksymalną szybkość na zmienną speed. Czyli, jeśli spełnione są warunki biegu, będzie to szybkość biegu, jeśli nie, to będziemy poruszać się normalnie.

Kucanie

Na początek wracamy na górę skryptu by dodać kilka zmiennych pomocniczych:

public float walkSpeed = 10.0f;
public float runSpeed = 20.0f;
public float crouchSpeed = 5.0f;      // Nowa linia
	
private CharacterMotor chMotor;
private CharacterController chCont;
private Transform tr;                 // Nowa linia
private float height;                 // Nowa linia

Jak widać dodaliśmy tylko publiczną prędkość dla kucania, oraz zmienną transform – czyli pozycję gracza, oraz jego wysokość (nie króla, tylko wzrost gracza ;)) Oczywiście nowe zmienne trzeba zainicjalizować:

void Start () {
	chMotor = GetComponent<CharacterMotor>();
        chCont = GetComponent<CharacterController>();
	tr = transform;
	height = chCont.height;
}

Transform to zwyczajnie pozycja obiektu do którego dodamy skrypt, czyli First Person Controllera, a height to wzrost naszej postaci.

W funkcji FixedUpdate dodajemy nowe zmienne lokalne:

void FixedUpdate () {
	float speed = walkSpeed;
        float standardHeight = height;
	float crouchHeight = 0.5f * standardHeight;
	float h = standardHeight;
	bool canStand = true;

standardHeight i crouchHeight to oczywiście zmienne określające wzrost postaci gdy stoi i kuca. Zmienna h, określa jakiego wzrostu mamy użyć. Posłuży nam ona w podobny sposób, co zmienna speed w przypadku biegania. Zmienna canStand, będzie odpowiadała za to, żeby uniemożliwić wstanie postaci, gdy nad głową ma sufit.

Wszystkie fragmenty kodu, będziemy dokładać w funkcji FixedUpdate, jednak podaje je porcjami, żeby wygodniej było omówić całość:

Vector3 startPos = transform.position;
float length =  (standardHeight - crouchHeight);

if(Physics.Raycast( startPos , Vector3.up, length)) {
	canStand = false;
} else {
	canStand = true;
}

startPos, to zmienna, która przetrzymuje pozycję postaci, lenght określa jak duża jest różnica, między kucającą, a stojącą postacią. Co w rezultacie oznacza: ile miejsca musi mieć kucająca postać, żeby mogła wstać.

Wewnątrz ifa, sprawdzamy za pomocą rzucania promienia, czy od naszej pozycji początkowej, patrząc w górę, mamy dość miejsca, by zmieścić naszą postać.  W przypadku prawdy, mamy kolizję, czyli niemożliwe jest zmieszczenie postaci, stąd zmienna canStand ma wartość false.

if(Input.GetKey(KeyCode.LeftControl)) {
	h = crouchHeight;
	speed = crouchSpeed;
}

Ten kod sprawia, że kiedy gracz trzyma wciśnięty lewy control, pomniejszamy postać i zmniejszamy jej prędkość, symulując kucnięcie.

if(!canStand) {
	h = crouchHeight;
	speed = crouchSpeed;
}

Następnie trzeba sprawdzić czy postać może wstać. Jeżeli nie jest to możliwe, kucamy dalej. Nie możemy całości umieścić w jednym ifie, ponieważ niemożliwe jest wykonanie operacji lub, dla wciśnięcia klawisza i zmiennej typu bool, stąd ten nieprzyjemny zapis.

float lastHeight = chCont.height;
chCont.height = Mathf.Lerp(chCont.height, h, 5 * Time.deltaTime);

Vector3 tmpPosition = tr.position;
tmpPosition.y += (chCont.height - lastHeight) / 2;
tr.position = tmpPosition;

Na koniec dopieszczamy całość.

Pobieramy sobie ostatnią wartość naszej wysokości, co wykorzystamy później. W tym czasie, wykorzystujemy funkcję Lerp, do zwiększenia wzrostu postaci. Działa ona tak, że przyjmuje 3 parametry. Dajmy na to: A, B, S. Parametr S, przyjmuje wartość od 0 do 1. Jeśli ma wartość 0, to zwraca A, jeśli wartość 1, to zwraca B. Łatwo się domyślić, że mając wartość pomiędzy 0, a 1, zwraca nam odpowiednią wartość z przedziału A do B. Dzięki takiemu zabiegowi, wstawanie postaci jest bardziej płynne i naturalne.

Drugim krokiem jest zmiana pozycji postaci w osi Y. Robimy to, ponieważ w czasie zwiększania wzrostu postaci, rozciąga się ona w obie strony, jeżeli pominęlibyśmy sztuczne podnoszenie jej, mogłaby zwyczajnie utknąć w podłodze. Robimy to za pomocą zmiennej tymczasowej, ponieważ tr.position.y jest wartością tylko do odczytu i nie możemy jej bezpośrednio podmienić.

Właściwie tyle. Dzięki tym kilku krokom, mamy kompletny system kucania i biegania, przez co nasza postać jest całkowicie mobilna. Mając tą wiedzę, dodanie np. czołgania się nie powinno stanowić problemu.

Kompletny projekt – Download

[to_like]

QuickTip#15

[/to_like]