미리 정의된 델리게이트 타입인 Action에 대해서 정리해본다.
델리게이트는 메서드를 변수처럼 사용할 수 있게하는 도구로
반환형이 없는 Action,
반환형이 있는 Func,
커스텀으로 사용하는 delegate,
총 3가지 타입이 있다.
Action은 반환값이 없는 메서드를 참조할 수 있는 델리게이트 타입이다.
즉, void로 선언된 메서드를 참조할 수 있다.
매개변수의 개수에 따라 다양한 오버로드가 존재하고
Action, Action<T>, Action<T1, T2> … 최대 16개까지 매개변수 지원한다.
반대로 반환값이 있는 메서드는 Func 델리게이트를 사용해야 한다.
기본적인 사용법은 다음과 같다.
using System;
public class DelegateExample
{
void SayHello()
{
Console.WriteLine("Hello!");
}
void Run()
{
//델리게이트 연결
Action sayHello = SayHello;
// 델리게이트 호출
sayHello(); // 출력: Hello!
}
}
//-------------------------------------------------------------
// 람다식으로 표현
using System;
public class DelegateExample
{
void Run()
{
// 람다식으로 바로 구현
Action sayHello = () => Console.WriteLine("Hello!");
sayHello(); // 출력: Hello!
}
}
//---------------------------------------------------------------
//매개 변수 포함
Action<string> greet = (name) => Console.WriteLine($"Hello, {name}!");
greet("우앜");
Action<string, int> showInfo = (name, age) => Console.WriteLine($"{name}은 {age}살입니다.");
showInfo("현오", 25);
| 구문 | 설명 |
|---|---|
Action act = MyMethod; | 일반 함수 연결 |
act += AnotherMethod; | 여러 메서드 연결 가능 |
act -= MyMethod; | 제거도 가능 |
act?.Invoke(); | null 체크 안전 호출 |
기본적인 사용방법을 바탕으로 유니티에서 사용해보면 다음과 같이 구성할 수 있다.
using System;
using UnityEngine;
public class ActionExample : MonoBehaviour
{
Action onGameStart; // 매개변수 없는 Action 선언
Action<int> onScoreChanged; // int 매개변수를 받는 Action 선언
private int score = 0;
void Start()
{
// 1. Action에 메서드 연결 (메서드 이름 전달)
onGameStart += GameStart;
onScoreChanged += UpdateScoreUI;
// 2. Action 실행
onGameStart?.Invoke(); // null 체크 후 호출 (이벤트 방식)
AddScore(10); // 점수 추가 함수 호출
}
void GameStart()
{
Debug.Log("게임이 시작되었습니다!");
}
void AddScore(int amount)
{
score += amount;
// 3. 점수 변경 Action 실행
onScoreChanged?.Invoke(score); // score 값을 전달하며 실행
}
void UpdateScoreUI(int currentScore)
{
Debug.Log($"현재 점수: {currentScore}");
}
}
Action onGameStart; : 인자도 없고 반환값도 없는 메서드를 등록할 수 있는 델리게이트이다.
반면 Action<int> onScoreChanged; 는 인자를 하나 받지만 반환값은 없는 메서드를 등록할 수 있는 델리게이트이다.
이후 Start()에서 델리게이트를 onGameStart += GameStart; 와 같이 메서드를 구독한다.
onGameStart?.Invoke(); 로 메서드를 호출한다. ?를 통해 null이 아닐 경우 호출한다.
유니티에서는 버튼 클릭 이벤트에 Action 델리게이트를 통해서 구현한다.
예를 들어,
public class UIButton : MonoBehaviour
{
public Action onClick;
public void OnClick()
{
onClick?.Invoke(); // 안전한 호출
}
}
//----------------------------------------------
public class UIManager : MonoBehaviour
{
[SerializeField] UIButton myButton;
void Start()
{
myButton.onClick = () => Debug.Log("버튼 클릭됨!");
}
}
onClick 델리게이트를 선언하고
이벤트 메소드에 델리게이트를 구독하여
안전한 호출이 가능하다.
또 다른 예시로는
public class Enemy : MonoBehaviour
{
public Action onDeath;
public void Die()
{
Debug.Log("적 사망");
onDeath?.Invoke(); // 등록된 모든 델리게이트 실행
}
}
//------------------------------------------------------------
public class GameManager : MonoBehaviour
{
[SerializeField] Enemy enemy;
void Start()
{
enemy.onDeath += OnEnemyKilled;
}
void OnEnemyKilled()
{
Debug.Log("적을 처치했습니다!");
}
}
public Action onDeath; 는 적이 죽었을 때 호출되는 델리게이트로
외부에서 +=연산으로 이벤트 리스너에 등록할 수 있는 델리게이트 필드이다.
onDeath?.Invoke(); 는 onDeath 에 등록된 모든 메서드를 호출한다.
null체크는 아무것도 등록되지 않았을 경우에 무시된다.
이 구조는 "죽었을 때 다른 애들이 뭔가 하게 만들고 싶다"는 목적을 위해 "옵저버 패턴"을 간단히 구현한 것과 유사하다.
음 롤에서 다리우스 궁극기로 적을 처치했을 때, 주변 미니언이 공포에 걸리는 이벤트에 이런 방식을 적용할 수 있을 것 같다.
GameManager에 Start()에서 이벤트에 메서드를 등록하고
OnEnemyKilled() 에서 실제 반응을 처리한다.