[TIL] C# - EventHandler

MINO·2024년 7월 9일
post-thumbnail

2024-07-09


C# 에서의 Event

주로 GUI 프로그래밍 , 비동기 작업, 특정 조건이 충족되었을 때 실행되는 동작을 처리하는 데 사용한다.

Delegate 를 기반으로 하며, Event 를 구독하고 발생시키는 방식으로 동작한다.

구독자 패턴을 통해 여러 메서드를 한 번에 호출할 수도 있다.


예시

퀘스트를 완료했을 때,
보상을 주는 메서드와 퀘스트 UI 를 업데이트 하는 메서드를
구독하고 호출하는 예시이다.

public class Quest : MonoBehaviour
{
	public event Action onQuestCompleted;

    public void CompleteQuest()
    {
        Debug.Log("Quest completed");
        onQuestCompleted?.Invoke(); // 이벤트 호출
    }

    void Start()
    {
        onQuestCompleted += GiveReward;
        onQuestCompleted += UpdateQuestUI;
    }

    private void GiveReward() // 보상 지급 로직
    {
        Debug.Log("Player received reward.");
    }

    private void UpdateQuestUI() // 퀘스트 UI 업데이트 로직
    {
        Debug.Log("Quest UI updated.");
    }
}

Unity 에서 사용되는 UnityEvent

대표적인 UnityEvent 로는 OnClick 이 있다.

버튼을 클릭한 후, 놓으면 호출되는 OnClick 이벤트를 통해
등록한 메서드를 호출할 수 있다.


실습 문제

💡 [캐릭터 피격 기능 연결]
레이어가 데미지를 받을 때 여러 기능이 한번에 작동하도록 시스템을 만드려고 합니다.
상태 메시지가 출력되고, 피격 이펙트가 나오며, 피격 사운드가 재생되는 메서드가 구현되어 있는 상황입니다.

  • DisplayStatus : 데미지 상태를 출력하는 메서드입니다.
  • DisplayHitEffect : 피격 이펙트를 표시하는 메서드
  • PlayHitSound : 피격 사운드를 재생하는 메서드
    1) TakeDamage 함수 내에서 플레이어가 데미지를 받을 때 이벤트를 발생(Publish)시키는 코드를 작성합니다.
    2) DisplayAndSound 클래스의 생성자에서 이벤트 발생 시 호출될 메서드(DisplayStatus, DisplayHitEffect, PlayHitSound)를 연결합니다.
    ⚠️주의!!!
    조건 : 데미지를 받는 로직을 처리하는 TakeDamage 메서드 안에서 DisplayStatus, DisplayHitEffect, PlayHitSound를 직접 호출하지 않고 코드를 완성해봅시다.
using System;

namespace GameEventExample
{
    // 이벤트 데이터를 담는 EventArgs 파생 클래스
    public class PlayerDamagedEventArgs : EventArgs
    {
        public int Damage { get; set; }
    }

    // 플레이어 클래스
    public class Player
    {
        public int HP { get; private set; } = 100;

        // 플레이어가 데미지를 받을 때 발생하는 이벤트를 정의하세요.
        public event EventHandler<PlayerDamagedEventArgs> PlayerDamaged;

        // 플레이어가 데미지를 받는 메서드
        public void TakeDamage(int damage)
        {
            HP -= damage;

            PlayerDamagedEventArgs e = new PlayerDamagedEventArgs { Damage = damage };
            //
            	Q1. 이벤트가 등록되어 있는지 확인하고 이벤트를 발생
            //
        }
    }

    public class DisplayAndSound
    {
        public DisplayAndSound(Player player)
        {
            // 
            	Q2. 각각의 이벤트 핸들러를 등록하세요.
            //
        }

        // 데미지 상태를 출력하는 메서드
        void DisplayStatus(object sender, PlayerDamagedEventArgs e)
        {
            Player player = sender as Player;
            Console.WriteLine($"Player took {e.Damage} damage!");
            Console.WriteLine($"Remaining HP: {player.HP}");
        }

        // 피격 이펙트를 표시하는 메서드
        void DisplayHitEffect(object sender, PlayerDamagedEventArgs e)
        {
            Console.WriteLine("Displaying hit effect...");
        }

        // 피격 사운드를 재생하는 메서드
        void PlayHitSound(object sender, PlayerDamagedEventArgs e)
        {
            Console.WriteLine("Playing hit sound...");
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Player player = new Player();
            DisplayAndSound displayAndSound = new DisplayAndSound(player);

            // 플레이어가 데미지를 받아 이벤트를 발생
            player.TakeDamage(20);
            Console.WriteLine($"Player HP: {player.HP}");
        }
    }
}

답안

Q1

PlayerDamaged?.Invoke(this, e);

Q2 :

player.PlayerDamaged += DisplayStatus;
player.PlayerDamaged += DisplayHitEffect;
player.PlayerDamaged += PlayHitSound;

풀이

event 의 형식인 EventHandler 에 대해 먼저 알아보자.

EventHandler

문제에 사용된 event 는 EventHandler 이다.
System NameSpace 에서 지원하는 object 와 EventArgs 를 매개변수로 가지는
void 형식의 Delegate 이다.

  • object sender : 이벤트를 실행시키는 객체
  • EventArgs e : 전달할 정보

문제에서
object sender 는 Player 이므로 this,
EvnetArgs e 는 PlayerDamagedEventArgs e 이다.


EventHandler 구독

이벤트핸들러 구독과 취소 방법은 + , - 이다.

DisplayStatus, DisplayHitEffect, PlayHitSound 메서드를
PlayerDamager 이벤트핸들러에 구독하고 호출하여 사용할 수 있다.


TIL 마무리

유니티 에디터를 잘 다루는 것도 중요하지만, 이러한 이벤트와 델리게이트를 잘 활용하여

서로 관련이 없는 클래스들이 독립적으로 동작하게 하거나,

특정 이벤트가 발생할 때 호출되는 로직을 쉽게 재사용하는 방식도 알아둬야겠다.

profile
안녕하세요 게임 개발하는 MINO 입니다.

0개의 댓글