Menu
CoddyTech

Unity에서 C# 이벤트

Coddy Unity 여정의 Unity 게임 아키텍처 섹션에 포함된 레슨. 50개 중 10번째.

점수가 바뀌면 여러 요소가 이에 반응합니다. 점수 텍스트, 사운드, 업적 등이 그렇습니다. ScoreKeeper가 각각을 호출하려면 모든 요소에 대한 필드가 필요하고, 새로운 기능을 추가할 때마다 ScoreKeeper를 수정해야 합니다. 이벤트는 이를 반대로 바꿉니다. ScoreKeeper가 변경을 알리고, 이에 반응하는 요소가 구독합니다. C#에서 event와 Action을 배웠습니다. Unity에서는 게시자와 리스너가 컴포넌트이며, 보통 서로 다른 GameObject에 있습니다:

using System;
using UnityEngine;

public class ScoreKeeper : MonoBehaviour
{
    public event Action<int> OnScoreChanged;
    private int score;

    public void AddPoints(int points)
    {
        score += points;
        OnScoreChanged?.Invoke(score);
    }
}
// ScoreLabel.cs, HUD의 텍스트에 부착
public ScoreKeeper keeper;
public TMP_Text label;

void Start()
{
    keeper.OnScoreChanged += ShowScore;
}

void ShowScore(int score)
{
    label.text = "Score: " + score;
}

Action<int>는 각 리스너가 int 하나를 받는 메서드라는 뜻이며, 여기서는 새 점수를 받습니다. Invoke는 구독한 메서드를 구독한 순서대로 모두 호출합니다. 리스너가 없으면 이벤트는 null이므로, 단순히 OnScoreChanged(score)라고 하면 NullReferenceException이 발생하지만 ?.는 호출을 건너뜁니다. event 키워드는 다른 스크립트에서 +=와 -=만 사용할 수 있도록 제한합니다. 따라서 ScoreLabel에서 keeper.OnScoreChanged(5)나 keeper.OnScoreChanged = null은 컴파일되지 않습니다.

ScoreKeeper에는 레이블을 위한 필드가 없고, 레이블의 존재도 모릅니다. 콤보 카운터나 사운드 플레이어도 같은 방식으로 구독할 수 있으며 ScoreKeeper는 그대로 유지됩니다. HUD가 없는 테스트 씬도 오류 없이 실행됩니다. 리스너는 여기처럼 Inspector 참조를 통해 게시자를 찾거나, GetComponent 또는 싱글톤처럼 알고 있는 다른 방법을 사용할 수 있습니다. 오브젝트가 파괴되기 시작하면 -=로 리스너를 제거하는 것이 중요하며, 이는 다음 강의에서 다룹니다.

challenge icon

챌린지

쉬움

파이터의 콤보는 Space를 누를 때마다 증가하고, 타격이 없으면 comboWindow(1)초 후 0으로 돌아갑니다. 현재 Fighter.cs에서 콤보 텍스트와 불꽃 크기를 직접 설정하므로, 각각에 대한 필드가 필요합니다. 이벤트를 사용하도록 바꿔 보세요:

  • Fighter.cs에서 콤보(int)를 전달하는 OnComboChanged라는 public 이벤트를 선언하세요. 콤보가 변경될 때마다 새 콤보 값을 전달해 이벤트를 호출하세요. 0으로 초기화될 때도 포함하며, 이벤트를 구독하는 대상이 없어도 타격 시 오류가 발생하지 않도록 하세요.
  • Fighter의 comboText 및 flame 필드와 이를 사용하는 줄을 삭제하세요. Fighter는 HUD에 대해 아무것도 알지 못합니다.
  • ComboText.cs와 ComboFlame.cs에서 Start() 안에 각 클래스의 Show 및 Grow 메서드를 파이터의 이벤트에 구독하세요. 두 클래스 모두 씬에서 연결된 fighter 필드가 있습니다.

Play를 누르고, Space를 몇 번 눌러 샌드백을 친 다음, 1초 기다렸다가 Check를 누르세요.

직접 해보기

using UnityEngine;
using UnityEngine.InputSystem;
using TMPro;

public class Fighter : MonoBehaviour
{
    public float comboWindow = 1f;
    public TMP_Text comboText;
    public Transform flame;
    private int combo;
    private float sinceHit;

    void Update()
    {
        if (Keyboard.current.spaceKey.wasPressedThisFrame)
        {
            combo++;
            sinceHit = 0f;
            comboText.text = "Combo x" + combo;
            flame.localScale = Vector3.one * (0.5f + 0.25f * combo);
        }
        else if (combo > 0)
        {
            sinceHit += Time.deltaTime;
            if (sinceHit > comboWindow)
            {
                combo = 0;
                comboText.text = "";
                flame.localScale = Vector3.one * 0.5f;
            }
        }
    }
}
quiz icon실력 점검

이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.

Unity 게임 아키텍처의 모든 레슨