Unity3d QuickTip – czyli szybkie porady, rozwiązania częstych problemów i sztuczki w Unity3d!
Dzisiejszy odcinek: Jak wykrywać kolizje i zdarzenia?
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
Teoria
W świecie Unity3d mamy 3 metody wykrywania kolizji. Warto się zapoznać z każdą z nich, ponieważ mają one różne zastosowania. Wyróżniamy 3 główne typy wykrywania kolizji:
- Kolizja (Collision) – Jest to zwykłe “zderzenie”. Tego typu kolizji użyjemy np. na ścianie, tak by gracz w nią wbiegając został zatrzymany.
- Wyzwalacz (Trigger) – Jest to rodzaj kolizji w której wiemy że znaleźliśmy się w obszarze kolizji, ale nie zostajemy przez nią brutalnie zatrzymani. Taki collider można sobie wyobrazić jako fotokomórkę. Wchodzimy w jej “pole widzenia”, więc zapala światło. Ten typ kolizji można też wykorzystać np. przy tworzeniu pola widzenia przeciwnika.
- Rzucanie promienia (Raycasting) – Chyba najdziwniejsze wykrywanie kolizji. Jedyne którego nie otrzymamy przez dodanie komponentu. Działa to tak, że “strzelamy” promieniem z naszej postaci do przodu na pewien dystans. I możemy wykryć czy coś na linii “strzału” się znalazło. I jak się łatwo domyślić, jako zasięg działania broni palnej, najczęściej się to wykorzystuje.
Same kolizje są narzędziem kluczowym. Możemy ich używać dzięki komponentowi Collider (Dostępny w menu: Component->Physics). Jak łatwo można zauważyć mamy klika typów colliderów: Box, Sphere, Capsule, Mesh, Wheel, Terrain.
- Box (Sześcian), Sphere (Kula), Capsule (Kapsuła) – Odnoszą się oczywiście do kształtu.
- Wheel (Koło) – Daje nam coś w rodzaju pierścienia
- Terrain (Teren) – Kluczowy dla obiektu terenu, ponieważ daje nam możliwość dodania colliderów do wygenerowanych drzew.
- Mesh – Jest to typ collidera, który dopasowuje się idealnie do kształtu obiektu
Nasuwa się pytanie: Jakiego typu collidera użyć? Jeżeli chodzi o kolizje zwykłe, naturalnym wyborem wydawałby się Mesh Collider. Ale jednak nie zawsze będzie to wybór najlepszy. Czemu? Bo zużywa więcej zasobów komputera.Więc jeśli mamy drzwi o wymyślnej fakturze i nałożymy na nie zwykłego box collidera, gracz nawet nie zwróci uwagi, a my zmniejszamy wymagania naszej gry. Ogólnie powinniśmy dążyć do jak największego uproszczenia kształtów colliderów.
Jeżeli chodzi o wyzwolenia. Tutaj Mesh collider również się nie sprawdzi. Ponieważ collidery tego typu są z reguły większe od samego obiektu. Jeżeli wykorzystujemy go np. do zbierania przedmiotów z ziemi, powinien być nawet przesadnie za duży, aby gracz mógł łatwo zebrać przedmiot, a nie musiał jak głupi biegać w kółko, żeby jakimś cudem trafić w nasz collider.
Collidery w scenie Unity3d oznaczone są za pomocą zielonych linii na obiekcie.
Zwykła kolizja (Collision)
Najpierw omówmy sobie parametry naszego komponentu.
- Is Trigger – Zmienia collider w wyzwalacz (omówiony później)
- Material – Możemy dodać colliderowi jakiś typ materiału. Np. gumę, co nada mu fizyczne właściwości gumy.
- Center – Pozycja collidera. Z racji że to komponent, współrzędne ustawiane są względem obiektu. Tzn. (0, 0, 0) to środek obiektu, a nie środek świata gry.
- Size – Rozmiar collidera.
Korzystając z jednej z metod dodania komponentu, dodajemy sobie Box Collider do ustawionego na scenie obiektu (U mnie sześcian). I… właściwie tyle! Jeżeli uruchomimy grę i spróbujemy przejść przez nasz Sześcian, nie powinno nam się to udać.
Oczywiście to nie wszystko co możemy zrobić. Możemy przechwycić wydarzenia.
void OnCollisionEnter(Collision collision) { // Tutaj kod wykonywany po wykryciu kolizji } void OnCollisionStay(Collision collisionInfo) { // Tutaj kod wykonywany co klatkę, w czasie trwania kolizji } void OnCollisionExit(Collision collisionInfo) { // Tutaj kod wykonywany w momencie "opuszczania" kolizji }
function OnCollisionEnter(collision : Collision) { // Tutaj kod wykonywany po wykryciu kolizji } function OnCollisionStay(collisionInfo : Collision) { // Tutaj kod wykonywany co klatkę, w czasie trwania kolizji } function OnCollisionExit(collisionInfo : Collision) { // Tutaj kod wykonywany w momencie "opuszczania" kolizji }
Zdarzenie – wyzwalacz (Trigger)
Jak łatwo zauważyć, żeby zmienić zwykły collider w wyzwalający, wystarczy zaznaczyć checkboxa Is Trigger. Zwiększyłem też rozmiar, abyśmy wykrywali wejście w obszar, zanim dotkniemy obiektu. Reszta zostaje bez zmian.
Często zdarza się że dany obiekt posiada dwa collidery. Jeden zwykły, a drugi wyzwalający. Można takie rozwiązanie zastosować np. w kodzie automatycznych drzwi. Collider wyzwalający odpowiada za to żeby drzwi się automatycznie otwierały, kiedy zwykły odpowiada za to żeby nie dało się przez nie przeniknąć.
O ile w zwykłym colliderze jego obecność jest często wystarczająca, tutaj meritum jest kod, ponieważ wyzwolenia baz kodu robiącego cokolwiek, nawet nie zauważymy.
void OnTriggerEnter(Collider other) { // Tutaj kod wykonywany po wejściu w obszar } void OnTriggerStay(Collider other) { // Tutaj kod wykonywany co klatkę, w czasie obecności obiektu w obszarze } void OnTriggerExit(Collider other) { // Tutaj kod wykonywany w momencie opuszczania obszaru }
function OnTriggerEnter(other : Collider) { // Tutaj kod wykonywany po wejścia w obszar } function OnTriggerStay(other : Collider) { // Tutaj kod wykonywany co klatkę, w czasie obecności obiektu w obszarze } function OnTriggerExit(other : Collider) { // Tutaj kod wykonywany w momencie opuszczenia obszaru }
Mamy 3 decydujące zdarzenia. Enter – wejście, Stay – w czasie, Exit – wyjście. Każda funkcja jako parametr otrzymuje obiekt typu Collider. Teraz pod zmienną other mamy obiekt, który wszedł w interakcję. Załóżmy że nasz kod przypisujemy przeciwnikowi i chcemy żeby po zauważeniu gracza, obrócił się w jego stronę. Oczywiście zauważenie to funkcja OnTriggerEnter. Teraz możemy przypisać jakiś tag naszemu graczowi i rozpoznać go po tagu:
void OnTriggerEnter(Collider other) { if(other.tag == "Player") { gameObject.LookAt(other.transform); } }
Jest to prosty przykład. Ale za pomocą odpowiedniego oprogramowaniach tych zdarzeń, możemy zbudować coś w rodzaju sztucznej inteligencji dla naszych przeciwników.
Rzucanie promieni (Raycasting)
Ostatni typ odbywa się wyłącznie w kodzie. Jeżeli trzymamy się przykładu przeciwnika. W poprzednim przykładzie kod jak collider przypisany byłby do przeciwnika. Jeśli teraz tworzymy np. strzelanie, to kod z użyciem raycastingu znajdzie się w kodzie gracza.
RaycastHit hit; if(Physics.Raycast(transform.position, transform.forward, out hit, 3)) { if(hit.collider.gameObject.tag == "opponent") { // Tutaj kod w przypadku trafienia promieniem w obiekt o tagu opponent } }
var hit : RaycastHit; if(Physics.Raycast(transform.position, transform.forward, hit, 3)) { if(hit.collider.gameObject.tag == "opponent") { // Tutaj kod w przypadku trafienia promieniem w obiekt o tagu opponent } }
Najpierw tworzymy promień w postaci zmiennej hit typu RaycastHit. Następnie wykonujemy rzutowanie promienia. Dzięki temu że wstawiliśmy go w ifa, dalszy ciąg kodu wykona się tylko jeśli w coś trafiliśmy.
Szybki rzut oka na parametry funkcji Physics.Raycast():
- Początek promienia – Czyli gdzie ma się zacząć. Najczęściej będzie to pozycja obiektu z którego rzutujemy.
- Kierunek promienia – Czyli w którym kierunku od punktu początku ma się rozwijać. (transform.forward – kierunek w którym spogląda obiekt)
- Informacje o rzucanym promieniu – Zmienna o strukturze RaycastHit
- Długość promienia – Odległość na jaką ma się ciągnąć promień w jednostkach ustalonych w grze
W C# przed hit dodajemy słowo kluczowe out. Pojawia się ono dlatego, abyśmy mogli otrzymać w zmiennej dane, które uzyskała wewnątrz funkcji Physics.Raycast().
Podsumowanie
Poznaliśmy dziś 3 metody wykrywania kolizji, oraz możliwości związane z tym. Jest to jeden z najczęściej wykorzystywanych mechanizmów w środowisku Unity3d jak i ogólnie w grach. Warto jednak mądrze korzystać z tego mechanizmu żeby nie przeciążyć zasobów komputera.
Jeżeli masz jakieś pytanie zadaj je w komentarzu. :)