[2026_01_21] 지휘관 스킬 전역 이벤트 시스템

2026. 1. 21. 21:02TIL

문제 상황

모바일 오토배틀 게임에서 지휘관 스킬이라는 특수한 버프가 필요했다.

요구사항:

  1. 지휘관 스킬 활성화 시 → 모든 플레이어 유닛의 기존 버프 제거
  2. 지휘관 스킬 활성화 시 → 모든 버퍼 유닛은 스킬 사용 중단
  3. 지휘관 스킬 비활성화 시 → 버퍼 유닛들 스킬 재개
  4. 각 유닛에게 지휘관 버프 이펙트 표시

핵심 문제: 지휘관 스킬 하나의 상태 변화가 필드 위의 모든 유닛에게 영향을 미쳐야 함

 

 

 이벤트 정의

csharp
// 지휘관 스킬 활성화 이벤트
public struct ActiveBuffSkillOnEvent
{
    public ActiveBuffSkillOnEvent(bool _) { }
}

// 지휘관 스킬 비활성화 이벤트
public struct ActiveBuffSkillOffEvent
{
    public ActiveBuffSkillOffEvent(bool _) { }
}

 

2. 유닛들이 이벤트 구독

 
csharp
public abstract class UnitBase : MonoBehaviour
{
    private GameObject commanderBuffEffect;
    
    public virtual void Init(...)
    {
        // ... 기존 초기화 ...
        
        // 플레이어 유닛만 지휘관 스킬 이벤트 구독
        if (Team == Enums.TeamType.Player)
        {
            StaticEventSystem.Subscribe<ActiveBuffSkillOnEvent>(OnCommanderSkillActivated);
            StaticEventSystem.Subscribe<ActiveBuffSkillOffEvent>(OnCommanderSkillDeactivated);
        }
    }

 

3. 지휘관 스킬에서 이벤트 발행

csharp
// 지휘관 스킬 활성화
public void ActivateCommanderSkill()
{
    // 이벤트 발행 - 모든 구독자에게 알림
    StaticEventSystem.Publish(new ActiveBuffSkillOnEvent(true));
    
    // 지휘관 버프 적용 (다른 곳에서 처리)
}

// 지휘관 스킬 비활성화
public void DeactivateCommanderSkill()
{
    // 이벤트 발행
    StaticEventSystem.Publish(new ActiveBuffSkillOffEvent(true));
}

 

동작 흐름

  1. 지휘관 스킬 ON → StaticEventSystem.Publish(new ActiveBuffSkillOnEvent(true))
  2. 모든 플레이어 유닛 (10+ 유닛)
    • OnCommanderSkillActivated() 호출
    • 기존 버프 전부 제거
    • 지휘관 버프 이펙트 생성 (각자)
  3. 모든 버퍼 유닛 (버퍼 3명)
    • isCommanderSkillActive = true
    • 스킬 사용 중단
    • 기존 적용 중인 버프 이펙트 정리
  4. 지휘관 스킬 OFF → StaticEventSystem.Publish(new ActiveBuffSkillOffEvent(true))
  5. 모든 플레이어 유닛
    • 지휘관 버프 이펙트 제거
  6. 모든 버퍼 유닛
    • isCommanderSkillActive = false
    • 스킬 사용 재개

핵심 패턴

1. Publish-Subscribe 패턴

  • 발행자(Publisher): 지휘관 스킬
  • 구독자(Subscriber): 모든 플레이어 유닛, 모든 버퍼 유닛
  • 느슨한 결합: 지휘관 스킬은 몇 명의 유닛이 있는지 몰라도 됨

2. 이벤트 구독 해제의 중요성

 
 
csharp
// Init에서 구독
StaticEventSystem.Subscribe<ActiveBuffSkillOnEvent>(OnCommanderSkillActivated);

// Die/Destroy에서 해제 (필수!)
StaticEventSystem.Unsubscribe<ActiveBuffSkillOnEvent>(OnCommanderSkillActivated);

해제하지 않으면:

  • 죽은 유닛이 계속 이벤트를 받음
  • 메모리 누수 발생
  • NullReferenceException 위험

3. 팀 필터링

 
 
csharp
if (Team == Enums.TeamType.Player)
{
    StaticEventSystem.Subscribe<...>(...);
}
  • 적 유닛은 지휘관 스킬의 영향을 받으면 안 되므로
  • 플레이어 팀만 구독

장점

  1. 확장성: 유닛이 100개든 1000개든 코드 변경 없음
  2. 유지보수: 지휘관 스킬 로직과 유닛 로직이 분리됨
  3. 명확한 의도: 이벤트 이름만 봐도 무슨 일이 일어나는지 알 수 있음
  4. 디버깅 용이: 이벤트 발행 시점에 로그 찍으면 전체 흐름 파악 가능

주의사항

  1. 구독 해제 필수: Init에서 구독했으면 Die/Destroy에서 반드시 해제
  2. 순환 참조 방지: 이벤트 핸들러 내에서 같은 이벤트를 발행하면 무한 루프
  3. 성능: 이벤트가 너무 자주 발생하면 오버헤드 발생 (이 경우는 ON/OFF 2회뿐이라 괜찮음)

배운 점

  • 전역 상태 변화가 필요할 때 이벤트 시스템이 효과적
  • 직접 참조(GetComponent, FindObjectsOfType)보다 이벤트가 더 깔끔
  • 이벤트 구독 해제를 습관화해야 함
  • 단순한 플래그(isCommanderSkillActive) 하나로 복잡한 동작 제어 가능