내일배움캠프 3주차 1일차 TIL - 델리게이트

백흰범·2024년 4월 29일
0

오늘 한 일

  • 스파르타 클럽 진도 나가기 (~ 4-3)
  • 팀프로젝트 계획 및 ERD 작성하기

저번 진도까지는 이미 파이썬을 경험한 바탕이 있기 때문에 빠르게 왔지만(3주차 숙제가 템플릿 없는 줄 알고 백지부터 시작하고 완성했다;), 4주차 강의부터는 여러 번의 이해 시도가 필요한 개념들이 있어서 한번 여기서 정리해보기로 했다.


델리게이트 (Delegate)

  • 델리게이트(Delegate)는 메서드를 참조하는 타입입니다.
    ^ 위와 같은 설명을 그냥 메서드의 리스트와 같은 개념으로 이해했다.
  • 이해를 위해 그려놓은 그림
    • MyDelegate dele = Method1; // 첫 메서드 할당 (할당되지 않은 delegate는 +=가 불가능하다.) 
      MyDelegate dele += Method2; // 이후로는 List.Add(Method2)와 비슷한 느낌이다.
      MyDelegate dele += Method3;

델리게이트를 이것저것 만져보면서 알게된 규칙

  • 초기 할당은 =로 메서드를 추가해주고 || 그 이후 메서드 추가는 += || 메서드 제거는 -= 로 이루어진다.

  • 메서드 추가

  • 메서드 제거

  • 호출 시 등록된 메서드부터 순차적으로 진행한다.

  • 출력 장면 (맨 오른쪽 밑에 실행창이 있다.)

  • 등록 순서를 바꾼다면 위와 같이 바뀐다.



event와 병합 사용하기

  • 강의에서 나온 내용대로 event와 함께 사용되는 모습을 볼 수 있다.
  • 해당 코드
// 델리게이트 선언
public delegate void EnemyAttackHandler(float damage);

// 적 클래스
public class Enemy
{
    // 공격 이벤트
    public event EnemyAttackHandler OnAttack;

    // 적의 공격 메서드
    public void Attack(float damage)
    {
        // 이벤트 호출
        OnAttack?.Invoke(damage);	// Invoke - 함수를 실행합니다.
				// null 조건부 연산자 - ? 
				// null 참조가 아닌 경우에만 멤버에 접근하거나 메서드를 호출
    }
}

// 플레이어 클래스
public class Player
{
    // 플레이어가 받은 데미지 처리 메서드
    public void HandleDamage(float damage)
    {
        // 플레이어의 체력 감소 등의 처리 로직
        Console.WriteLine("플레이어가 {0}의 데미지를 입었습니다.", damage);
    }
}

// 게임 실행
static void Main()
{
    // 적 객체 생성
    Enemy enemy = new Enemy();

    // 플레이어 객체 생성
    Player player = new Player();

    // 플레이어의 데미지 처리 메서드를 적의 공격 이벤트에 추가
    enemy.OnAttack += player.HandleDamage;

    // 적의 공격
    enemy.Attack(10.0f);
}
  • 솔직히 이거 처음보고 강의에서도 뭐라고 말하는지 전혀 이해가 안되서 애를 먹었다.


순차적으로 이해하는 과정

  1. 일단 델리게이트와 이벤트의 관계를 생각해보았다. 델리게이트는 메서드를 합쳐주는 기능이 있는데 그걸 이벤트가 할당 받았다.
public delegate void EnemyAttackHandler(float damage);
// ... (생략) ...
    // 공격 이벤트
    public event EnemyAttackHandler OnAttack; 

! 참고로 event에 델리게이트를 붙여주지않으면 안된다. 코파일럿의 답변


  • 그림과 같이 이해해보기 ( 대충 Event가 Delegate의 타입을 가져왔다. )


2. 그 다음에 Attack이라는 메서드에 이벤트 호출을 써놓았다.

    // 적의 공격 메서드
    public void Attack(float damage)
    {
        // 이벤트 호출
        OnAttack?.Invoke(damage);	// Invoke - 함수를 실행합니다.
				// null 조건부 연산자 - ?
				// null 참조가 아닌 경우에만 멤버에 접근하거나 메서드를 호출
    }
}
  • 그림과 같이 이해 (이번에는 Method와 event가 붙었다.)


3. 각각의 객체가 생성된 이후에 Event 타입의 이름으로 player.HandleDamage 메서드를 저장했다.

// 게임 실행
static void Main()
{
    // 적 객체 생성
    Enemy enemy = new Enemy();

    // 플레이어 객체 생성
    Player player = new Player();

    // 플레이어의 데미지 처리 메서드를 적의 공격 이벤트에 추가
    enemy.OnAttack += player.HandleDamage; // 그렇다면 알 수 있는 것은 event가 실행시킬 메서드를 저장하는 역할임을 알 수 있다!
}
  • 그림과 같이 이해


4. 그 이후에 Enemy클래스의 Method를 실행시켜보았다.

	enemy.Attack(10.0f);
  • 출력 결과
  • 그림과 같이 이해
  • 실행이 어떻게 흘러갔는가
      1. Attack 메서드가 10의 인자를 받고 그것을 OnAttack에 전달
      2. OnAttack 이벤트는 저장된 메서드를 호출하면서 damage(10)을 전달
      3. player는 damage(10)을 받고 입력된 WriteLine 코드 실행

? 혹시나 이 과정을 보고도 이해가 안된다면 직접 코드를 이리저리 만져보는 것을 추천한다.



이해가 됐다면 이러한 변형이 가능해진다.

  • 메서드에서 이벤트에게 값을 주기 전에 임의로 변동시켜서 줄 수 있고
          public void Attack(float damage)
          {
              damage = 20; // 몬스터의 공격력이 20이라는 가정을 할 때!
              // + 또는 추가시키고 싶은 로직을 추가시킬 수도 있다. (치명타 판정, 회피 판정 등등)
              // 이벤트 호출
              OnAttack?.Invoke(damage);
          }

  • 추가적인 메서드를 넣어서 공격을 전체에게 가하는 느낌도 낼 수 있다.
      // 플레이어 객체 생성
      Player player1 = new Player("플레이어1");
      Player player2 = new Player("플레이어2");
      Player player3 = new Player("플레이어3");
      Player player4 = new Player("플레이어4");

      // 플레이어의 데미지 처리 메서드를 적의 공격 이벤트에 추가
      enemy.OnAttack += player1.HandleDamage;
      enemy.OnAttack += player2.HandleDamage;
      enemy.OnAttack += player3.HandleDamage;
      enemy.OnAttack += player4.HandleDamage;
  • 회피율 기능을 추가한다면 밑과 같이 출력이 된다.

그리고 이해를 하고나니 남은 부분들도 빠르게 이해가 되었다.


  • 람다 - 델리게이트에 간단한 기능을 넣고자 할 때 사용됨.

// 표현식 람다 (한줄짜리) 
(해당 람다를 담을 수 있는 델리게이트) = x => x + x;
// 문장 람다 (다줄짜리) 
(해당 람다를 담을 수 있는 델리게이트) = (x) => { x + x; };

enemy.OnAttack += (float a) => Console.WriteLine("적이 공격을 시도합니다!"); // 매개변수가 동일하다면 집어넣을 수 있다.

델리게이트를 간단하게 만들 수 있는 템플릿

델리게이트는 미리 선언해줘야할 필요가 있는 반면 제네릭 델리게이트인 Func과 Action은 바로 선언해서 사용 가능하다.

  • Func - 값을 반환하는 메서드를 위한 델리게이트

Func<매개변수1, 매개변수2, ... 마지막 매개변수, 반환값> addFunc = method;
  • Action - 값을 반환하지 않는 메서드를 위한 델리게이트

Func<매개변수> addFunc = method;

활용법 (강의에 나온 예제 코드입니다.)

class GameCharacter
{
    private Action<float> healthChangedCallback;	// Action 선언

    private float health;

    public float Health
    {
        get { return health; }
        set
        {
            health = value;
            healthChangedCallback?.Invoke(health);	// health가 set(변할) 때마다 호출된다.
        }
    }

    public void SetHealthChangedCallback(Action<float> callback)	// Action을 외부로 가져갈 수가 없으니 메서드가 콜백함수들을 붙이는 걸 도와주자
    {
        healthChangedCallback = callback;
    }
}

// 게임 캐릭터 생성 및 상태 변경 감지
GameCharacter character = new GameCharacter();
character.SetHealthChangedCallback(health =>
{
    if (health <= 0)
    {
        Console.WriteLine("캐릭터 사망!");
    }
});	// 체력이 0 이하가 되면 텍스트를 출력하는 람다를 Action에 추가했다.



강의를 듣고 나서 느낀점

저번 주에는 진도 외의 것을 TIL로 작성을 하려할 때 해석에 너무 어려운 느낌이 들었다. 아무래도 지금 상황에서는 빠르게 진도를 나가서 기초부터 빠르게 다진 다음에 진도 이외의 공부를 해나가는것이 올바른 절차일 것같다.




내일 할 일

  • 스파르타 코딩클럽 강의 (~ 4주차 과제)
  • 이번주 팀 프로젝트 코드 만들어보기 ( 전투 기능 만들기 )
profile
게임 개발 꿈나무

0개의 댓글