게임 개발 일지/내일배움캠프 TIL

Delegate를 이용한 CallBack 함수 만들기 / LINQ 사용법

빛하_ 2023. 11. 14. 15:45

 

 

 

C# 문법 복습

 

delegate 

 

 

// 델리게이트 선언
public delegate void EnemyAttackHandler(float damage);

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

    // 적의 공격 메서드
    public void Attack(float damage)
    {
        // 이벤트 호출
        OnAttack?.Invoke(damage);
		// 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);
}

 

event는 할당연산자( = )를 사용할 수 없으며, 클래스 외부에서는 직접 이벤트를 호출할 수 없다.
 

1) Enemy 라는 클래스에 OnAttack 이라는 delegate 할당

2) 해당 delegate를 호출하기 위해 invoke 함수 사용

3) 이 때 OnAttack?.Invoke(damage); 는 null 조건부 연산인데,

    OnAttack이 null일 경우를 포함하는 조건부 연산자이다. (null able이다.)

   OnAttack이 null이면 실행하지 않고, null이 아니라면 실행한다. (즉, OnAttack이 세팅되어 있는 경우에만 실행)

 

4) void Main 에서 enemy.OnAttack += player.HandleDamage;

이것은 OnAttack 이라는 delegate에 player의 void HandleDamage를 추가한 것.

 

5) 몬스터가 플레이어에게 데미지(10)을 주는 방식 (로직 순서)

몬스터가 공격 실행 - damage 10 이 Invoke에 전달 - damage 10이  OnAttack에 전달 - Player의 HandleDamage에 10 도착

- 플레이어가 {10}의 데미지를 입었습니다. 출력

 

즉, OnAttack이 이벤트를 담당하기 때문에 damage10이라는 정보를 모두에게 전달해줄 수 있음.

 

 

 

메서드를 저장하기 위한 두 가지 제네릭 형 delegate

 

1. Func

- 반환값이 존재하는 메서드를 나타내는 델리게이트.

ex) Func(int, string) 이라면 int (매개변수)를 입력으로 받아 string(반환값)을 반환하는 메서드를 나타낸다.

 

2. Action

- 반환값이 없음. 매개변수를 받아들이기만 한다.

ex) Action(int, string) 은 int와 string을 입력으로 받고 값을 변환하지 않는 매서드.

 

 

 

람다와 델리게이트 활용해 Callback 만들기

 

게임에 자주 사용되는 HP 매커니즘의 경우 HP값 변동에 대한 정보를 많은 변수들이 기다리고 있다.

이 때 Callback을 사용하면 동시다발적으로 변수들에게 HP에 변동이 있음을 알려줄 수 있다.

// 게임 캐릭터 클래스
class GameCharacter
{
    private Action<float> healthChangedCallback;

    private float health;

    public float Health
    {
        get { return health; }
        set
        {
            health = value;
            healthChangedCallback?.Invoke(health);
        }
    }

    public void SetHealthChangedCallback(Action<float> callback)
    {
        healthChangedCallback = callback;
    }
}

// 게임 캐릭터 생성 및 상태 변경 감지
GameCharacter character = new GameCharacter();
character.SetHealthChangedCallback(health =>
{
    if (health <= 0)
    {
        Console.WriteLine("캐릭터 사망!");
    }
});

// 캐릭터의 체력 변경
character.Health = 0;

 

 

 

LINQ를 사용하는 이유

 

LINQ 를 사용해 컬렉션, 배열, 데이터베이스 등의 자료 구조에 쿼리문을 던질 수 있다.

데이터에 대한 필터링, 정렬, 그룹화, 조인 등 작업을 수행한다.

 

 

LINQ 사용법

var result = from 변수 in 데이터소스
             [where 조건식]
             [orderby 정렬식 [, 정렬식...]]
             [select 식];

 

  • var 키워드는 결과 값의 자료형을 자동으로 추론
  • from 절에서는 데이터 소스를 지정
  • where 절은 선택적으로 사용하며, 조건식을 지정하여 데이터를 필터링
  • orderby 절은 선택적으로 사용하며, 정렬 방식을 지정
  • select 절은 선택적으로 사용하며, 조회할 데이터를 지정

 

 

 

nullable

 

null은 '아무 것도 없음'을 의미한다.

Nullable 형을 사용해 값형 변수에 null 값을 지정할 수 있게 함으로써 해당 변수가 null인지 아닌지 구분할 수 있다.

방법은 자료형 뒤에 ? 를 붙인다.

ex. int? , double?

 

null 병합 연산자

int nonNullableInt = nullableInt ?? 0;

Nullable 형식 뒤에 ??를 붙임으로써 nullableInt 가 null 일 때 0 이 되도록 지정할 수 있다.

 

 

 

StringBuilder
  • Append: 문자열을 뒤에 추가합니다.
  • Insert: 문자열을 지정한 위치에 삽입합니다.
  • Remove: 지정한 위치에서 문자열을 제거합니다.
  • Replace: 문자열의 일부를 다른 문자열로 대체합니다.
  • Clear: StringBuilder의 내용을 모두 지웁니다.

문자열을 "A" + "B" 형태로 코딩하는 경우

메모리에 저장할 값이 많아지기 때문에 코드가 무거워질 수 있다.

따라서 StringBuilder를 사용함으로써 메모리 할당을 줄일 수 있다.

 

StringBuilder 역시 클래스이기 때문에 

StringBuilder sb = new StringBuilder(); 이런 형태로 객체화 시켜야 사용 가능하다.

 

 

개인과제 [스파르타 던전] 피드백

 

1. Item 클래스의 Equip과 UnEquip 메서드의 중복되는 로직을 리팩토링하기

2. 사용자 입력 오류에 대한 예외처리

3. 메서드 주석 연습하기

4. 커밋 메시지 작성 연습하기

 

 

 

오늘의 회고

 

개인과제에 대한 튜터님의 피드백을 보고 부족한 점을 보완했다.

과제 해설 영상을 보고 배운 텍스트 색상 변환 함수를 사용해 한 줄로 텍스트에 색을 입힐 수 있었다.

 

상점 구현, 아이템 장착 개선(무기와 방어구 하나씩만 장착할 수 있도록) 까지 마쳤다.

중복되는 로직의 경우 리팩토링하고 싶었는데 건드리다보니 오류가 자주 발생해서 우선 되돌렸다.

리팩토링에 대한 아이디어는 계속 공부해야겠다.

 

내일부터 알고리즘 학습이 시작된다. 

힘내자!