턴제 카드 게임을 만들다보니 많은 곳에서 이벤트를 사용합니다.
스킬 효과, 장비 효과, 유물 등과 같이 전투 내에서만 필요한 것들(전투 스테이지가 끝나면 -=로 해제되어야 하는)
(스킬은 전투 시작시 구독, 스킬 삭제 시 구독해제. 장비는 착용 시 구독, 착용 해제 시 구독 해제. 유물은 런동안 영구 지속, 이 외의 효과도 생명주기가 각각 다릅니다)
그리고 해금시스템과 같이 게임 플레이 내내 이벤트를 구독해야하는 것들(게임 시작 직후 한번만 +=하면 되는)
이렇게 2가지로 볼 수 있을 거 같습니다.
모든 이벤트들을 static Events 클래스에서 관리하고 있습니다.
public static class Events{ public static Action<DamageInfo> OnTakeDamage; //이런게 매우 많음! public static void Registr<T>(ref Action<T> evt, Action<T> fun, object caller) { evt += fun; #if UNITY_EDITOR Debug.Log($"[Events] Registr {evt.Method.Name} from {caller?.GetType().Name}"); #endif } public static void Trigger<T>(Action<T> evt, T args, object caller) { evt?.Invoke(args); #if UNITY_EDITOR Debug.Log($"[Events] Trigger {evt.Method.Name} from {caller?.GetType().Name}"); #endif } public static void UnSubscribe<T>(ref Action<T> evt, Action<T> fun, object caller) { evt -= fun; #if UNITY_EDITOR Debug.Log($"[Events] Trigger {evt.Method.Name} from {caller?.GetType().Name}"); #endif } } |
지금 제가 생각하는 고민은 전투 내에서 쓰이게 될 효과 컴포넌트들이 워낙 많은데 이 모든 효과들에 대해, 그리고 특정 타이밍(Dead, OnDisable, OnAble, Equip, UnEquip 등...)에,
하나하나
Events.Registr(Events.OnTakeDamage, GoodFunction, this);
Events.UnSubscribe(Events.OnTakeDamage, GoodFunction, this);
를 호출하는 것이 최선의 방법일까요?
뭔가 실수로 구독 해제 구문을 빼먹어서 누수가 생기거나 여러 전투 스테이지를 반복하면서 같은 이벤트가 여러번 구독되어 버리는 사고가 나지 않을까 하는 괜한 걱정이 들어서요.
나름 챗지피티한테 계속 대가리를 박아보고 생각했을때 몇가지 방법이 더 있었습니다.
1. 이런 류의 모든 객체를 IEventEffect로 묶고 매니저에서 전투가 시작하고 끝날때 IEventEffect를 전부 수집한다음 Registr와 UnSubscribe를 호출하면 된다는데 아직도 이게 최선일까? 하는 생각이 드네요.
2. 1번 방법에서 더 나아가 List<IEventEffect> runEvents; List<IEventEffect> battleEvents; 이런식으로 나눈 후 전투가 끝나면 battleEvents에 대해서만 UnSubscribe를 하는 겁니다.
3. 다음과 같습니다.
public static void Registr<T>(ref Action<T> evt, ref Action<T> function) { subs.Add(() => evt -= function); } public static void UnSubsAll(){ foreach(var sub in subs){ sub.Invoke(); } } |
이런 구조를 생각했었는데 챗지피티가 'ref 파라미터는 람다 또는 클로저 내에서 사용할 수 없습니다.'라고 불가능하다네요.
4. 그래서 생각한 방법이 리플렉션으로 string 값을 통해 이벤트를 찾아오는 건데 이것도 지피티씨가 비용이 비싸서 전투 중 수많은 효과에 쓰기엔 무리가 있을거라고 합니다.
그래서 계속 고민하다 고수님들께 묻는게 빠를 거 같아 글을 써봅니다.
앞서말한 실수가 안생길 수 있도록 설계 방식이 있을까요? 고수님들은 이벤트를 어떻게 관리하는지도 궁금합니다.
글 읽어주셔서 감사합니다.
도와주고싶은데 나도 비슷한 상황이라 못도와준다 ㅠ
방법 찾게 된다면 알려주세요 화이팅
idisposable 기반의 이벤트 구독 해제가 필요해보임 - dc App
이벤트를 구독했으면 나중에 구독 해제할 책임도 구독자한테 있어야 된다고 생각함 다른 데서 구독자 싹 날리다 보면 원치 않게 꼬일수도 있고 트레이드 오프라 봐야함
다들 의견 감사합니다
음...모든 콜백을 전부다 이벤트로 관리하는 이유가 있을까요?
이벤트 기반으로 하면 뭘 어떻게 만들어도 구독 구독해제 코드 제대로 써야 하는 압박은 피할 수 없음 이벤트 매니저 등에서 로그 같은거 잘 찍어서 매니저 자체가 리셋되어야하는 상황에서 구독해제된 이벤트 같은거 있나 경고같은걸로 확인하던가 하는게 최선임
나는 그냥 전수조사 했네. 유물이 있으면 List 이런데다 담아놓고, 캐릭터가 피해를 입으면 모든 artifact들에 OnDamaged 함수 호출하는 식으로
이벤트 기반의 프로그래밍은 버그도 많고 로직의 순서로 컨트롤하기 어려움 애초에 이벤트 자체가 인스턴스간 통신에 익숙하질않아서 이벤트를 우후죽순 선언해서 쓰는거같은데 이벤트를 최소화하고 스트림기반(R3, UniRX)이나 인터페이스(걍 씨샵 인터페이스) 기반의 통신이 좋음.