그렇다면 해당 Delegate를 등록받는 onClick 이라는 녀석은 무엇일까? 바로 이 onClick이 UnityEventBase를 상속받는 UnityEvent 클래스의 Instance이다. 즉, 이 Class의 Instance에 UnityAction을 Register하는 것이다. 그런 다음 해당 클래스의 Invoke 메소드가 호출되면서 모든 등록된 Delegate들이 실행되게 된다.
위 코드에서 A키를 8회 눌렀을 때 출력은 아래와 같다. 순서대로 찍히고 마지막에 다 찍힌다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
1>Air
2>Baby
3>Cat
4>Do
5>Ear
6>Fly
7>Good
8>Air
Baby
Cat
Do
Ear
Fly
Good
동일한 기능을 하는 코드를 UnityEvent와 UnityAction을 이용하면 아래와 같이 구현할 수 있다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
using UnityEngine;
using System.Collections;
using UnityEngine.Events;
using UnityEngine.UI;
publicclassUnityEventTest:MonoBehaviour{
publicUnityAction _unityAction;
publicUnityEvent[]_event=newUnityEvent[8];
intstate=0;
voidStart()
{
//create unityaction delegate
UnityAction[]action=newUnityAction[8];
action[0]=newUnityAction(FuncA);
action[1]=newUnityAction(FuncB);
action[2]=newUnityAction(FuncC);
action[3]=newUnityAction(FuncD);
action[4]=newUnityAction(FuncE);
action[5]=newUnityAction(FuncF);
action[6]=newUnityAction(FuncG);
action[7]=newUnityAction(FuncA);
action[7]+=newUnityAction(FuncB);
action[7]+=newUnityAction(FuncC);
action[7]+=newUnityAction(FuncD);
action[7]+=newUnityAction(FuncE);
action[7]+=newUnityAction(FuncF);
action[7]+=newUnityAction(FuncG);
//register
for(inti=0;i<8;i++)
{
_event[i]=newUnityEvent();
_event[i].AddListener(action[i]);
}
}
voidUpdate()
{
if(Input.GetKeyDown(KeyCode.A))
{
_event[state].Invoke();
state=(++state>7)?0:state;
}
}
publicvoidFuncA(){Debug.Log("Air");}
publicvoidFuncB(){Debug.Log("Baby");}
publicvoidFuncC(){Debug.Log("Cat");}
publicvoidFuncD(){Debug.Log("Do");}
publicvoidFuncE(){Debug.Log("Ear");}
publicvoidFuncF(){Debug.Log("Fly");}
publicvoidFuncG(){Debug.Log("Good");}
}
이렇게 하면 코드량은 조금 더 길어 보일 지 모르나, 각 이벤트에 등록된 UnityAction의 추가(AddListener) 및 삭제(RemoveListener)가 동적으로 자유로우므로, 마치 Observer Pattern을 적용한 느낌도 나면서 훨씬 더 유연한 코드 작성이 가능하다. (사실 UnityEvent는 미리 구현하여 내장된 Observer Pattern으로 보는 것이 맞을 것이다.)
아래와 같이 추상 클래스인 UnityEvent<T0>를 상속하여 선언함으로써 인자를 넘겨 주는 타입의 UnityAction을 호출할 수도 있다. 인자를 4개 받는 것까지 확장 가능하다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
publicclassMyEvent:UnityEvent<int>{}
publicclassUnityEventTest:MonoBehaviour{
publicUnityAction<int>_unityAction;
publicMyEvent[]_event=newMyEvent[8];
intstate=0;
voidStart()
{
//create unityaction delegate
UnityAction<int>[]action=newUnityAction<int>[8];
action[0]=newUnityAction<int>(FuncA);
action[1]=newUnityAction<int>(FuncB);
action[2]=newUnityAction<int>(FuncC);
action[3]=newUnityAction<int>(FuncD);
action[4]=newUnityAction<int>(FuncE);
action[5]=newUnityAction<int>(FuncF);
action[6]=newUnityAction<int>(FuncG);
action[7]=newUnityAction<int>(FuncA);
action[7]+=newUnityAction<int>(FuncB);
action[7]+=newUnityAction<int>(FuncC);
action[7]+=newUnityAction<int>(FuncD);
action[7]+=newUnityAction<int>(FuncE);
action[7]+=newUnityAction<int>(FuncF);
action[7]+=newUnityAction<int>(FuncG);
//register
for(inti=0;i<8;i++)
{
_event[i]=newMyEvent();
_event[i].AddListener(action[i]);
}
}
voidUpdate()
{
if(Input.GetKeyDown(KeyCode.A))
{
_event[state].Invoke(100);
state=(++state>7)?0:state;
}
}
publicvoidFuncA(intcnt){Debug.Log("Air:"+cnt);}
publicvoidFuncB(intcnt){Debug.Log("Baby:"+cnt);}
publicvoidFuncC(intcnt){Debug.Log("Cat:"+cnt);}
publicvoidFuncD(intcnt){Debug.Log("Do:"+cnt);}
publicvoidFuncE(intcnt){Debug.Log("Ear:"+cnt);}
publicvoidFuncF(intcnt){Debug.Log("Fly:"+cnt);}
publicvoidFuncG(intcnt){Debug.Log("Good:"+cnt);}
}
Unity에서 사용하는 Mono 2.x는 C# 3.0까지는 안전하게 지원하므로, C# 3.0 버젼의 Lambda를 이용할 수 있다. Lambda expression은 anonymous function의 일종으로 이를 이용하면 간단히 delegate를 생성 가능하다. 따라서, 아래와 같이 동일 기능을 더 간단히 구현할 수 있다. Lambda 정의 Ref