You are currently viewing Unity3D: Detekcja wielu zdarzeń OnTriggerEnter w ramach jednego GameObject

Unity3D: Detekcja wielu zdarzeń OnTriggerEnter w ramach jednego GameObject

Wyobraźmy sobie, że pisząc grę – powiedzmy piłkę nożną – chcemy sprawdzić czy gracz strzelając gola trafił w prawe górne okienko, lewe dolne, czy może od poprzeczki. Efekt ten możemy osiągąć używając tzw. triggerów. Niestety, w przypadku wielu triggerów przypiętych do tego samego GameObject, Unity by design, nie pozwala na identyfikację, który z nich został aktywowany. Natomiast okazuje się, że zmieniając nieco podejście, można w bardzo prosty sposób, osiągnąć zamierzony efekt i o tym właśnie jest ten artykuł.

W czym problem?

Wyobraźmy sobie sytuację, w której mamy sześcian oraz sferę i chcielibyśmy wykryć dwa zdarzenia – gdy sfera koliduje z górną połową sześcianu oraz gdy koliduje z jego dolną częścią. Pierwsze rozwiązanie, które przychodzi na myśl, to utworzenie dwóch BoxCollider’ów w ramach sześcianu, a następnie obsłużenie zdarzenia OnTriggerEnter w skrypcie. Niestety w takim przypadku, mimo że interakcja z oboma Collider’ami spowoduje wywołanie metody OnTriggerEnter, to nie będziemy w stanie ustalić, który z nich został w danym momencie aktywowany.

Zasoby

Gotowy projekt znajdziesz na moim GitLab. Poradnik w wersji wideo znajdziesz na moim kanale YouTube.

Właściwe podejście to podstawa

Zamiast tworzyć dwa Collidery w ramach jednego GameObject naszego sześcianu, możemy zrobić coś innego. Do sześcianu dodajemy dwa puste GameObject i w ramach każdego z nich dodajemy po jednym obiekcie BoxCollider. Następnie pozostaje już tylko obsłużyć zdarzenia w skryptach C#. Aby uprościć sobie to zadanie, możemy skorzystać z Detektora, który opisałem w poprzednim artykule. Wystarczy przypisać komponenty detektorów do utworzonych przed chwilą GamęObject z Colliderami.

Dzięki takiemu podejściu jedyne co zostanie nam do zaimplementowania, to skrypt MultiDetectora, który wykorzystuje dwa proste Detektory. Poniżej zaprezentuję dwa warianty zaimplementowania takiego MultiDetectora oraz omówię różnice miedzy nimi.

MultiDetectorV1 – Zdarzenia konfigurowane z poziomu inspektora

Pierwszy z MultiDetektorów, to tak naprawdę tylko zestaw metod pełniacych rolę listenerów zdarzeń, które pochodzą z górnego oraz dolnego Detektora.

using UnityEngine;

public class MultiAreaDetector : MonoBehaviour
{
    public void WhenTopTriggerEnter(Collider collider)
    {
        Debug.Log("WhenTopTriggerEnter");
    }

    public void WhenTopTriggerExit(Collider collider)
    {
        Debug.Log("WhenTopTriggerExit");
    }

    public void WhenBottomTriggerEnter(Collider collider)
    {
        Debug.Log("WhenBottomTriggerEnter");
    }

    public void WhenBottomTriggerExit(Collider collider)
    {
        Debug.Log("WhenBottomTriggerExit");
    }
}

W tym przypadku przekierowanie zdarzeń z górnego i dolnego Detektora dokonujemy w inspektorze Unity – tak jak to zostało pokazane poniżej.

W takim podejściu nasz MultiDetector nie jest świadomy, co jest źródłem zdarzenia, ani jakim mechanizmem trafia ono do metod. Minusem tego podejścia jest jednak to, że konfiguracji przekierowania zdarzeń dokonujemy na obiekcie będącym ich źródłem. Czyli źródło zdarzenia niejako jest świadome tego, kto je obsłuży. Osobiście nie podoba mi się takie podejście więc poniżej przedstawię alternatywną wersję MultiDetectora.

MultiDetectorV2 – Detektory konfigurowane z poziomu inspektora

W tej wersji MultiDetectora, w inspektorze Unity musimy wskazać instancje górnego i dolnego Detektora. Następnie MultiDetector sam zarejestruje stosowne listenery. W odróżnieniu od poprzedniej wersji, w tym przypadku, to odbiornik zdarzenia odpowiada za powiązanie detektor-odbiornik. Moim zdaniem, odwrócenie zależności w ten sposób sprawia, że projekt staje się prostszy w utrzymaniu.

using UnityEngine;

namespace DefaultNamespace
{
    public class MultiAreaDetectorV2: MonoBehaviour
    {
        [SerializeField] private Detector topAreaDetector;
        [SerializeField] private Detector bottomAreaDetector;

        private void OnEnable()
        {
            topAreaDetector.onTriggerEnter.AddListener(WhenTopTriggerEnter);
            topAreaDetector.onTriggerExit.AddListener(WhenTopTriggerExit);
            bottomAreaDetector.onTriggerEnter.AddListener(WhenBottomTriggerEnter);
            bottomAreaDetector.onTriggerExit.AddListener(WhenBottomTriggerExit);
        }
        
        private void OnDisable()
        {
            topAreaDetector.onTriggerEnter.RemoveListener(WhenTopTriggerEnter);
            topAreaDetector.onTriggerExit.RemoveListener(WhenTopTriggerExit);
            bottomAreaDetector.onTriggerEnter.RemoveListener(WhenBottomTriggerEnter);
            bottomAreaDetector.onTriggerExit.RemoveListener(WhenBottomTriggerExit);
        }
        
        
        public void WhenTopTriggerEnter(Collider collider)
        {
            Debug.Log("WhenTopTriggerEnter");
        }

        public void WhenTopTriggerExit(Collider collider)
        {
            Debug.Log("WhenTopTriggerExit");
        }

        public void WhenBottomTriggerEnter(Collider collider)
        {
            Debug.Log("WhenBottomTriggerEnter");
        }

        public void WhenBottomTriggerExit(Collider collider)
        {
            Debug.Log("WhenBottomTriggerExit");
        }
    }
}

Zobaczmy jeszcze jak wygląda konfiguracja drugiego wariantu MultiDetektora z poziom inspektora Unity.

To już wszystko co dla Ciebie przygotowałem w tym poradniku. Udostępnij proszę ten post aby pomóc mi dotrzeć do większego grona odbiorców. Dzięki i do zobaczenia w kolejnym poradniku.