Perspektywa pierwszoosobowa- zbieranie zasobów
Rozwijamy tworzenie kontrolera postaci opartego na komponencie Character Controller. Do kodu z projektu opisanego w tym temacie (zobacz) dojdzie kilka metod pozwalających zbierać przedmioty umieszczone w wirtualnym świecie 3D.
Krótki film przedstawiający omawiany temat (kliknij na obrazku)
Nowa scena
W Blenderze przygotowujemy nową scenę, która może być dowolna. Ważne, aby zawierała siatkę modelu beczki i dzidy.
Gotową scenę z Blendera wraz z przedmiotami eksportujemy do pliku *fbx, importujemy i osadzamy w scenie w Unity.
Po uruchomieniu sceny możemy zauważyć niepożądany efekt przechodzenia przez obiekty. Co oznacza brak wykrywania kolizji- obiekt nie ma podpiętego collidera.
W przypadku obiektów, na których nam zależy aby kolizja była dość zgodna z ich siatką najwygodniej jest zastosować Mesch Collider. Ale w przypadku użycia Character Controller do sterowania ruchem gracza, to nie wystarcza jeżeli chcemy zachować fizyczność obiektu kolidującego. Dodatkowo na komponent Charakter Controller postaci nie mogą wpływać obiekty poprzez fizykę. Należy opracować własne skrypty wykrywania kolizji.
Przepychanie obiektu
Przepychanie obiektu zademonstruję na przykładzie beczki, której podczas kolizji z graczem zostanie przekazana siła. Kierunek wektora działającej siły zostanie wyznaczony na podstawie współrzędnych pozycji gracza i beczki w świecie sceny gry 3D.
Do obiektu beczki dodajemy komponent Rigidbody i Mesh Collider. Poniżej ilustracja przedstawia przypisane opcje.
Tworzymy nowy skrypt o nazwie KolizjeZasoby(). Kod skryptu poniżej.
Wskazówka:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class KolizjeZasoby : MonoBehaviour
{
[SerializeField] float silaWartosc = 0;
private void OnControllerColliderHit(ControllerColliderHit hit)
{
Rigidbody rb=hit.collider.attachedRigidbody;
if (rb != null)
{
Vector3 silaKierunek =hit.gameObject.transform.position-
transform.position;
//zeruj pion
silaKierunek.y = 0;
//wyznacz jednostkowy wektor kierunku wektora
silaKierunek.Normalize();
//przekaz siłe uderzenia
rb.AddForceAtPosition(silaKierunek * silaWartosc,
transform.position,
ForceMode.Impulse);
}
}
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
}
Skrypt podpinamy do obiektu gracza. Wartość współczynnika siły wstępnie ustawiamy na 1.
Uruchamiamy scenę i staramy się tak poruszać postacią aby zderzyć się z beczką. Przy każdej kolizji beczka dozna działania siły. Jeżeli się przewróci to będzie się toczyć.
Zbieranie obiektów
Zbieranie obiektów (artefaktów broni czy innych zasobów) przedstawię na modelu przejęcia dzidy. Dodatkowo w łatwy sposób bez zmiany już istniejących animacji można uzyskać animację niesienia włóczni. Animacje walki włócznią należy opracować i dołączyć do animatora postaci na zasadach wcześniej omówionych.
Przyjęte rozwiązanie wymaga przygotowania dwóch prefabrykatów dzidy. Pierwszy prefabrykat dzidy będzie dostępny do zabrania przez postać, drugi do noszenia. Oczywiście, nie jest to jedyny pomysł na zbieranie i noszenie broni w grze 3D.
Dla pierwszego prefabrykatu ustawiamy tag: dzida, dodajemy komponent Rigidbody i Capsule Collider. Masę ustawiamy na dwie jednostki, dopasowujemy rozmiar collidera.
Duplikujemy prefabrykat i z duplikatu tworzymy prefabrykat dzidy do noszenia. Dzida do noszenia musi zachować możliwość obrony przy machaniu lub dźganiu. Nie może spadać, kolizje z ciałem gracza powinny być ignorowane. Różne warunki można narzucić na niesione dzidę. Ja zostanę przy dwóch.
Do nowego prefabrykatu dzidy oprócz tagu: dzida przypisujemy warstwę (Layer): Gracz. W komponencie Rigidbody odhaczamy opcję Use Gravity, odznaczamy opcję Is Kinematic. W komponencie Capsule Collider wykluczamy warstwę Gracz (Exclude Layers :Gracz). Patrz ustawienia na poniższej ilustracji.
Przechodzimy do skryptu KolizjeZasoby() i wprowadzamy poniższe modyfikacje. Kod zawiera komentarz, więc łatwo jest się zorientować o przeznaczeniu poszczególnych linii kodu.
Wskazówka:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class KolizjeZasoby : MonoBehaviour
{
[SerializeField] float silaWartosc = 0;
[SerializeField] GameObject slotBroniPrawy;
//tablica prefabrykatów broni
[SerializeField] GameObject[] prafbBroni;
private void KolizjaDzida(ControllerColliderHit hit)
{
GameObject dzida=hit.gameObject;
//usuń dzidę ze swiata gry
Destroy(dzida);
//daj dzide do reki bohatera
GameObject d = Instantiate(prafbBroni[0],
slotBroniPrawy.transform.position,
slotBroniPrawy.transform.rotation);
d.transform.SetParent(slotBroniPrawy.transform);
}
private void OnControllerColliderHit(ControllerColliderHit hit)
{
Rigidbody rb=hit.collider.attachedRigidbody;
if (rb != null)
{
if (hit.gameObject.tag == "dzida")
{KolizjaDzida(hit); return; }
//nie jest to dzida to przesuń ciało
Vector3 silaKierunek =hit.gameObject.transform.position-
transform.position;
//zeruj pion
silaKierunek.y = 0;
//wyznacz jednostkowy wektor kierunku wektora
silaKierunek.Normalize();
//przekaz siłe uderzenia
rb.AddForceAtPosition(silaKierunek * silaWartosc,
transform.position,
ForceMode.Impulse);
}
}
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
}
Uaktualniamy postać gracza. Postać gracza przypisujemy do warstwy Gracz (Layer :Gracz). Do slotu na broń podpinamy ostatnią kość prawej ręki. Do tablicy prefabrykatów podpinamy prefabrykat dzidy przeznaczony do noszenia.
Zapisujemy scenę i uruchamiamy. Poruszaj się postacią tak, aby trafić na dzidę umieszczoną na scenie.
Postać powinna przechwycić dzidę. Przechwycona dzida porusza się zgodnie z animacją dłoni i całą postacią gracza.