외국 유튜버의 강좌를 보다가 델리게이트와 유니티 이벤트, 액션을 알게되었다.
근데 유니티 이벤트랑 유니티 액션 이전에 이벤트 개념 자체를 한번 살펴보기로 했다.
#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로 아무것도 없는 상자를 전달 할 수도 있지만
위에 설명한 방식을 활용해서 버튼 클릭한 횟수라던지 이벤트 실행한 횟수 등
다양하게 활용가능하니 어려운 부분이여도 한번 직접 만들어서 활용해보면 좋다.
더 자세한 내용은 아래 링크에
코드를 보면 EventHandler뒤에 제네릭<T>을 붙여 주는데 이 제네릭에 들어갈 EventArgs의 클래스를 만들면 된다.
주의할 점은 매개변수에 있는 우리가 만든 EventArgs에 new 키워드 붙여서 새로운 인스턴스를 만들어야한다.
┎(심화) 다른 클래스에서 위의 커스텀 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으로 해도 인스펙터에 안나옴
- 구독과 구독취소는 +=과 -= 연산자 사용
- UnityEvent의 AddListener에 넣을 수 있음
┎UnityEvent의 AddListener에 UnityAction 넣는법
public UnityEvent OnSpacePressed; //요 이벤트에다가 유니티액션 넣기 가능
public UnityAction unityAction;
private void Start() {
OnSpacePressed.AddListener(unityAction);
}
.Net의 기본적인 이벤트와 비슷한게 생겼지만 +=과 -= 연산자 때문에 같은 스크립트 내에서 주로 쓴다.
오늘 배운 유니티 이벤트와 액션으로 코드를 정리해야겠단 생각이 들었다.
'유니티 > 유니티 관련 지식' 카테고리의 다른 글
유니티 프리팹 배리언트(Prefab variant)? 프리펩 상속 느낌? (0) | 2023.02.17 |
---|---|
유니티 인터페이스(Interface) 실전 사용해보기 (0) | 2023.02.15 |
유니티 포스트 프로세싱의 개쩌는 효과들 체험기 (0) | 2023.02.02 |
유니티(C#)에서의 코딩 규칙(네이밍 규칙) (0) | 2023.01.31 |