수준별 학습 세션 - Event

Amberjack·2024년 1월 30일
0

Unity

목록 보기
22/44
post-custom-banner

🤔 event?

event : delegate의 한 종류

delegate에 기반, 옵저버 패턴과 유사하게 활용할 수 있다.

선언 방식

델리게이트와 비슷하다. 델리게이트의 경우, 델리게이트를 선언하고, 델리게이트 변수를 사용한다.
delegate 반환형 델리게이트이름(매개변수);

델리게이트이름 변수이름;

event의 경우, 델리게이트 변수 앞에 event 만 추가하면 된다.
event 델리게이트이름 변수이름;

차이점?

event와 delegate의 사용법은 비슷하나 활용에서 조금 다르다.

delegate의 경우, 함수에 대한 참조 그 자체를 말한다.

🖥️ event 사용 방법

ex) 플레이어가 전투를 하다 패배를 한다고 가정해보자.

Player → Battle! → 전투 패배 시

게임 오버 시 게임 오버창을 띄우거나 플레이어 입력을 멈추거나 몬스터 생성을 막는다던가 여러가지 처리를 해야 한다.

이런 상황에서 옵저버 패턴을 사용한다.

옵저버 패턴

옵저버가 관찰하고 있다가, 관찰하고 있는 대상자의 상태가 변화할 때 구독자들에게 통지를 하고, 구독자들이 알림을 받아 조치를 취하는 형태.

Player Input의 예.

우리가 작업한 프로젝트에서 플레이어의 입력을 받아드릴 때 Input System을 사용했다.

OnMove, OnAttack과 같은 event를 등록하고, 이러한 입력을 받아야하는 함수들을 event에 구독,
이후 event가 실행되면 구독한 함수 모두 실행하는 방식이다.

Input System에서의 이벤트 호출

InputSystem을 사용할 때, 키보드나 마우스의 입력을 Input Action에서 받아 event를 실행시켜준다.

이후, Input Action에서 Behavior의 Send Messages들을 통해 이벤트를 호출해준다.
Behavior의 밑에 설명에 OnMove, OnLook, OnFire를 가진 Object들에게 메세지를 보내준다고 적혀있는 것을 확인할 수 있다.

public class PlayerInputController : CharacterController
{
    private Camera _camera;
    [SerializeField] Vector2 minCameraBoundary; //최소 이동 수치
    [SerializeField] Vector2 maxCameraBoundary; // 최대 이동 수치

    protected override void Awake() 
    {
        base.Awake(); 
        _camera = Camera.main; //메인 카메라 연결
    }

    protected override void FixedUpdate()//부모 클래스에서 virtual로 선언했기에 override하여 FixedUpdate 선언, 지속적으로 위치값을 메인카메라와 연결
    {
        Vector3 targetPos = new Vector3(transform.position.x, transform.position.y, -10);//연결되어 있는 gameobject의 위치값 인식

        targetPos.x = Mathf.Clamp(targetPos.x, minCameraBoundary.x, maxCameraBoundary.x); //mathf.clamp는 최소,최대 비교후 사이값이면 그대로 출력
        targetPos.y = Mathf.Clamp(targetPos.y, minCameraBoundary.y, maxCameraBoundary.y);

        _camera.transform.position = new Vector3(targetPos.x, targetPos.y, -10); //카메라 위치값 적용
    }

    public void OnMove(InputValue value)
    {
        //Debug.Log("OnMove" + value.ToString());
        Vector2 moveInput = value.Get<Vector2>().normalized;
        CallMoveEvent(moveInput);
    }

    public void OnLook(InputValue value)
    {
        Vector2 newAim = value.Get<Vector2>();
        Vector2 worldPos = _camera.ScreenToWorldPoint(newAim);
        newAim = (worldPos - (Vector2)transform.position).normalized;

        if (newAim.magnitude >= .9f)
        {
            CallLookEvent(newAim);
        }
    }

    public void OnFire(InputValue value)
    {
        IsAttacking = value.isPressed;
    }
}

플레이어의 입력을 처리하는 PlayerInputController에서 이벤트를 등록해놓고, Input Action에서 event를 호출해줄 때, 해당 이벤트들이 실행되는 방식이다.

📌 event의 사용

event는 특정 상황을 체크하고 있다가 행동할 때 주로 사용한다.

delegate는 어떠한 요청을 했을 때 그 결과를 처리할 때 사용.
event는 어떠한 요청이 들어올 상황을 예약해두고, 상황 발생 시 필요한 기능들을 실행하게 함.

delegate는 외부에서 실행을 할 수 있지만, event는 외부에서 실행할 수 없다.

이벤트를 실행하는 주체에 따라 모아서 만들어 놓는 것이 좋다.
ex) 게임 로직에 관련된 이벤트들은 이벤트 매니저에서, 몬스터와 관련된 이벤트들은 몬스터매니저에서 등등...

📌 Action

C#에서 제공하는 내장 delegate.

public delegate void MoveDelegateFunc(Vector2 moveVector);

public event MoveDelegateFunc OnMoveEvent;

델리게이트를 선언하고 이벤트를 선언하는 것이 번거롭기 때문에 델리게이트 선언부분을 Action으로 선언하여 사용한다.

public event Action<Vector2> OnMoveEvent;

다만, 반환이 필요 없는 void일 경우에만 사용이 가능하다.

🤔 OnEvent?.Invoke()

event 실행 시

OnAttackEvent?.Invoke();

를 사용해서 이벤트를 호출해주는데, 이때 Invoke를 사용하는 이유는 Null을 체크하기 위해서이다.

if(OnAttackEvent != null)
	OnAttackEvent();

와 같은 내용이다.

Null을 체크하기 위해 ? 연산자를 사용하고, Nullable을 사용하기 위해 Invoke()를 사용한다. → OnAttackEvent?()라는 문법은 없기 때문에 Invoke를 연결해주어야 함.

post-custom-banner

0개의 댓글