02
05

외국 유튜버의 강좌를 보다가 델리게이트와 유니티 이벤트, 액션을 알게되었다.

근데 유니티 이벤트유니티 액션 이전에 이벤트 개념 자체를 한번 살펴보기로 했다.


#1 Event(이벤트) 개념

 

 

우선 프로그래밍 세계에서 이벤트(Event)는 하나의 시스템이다.

클릭이나 터치에 따른 반응을 처리하는 그러한 시스템을 뜻한다.

 

이벤트(Event)에서는 제공자구독자가 각각 다음과 같은 역할을 한다.

 

제공자 :사용자의 행동을 기다리다가구독자에게 알리는 역할

 

구독자 : 공급자를 구독해서 사용자의 행동을 전달받아 반응하는 역할

 

즉, 제공자의 여러 이벤트(Event)들을

리스너같은 구독자들이 구독해서 정보를 기다리고 있는 것이다.

 

이벤트하면 꼭 델리게이트를 언급하는데 그 이유를 찾아보았다.

C#에서 이벤트가 델리게이트의 확장개념이라고하는데 MulticaseDelegate <- 이거의 확장이라고한다.

 

델리게이트가 더 큰 개념이다.

 

Event(이벤트) 구조

 

위의 사진은 이벤트로 로직을 구현하면 구독할 비주얼적인 모델 같은것만 바꾸면 되는 재활용하기 편한 구조를 나타낸 것이다.

 

여기서 제공자(이벤트)는 누가 구독자인지 몰라도 되니깐 참조관계가 깔끔하다.

 

구독자가 뭘하든 제공자는 알필요가 없다. 

중요한건 제공자 오브젝트를 파괴해버리면 구독자들이 다 미아 상태가 되버리니 주의할 필요가 있다.

 


#2 Event(이벤트) 사용법

이제 .Net의 기본적인 이벤트의 사용법을 알아보자

 

우선 네임스페이스 정의부터

using System;

 

이벤트(제공자)를 정의하는 법 - On동사/시제 형식

public event EventHandler OnSpacePressed; //이벤트 정의

event라는 키워드 뒤에 EventHandler 타입을 붙여준다.

 

변수 이름을 지을 땐 보통 On에다가 동사나 시제를 붙여주면된다.

참고로 EventHandler와 같은 핸들러들은 보통 터치나 클릭같은 이벤트들를 관찰하는 역할을 부른다.

 

 

 

이벤트(제공자)를 호출하는 법 ① - 이벤트(this, EventArgs.Empty)

public void Update() {
    if (Input.GetKeyDown(KeyCode.Space)) {
        //스페이스바 눌리면
        if (OnSpacePressed != null)
            OnSpacePressed(this, EventArgs.Empty); // 이벤트 핸들러들을 호출 
    }
}

이벤트 헨들러를 null 검사하는 이유는 아직 구독한 구독자가 없기 때문이다.

 

 

이벤트(제공자)를 호출하는 법 ② - Invoke 함수 사용

public void Update() {
    if (Input.GetKeyDown(KeyCode.Space)) {
        //스페이스바 눌리면
        OnSpacePressed?.Invoke(this, EventArgs.Empty); // 이벤트 핸들러들을 호출 
    }
}

 

근데 null 검사를 좀 더 멋있게 쓰는 방법이 있다.

Invoke 함수를 통해 이벤트를 호출하여 구독자 목록을 순차적으로 실행하면서

?. 부분으로 null이 아니여야 액세스 가능하게 하는 것이다.

 

 

이벤트(제공자)의 구독자 시그니처

//표준 이벤트 구독자 시그니처
private void Test_OnSpacePressed(object sender, EventArgs eventArgs) 
{
    Debug.Log("스페이스 바 눌림!");
}

 

구독자 시그니처에서 시그니처란?

더보기

컴퓨터 함수 서명 혹은 메서드 서명은 컴파일러가 함수를 구분하기 위한 구성요소를 말한다. 

만약 두 함수의 이름, 매개변수의 개수, 타입까지 모두 같다면 이 두 함수의 시그니처는 같다고 할 수 있다.

결론은 위에 구독자 시그니처구독자를 구현한 것이다.

 

구독자 표준 반환값은 void 인데 void 외에도 다 되지만 구독자 호출 구분이 어려워서 EventArgs를 사용해야한다.

구독자 표준 매개변수로는 object 와 EventArgs 인데

 

object공급자를 호출하여 이벤트를 발생시킨 객체이며

EventArgs이벤트와 관련한 변수같은 데이터가 담기는 상자이다.

여기에 담긴 변수들은 구독자들도 공유해서 사용가능하다. 

 

이벤트(제공자)를 구독하기

private void Start() {
    OnSpacePressed += Test_OnSpacePressed; //구독 꾹
}

보통 Start 함수 부분에서 만들어둔 구독자 시그니처+= 연산자로 구독시키면 된다.

 

이벤트(제공자)를 구독취소

private void Start() {
    OnSpacePressed -= Test_OnSpacePressed; //구취
}

구독 취소도 간단한데 -= 연산자로 구독취소 하면된다.

약간 델리게이트 쓰는 느낌?

 

다른 클래스에서 이벤트(제공자)를 구독

private void Start() {
    TestingEvents testingEvents = GetComponent<TestingEvents>();
    testingEvents.OnSpacePressed += Test_OnSpacePressed; //구독 꾹
}

//표준 이벤트 구독자 시그니처
private void Test_OnSpacePressed(object sender, EventArgs eventArgs) 
{
    Debug.Log("구독자의 스페이스바!");
}

같은 오브젝트라는 전제에서 이벤트(제공자)의 클래스를 가져와서 구독 하면된다.

에디터를 사용하면 구독자 시그니처 자동완성이 된다. 

이벤트에서는 구독자누군지 몰라도 되므로 그저 자기의 EventArgs 목록에 있는 구독자들을 실행한다.

 

 

(심화) 이벤트(제공자)를 EventArgs + 제네릭으로 더 디테일하게

public event EventHandler<OnSpacePressedEventArgs> OnSpacePressed; //이벤트 정의
public class OnSpacePressedEventArgs : EventArgs {
    public int spaceCount;
}
public void Update() {
    if (Input.GetKeyDown(KeyCode.Space)) {
        //스페이스바 눌리면
        OnSpacePressed?.Invoke(this, new OnSpacePressedEventArgs { spaceCount = 0 }); // 이벤트 핸들러들을 호출 
    }
}

 

(심화) 부분 좀 더 디테일한 설명 ↓

더보기

위에서 설명했듯이 EventArgs는 이벤트를 위한 변수를 담는 상자이다.

따라서 이벤트를 실행할 때 만약에 구독자들이 필요한 변수 있다면 공유할 수 있다.

 

위에 코드를 잘 보면 spaceCount를 새로 인스턴스 생성해서 0으로 초기화했는데

이 spaceCount는 OnSpacePressed 구독한 함수들에서 사용가능하다.

 

구독자 함수에서 spaceCount == 0; 이나 spaceCount = 10; 이런식으로 사용가능하다.

 

간혹 전달할게 없으면 성능 위해서 굳이 인스턴스 생성하지않고

EventArgs.Empty로 아무것도 없는 상자를 전달 할 수도 있지만

 

위에 설명한 방식을 활용해서 버튼 클릭한 횟수라던지 이벤트 실행한 횟수 등

다양하게 활용가능하니 어려운 부분이여도 한번 직접 만들어서 활용해보면 좋다.

 

더 자세한 내용은 아래 링크에

제네릭 방식으로 EventArgs 자식 스크립트를 활용 메뉴얼

 

코드를 보면 EventHandler뒤에 제네릭<T>을 붙여 주는데 이 제네릭에 들어갈 EventArgs의 클래스를 만들면 된다.

주의할 점은 매개변수에 있는 우리가 만든 EventArgsnew 키워드 붙여서 새로운 인스턴스를 만들어야한다.

 

 

(심화) 다른 클래스에서 위의 커스텀 EventArgs 갖다 쓸 때

private void Test_OnSpacePressed(object sender, TestingEvents.OnSpacePressedEventArgs e) 
{
    Debug.Log("구독자의 스페이스바!" + e.spaceCount);
}

매개변수에 위와같이 쓰면 된다.

 

만약 EventHandler 대신 델리게이트로 쓰고 싶다면 아래 글 보시면 됩니다.

코더 제로 - [유니티 C# 강좌] 17. 이벤트(Event), 대리자 (델리게이트, Delegate)

 


#3 UnityEvent(유니티 이벤트) 사용법

이제 드디어 유니티 이벤트를 사용해보자

 

우선 네임스페이스 정의부터

using UnityEngine.Events;

 

유니티이벤트(제공자)를 정의하는 법

public UnityEvent OnSpacePressed;

이때 public으로 하면 유니티 에디터의 인스펙터창에서 구독자를 수동으로 추가 가능하지만

개인적으로 선호하진 않기에 [HideInInspector] 키워드를 쓰도록 하자

 

유니티이벤트(제공자)를 호출하는 법

public void Update() {
    if (Input.GetKeyDown(KeyCode.Space)) {
        //스페이스바 눌리면
        if (OnSpacePressed != null)
            OnSpacePressed.Invoke(); // 이벤트 핸들러들을 호출 
    }
}

 

유니티이벤트(제공자)를 호출하는 법 ②

public void Update() {
    if (Input.GetKeyDown(KeyCode.Space)) {
        //스페이스바 눌리면
        OnSpacePressed?.Invoke(); // 이벤트 핸들러들을 호출 
    }
}

위에 이벤트설명 할 때 처럼 ?. 연산자로 null 아니여야 접근 가능하게 하면된다.

 

 

다른 클래스에서 유니티이벤트(제공자)를 구독

private void Start() {
    TestingEvents testingEvents = GetComponent<TestingEvents>();
    testingEvents.OnSpacePressed.AddListener(Test_OnSpacePressed); //구독 꾹
}

//구독자로 쓸 함수
private void Test_OnSpacePressed() 
{
    Debug.Log("구독자의 스페이스바!");
}

일반적인 이벤트들과 다른점은 AddListener이라는 콜백함수를 쓴다.

또한 구독자 시그니처를 구현할 필요가 없어서 상당히 깔끔한거같다.

반대로 구독 취소RemoveListener을 쓰면된다.

 

유니티이벤트(제공자)제네릭 매개변수 지정

public UnityEvent<int> OnOnSpacePressed;

그리고 다른 클래스 구독자의 함수의 매개변수도 함께 넣고 싶으면 형식을 지정해줘야 등록가능하다.

예를들어 아래 함수처럼 int s가 있으면 <int> 제네릭을 UnityEvent뒤에 넣어줘야한다.

//구독자로 쓸 함수
private void Test_OnSpacePressed(int s) 
{
    Debug.Log("구독자의 스페이스바!");
}

#4 UnityAction(유니티 액션) 사용법

기본적으로 유니티 액션유니티 이벤트와 같은 역할을 한다.

 

유니티 이벤트와의 차이점은 다음과 같다

  • public으로 해도 인스펙터에 안나옴
  • 구독과 구독취소는 +=-= 연산자 사용
  • UnityEventAddListener에 넣을 수 있음

UnityEventAddListenerUnityAction 넣는법

public UnityEvent OnSpacePressed; //요 이벤트에다가 유니티액션 넣기 가능
public UnityAction unityAction;

private void Start() {
    OnSpacePressed.AddListener(unityAction);
}

.Net의 기본적인 이벤트와 비슷한게 생겼지만 +=과 -= 연산자 때문에 같은 스크립트 내에서 주로 쓴다.

오늘 배운 유니티 이벤트와 액션으로 코드를 정리해야겠단 생각이 들었다.

COMMENT