강의

멘토링

로드맵

Inflearn brand logo image

인프런 커뮤니티 질문&답변

Rio Song님의 프로필 이미지
Rio Song

작성한 질문수

절대강좌! 유니티 6 - TPS 게임으로 배우는 유니티 마스터클래스

BehaviorGraph - ScriptableObject를 활용한 Event Chanel

Player에 Event 연결 관련 질문

작성

·

55

0

안녕하세요.

image.pngimage.png

 

위와 같이 정상적으로 Event Channel를 만들고, Scriptable Object도 생성 후 연결한 상황입니다.

그런데, PlayerController에서 Event를 연결시켜야 하는데, Event가 없습니다.

image.png

지우고 다시 해봐도 Event가 나타나지 않는데, 혹시 제가 잘못한 부분이 있을까요?

답변 2

0

이재현님의 프로필 이미지
이재현
지식공유자

안녕하세요. Rio Song님,

확인해보니 최신 BehaviorGraph 1.0.10에서는 Event 프로퍼티를 직접 접근하는 방식이 변경되었습니다. ㅠㅠ

우선 아래 코드와 같이 변경해서 적용해주세요. 아직 강의 원본 프로젝트에 적용시켜 보지 않아서 정확하지는 않을 수 있습니다.

 

원본 강의 프로젝트를 업그레이드 하고 테스트해 본 후 다시 말씀드리겠습니다.

 

    [SerializeField] private ApplyDamageToPlayer _applyDamageDamageToPlayer;
    private Action<Transform, float> _handler;   
    
    private void OnEnable()
    {
        _handler = OnPlayerDamaged;
        _applyDamageDamageToPlayer.RegisterListener(_handler);
    }


    private void OnDisable()
    {
        _applyDamageDamageToPlayer.UnregisterListener(_handler);
    }
Rio Song님의 프로필 이미지
Rio Song
질문자

https://docs.unity3d.com/Packages/com.unity.behavior@1.0/manual/bind-c.html#c-binding-using-event-channel-asset

공식 문서에는 아직도 Event가 있어서 제가 잘못한 것인 줄 알았습니다.


일단 알려주신 대로 수정하고 실행하니 데미지가 들어오지 않습니다.

image.png

RegisterListener 메서드를 찍어보니, m_Event에 등록이 안되고 있는 것 같은데 저도 조금 더 해결책을 찾아보겠습니다.

이재현님의 프로필 이미지
이재현
지식공유자

우선 ApplyDamageToPlayer.cs 코드를 다음과 같이 수정하시면 Event += OnPlayerDamaged; 코드는 동작하실 겁니다. 네부적으로 handler를 등록하고 Event를 Invoke하도록 되어 있습니다. 이번에 변경되 방식을 좀 더 간단히 처리하는 부분은 좀 더 테스트 완료 후 말씀드리겠습니다.

 

public partial class ApplyDamageToPlayer : EventChannelBase
{
    public delegate void ApplyDamageToPlayerEventHandler(Transform Player, int Damage);
    public event ApplyDamageToPlayerEventHandler Event; 

    public void SendEventMessage(Transform Player, int Damage)
    {
        Event?.Invoke(Player, Damage);
    }

    public override void SendEventMessage(BlackboardVariable[] messageData)
    {
        BlackboardVariable<Transform> PlayerBlackboardVariable = messageData[0] as BlackboardVariable<Transform>;
        var Player = PlayerBlackboardVariable != null ? PlayerBlackboardVariable.Value : default(Transform);

        BlackboardVariable<int> DamageBlackboardVariable = messageData[1] as BlackboardVariable<int>;
        var Damage = DamageBlackboardVariable != null ? DamageBlackboardVariable.Value : default(int);

        Event?.Invoke(Player, Damage);
    }

    public override Delegate CreateEventHandler(BlackboardVariable[] vars, System.Action callback)
    {
        ApplyDamageToPlayerEventHandler del = (Player, Damage) =>
        {
            BlackboardVariable<Transform> var0 = vars[0] as BlackboardVariable<Transform>;
            if(var0 != null)
                var0.Value = Player;

            BlackboardVariable<int> var1 = vars[1] as BlackboardVariable<int>;
            if(var1 != null)
                var1.Value = Damage;

            callback();
        };
        return del;
    }

    public override void RegisterListener(Delegate del)
    {
        Event += del as ApplyDamageToPlayerEventHandler;
    }

    public override void UnregisterListener(Delegate del)
    {
        Event -= del as ApplyDamageToPlayerEventHandler;
    }
}
Rio Song님의 프로필 이미지
Rio Song
질문자

감사합니다.

override 하는 방법이 있었군요.

이재현님의 프로필 이미지
이재현
지식공유자

BehaviorGraph 이벤트의 Base 클래스가 EventChannel 로 변경되었기에 앞서 보내드린 코드 보다 좀 더 간결한 코드를 보내드립니다.

블랙보드변수를 인자로 전달 받기 때문에 따로 찾아오는 번거로움은 면할 것 같네요.

아직 자료가 별로 없어서 정확한 방법인지는 확신할 수는 없지만 또 다른 방식이 있다면 답글 드리겠습니다. 알려주셔서 감사합니다.

 

public sealed partial class ApplyDamageByPlayer : EventChannel<Transform, int>
{
    public delegate void EventHandler(Transform Player, int Damage);
    public event EventHandler Event;

    public void SendEventMessage(Transform Player, int Damage)
    {
        Event?.Invoke(Player, Damage);
    }

    public override void RegisterListener(Delegate del)
    {
        Event += del as EventHandler;
    }

    public override void UnregisterListener(Delegate del)
    {
        Event -= del as EventHandler;
    }    
}

 

Rio Song님의 프로필 이미지
Rio Song
질문자

앞에 알려주신 긴 코드는 정상 동작하지만, 뒤에 알려주신 코드는 Event가 정상적으로 등록이 된 것 같은데, 데미지가 안들어 오는 문제가 있습니다.

image.png

Debug로 짧은 코드의 public void SendEventMessage에 breakpoint를 걸어봤지만, 해당 메서드가 호출되지 않습니다.

 

아래와 같이 override void SendEventMessage를 추가해야 정상 동작하는 것을 확인하였습니다.

확인해보니 override된 SendEventMessage 메서드가 없으면, EventChannel의 SendEventMesage가 호출되면서 EventChannel의 m_Event?.Invke()가 실행되면서 발생한 문제인 것 같습니다.

 

public sealed partial class ApplyDamageToPlayer : EventChannel<Transform, float>
{
    public delegate void EventHandler(Transform Player, float Damage);
    public event EventHandler Event;

    public void SendEventMessage(Transform Player, float Damage)
    {
        Event?.Invoke(Player, Damage);
    }

    public override void SendEventMessage(BlackboardVariable[] messageData)
    {
        var PlayerBlackboardVariable = messageData[0] as BlackboardVariable<Transform>;
        var Player = PlayerBlackboardVariable != null ? PlayerBlackboardVariable.Value : default;

        var DamageBlackboardVariable = messageData[1] as BlackboardVariable<float>;
        float Damage = DamageBlackboardVariable != null ? DamageBlackboardVariable.Value : default;

        Event?.Invoke(Player, Damage);
    }

    public override void RegisterListener(Delegate del)
    {
        Event += del as EventHandler;
    }

    public override void UnregisterListener(Delegate del)
    {
        Event -= del as EventHandler;
    }
}
이재현님의 프로필 이미지
이재현
지식공유자

질문 주신 후 기존 코드에서 이벤트채널을 새로 만들어서 테스트를 했습니다. 확인해보니 처음 만든 이벤트채널은 EventChannelBase 클래스를 기반으로 제가 드렸던 긴 코드가 있는 상태여서 SendEventMessage가 override 된 상태여서 짧은 코드가 동작했었던 것으로 추축해봅니다. 기존의 긴 코드를 삭제한 후 에는 Rio Song 님과 같이 이벤트가 호출되지 않았습니다. 알려주셔서 감사합니다.

0

안녕하세요, 인프런 AI 인턴입니다. Rio Song님.

질문해 주신 내용은 Unity에서 ScriptableObject를 활용하여 Event Channel을 만들고 이를 PlayerController와 연결하는 과정에서 발생한 문제에 대한 것입니다. Event 시스템을 제대로 설정하기 위해 몇 가지 확인해야 할 사항이 있습니다.

Event Channel 확인 사항

  1. ScriptableObject의 설정:

    • ScriptableObject가 올바르게 생성되고, 원하는 이벤트 타입(Event Channel)이 포함되어 있는지 확인하세요. ApplyDamageToPlayer를 제대로 설정하셨는지 파일 이름과 타입을 검토해 보세요.
  2. SerializeField 적용 여부:

    • PlayerController 스크립트에 [SerializeField]로 선언한 applyDamageToPlayer 변수가 에디터에서 올바르게 할당되어 있는지 확인합니다. 변수 이름이나 참조가 맞는지 살펴보세요.
  3. 이벤트 등록:

    • OnEnable()OnDisable() 메서드에서 이벤트를 올바르게 등록하고 해제하는지 확인해주세요. 일반적으로, Event 속성을 사용하여 이벤트를 구독 및 해제합니다. 아래와 같은 형태로 구현이 되어 있는지 확인해 보세요:
   private void OnEnable()
   {
       applyDamageToPlayer.Event += OnDamageEvent;
   }

   private void OnDisable()
   {
       applyDamageToPlayer.Event -= OnDamageEvent;
   }

   private void OnDamageEvent()
   {
       // Event 처리 로직
   }
  1. 타입 확인:
    • ApplyDamageToPlayer의 타입이 올바른지, 특히 Event 속성이 실제로 정의되어 있는지 살펴보세요. 타입에 적절한 이벤트 프로퍼티나 메서드가 포함되어 있는지 코드와 파일을 검토하는 것이 중요합니다.

유사한 질문 참조

저는 질문자님의 이해를 돕고자 지식공유자님의 기존 답변을 참고하여 유사한 답변 링크를 추천해드리고 있습니다.
현재 베타 기능으로 답변이 만족스럽지 않을 수 있는 점 양해 부탁드립니다. 🙏
추가적으로 궁금한 점이 있으시면, 이어서 질문해 주세요. 곧 지식공유자께서 답변해 주실 것입니다.

Rio Song님의 프로필 이미지
Rio Song

작성한 질문수

질문하기