Inspector 뷰에서 공개된 enum의 값을 여러 개 선택 가능하게 하기

 

게임이나 프로그램을 제작할 때, 유닛이나 캐릭터, 객체의 상태를 표현하기 위한 방법 중 하나로 enum을 많이 사용한다. 정수나 문자열을 이용해서도 객체의 상태를 나타낼 수 있지만, 정수의 경우는 이 상태가 어떤 상태인지 직관적으로 알아보기 어려운 문제가 있고, 문자열의 경우에는 어떤 상태인지 알아보기 쉽지만, 정수형에 비해서 비교 연산 속도가 느리고, 비교하는 부분에서 오타가 발생할 경우 찾기 어려운 버그를 발생시킬 수도 있다.

 

public class Character : MonoBehaviour
{
    int state;
    // 주석이 없으면 캐릭터가 어떤 상태인지 알기 어렵다.
    public void CheckState()
    {
        switch (state)
        {
            case 0: // Jump
                break;

            case 1: // Attack
                break;

            case 2: // Move
                break;

            default:
                break;
        }
    }
}

비직관적인 정수형 상태 표현

 

public class Character : MonoBehaviour
{
    string state;

    // 정수형보다 비교가 느릴 수 밖에 없다.
    public void CheckState()
    {
        switch (state)
        {
            case "Jump": // Jump
                break;

            case "Attcak": // Attack 오타가 발생하면 버그가 발생하고 찾기 어려울 수 있다.
                break;

            case "Move": // Move
                break;

            default:
                break;
        }
    }
}

보기는 편하지만 느리고 오타에 의한 버그 발생 가능성이 높은 문자열 상태 표현

 

 

 

 

 

그에 비해서 enum 타입의 변수를 이용하면 정수를 사용한 것과 같은 비교 연산 속도와 문자열을 사용한 것과 같은 직관성을 가질 수 있다. 오타가 발생하면 그 즉시 신텍스 에러가 뜨는 것은 덤이다.

 

public enum ECharacterState
{
    Jump,
    Attack,
    Move,
}

public class Character : MonoBehaviour
{
    public ECharacterState state;
    // 알아보기 쉬우며 오타가 발생할 가능성을 차단해준다.
    public void CheckState()
    {
        switch (state)
        {
            case ECharacterState.Jump: // Jump
                break;

            case ECharacterState.Attack: // Attack
                break;

            case ECharacterState.Move: // Move
                break;

            default:
                break;
        }
    }
}

 

객체의 상태를 표시하는데 enum 타입을 사용하면 이렇게 여러가지 이점이 있다. 그리고 이런 enum 타입의 변수는 public이나 [SerializeField]로 설정하면 유니티 에디터의 Inspector 뷰에서 볼 수 있게 된다.

 

 

일반적인 경우라면 이것으로 충분하겠지만, 캐릭터의 상태의 경우에는 기획에 따라 다르겠지만, 여러 가지의 상태가 중첩되는 경우도 있을 수 있다. 예를 들자면, 점프하면서 공격한다는 상태도 있을 수 있고 점프하면서 이동한다는 상태도 있을 수 있다. 그 외의 경우 역시 가능하다. 중첩된 상태를 표현하기 위해서는 각 비트에 상태를 매핑해서 비트 연산을 하는 방법을 주로 사용한다. 즉, 각 상태의 값을 2의 n승으로 하여 해당 비트의 값이 1이면 그 캐릭터는 해당 상태인 것이다.

 

public enum ECharacterState
{
    Jump    = 0x00000001,   // 0001
    Attack  = 0x00000002,   // 0010
    Move    = 0x00000004,   // 0100
}

public class Character : MonoBehaviour
{
    public ECharacterState state;

    private void Start()
    {

        // or 연산을 통해 캐릭터의 state는 이동 중이면서 점프 중인 상태가 된다.

        state = ECharacterState.Move | ECharacterState.Jump;
    }

    public void CheckState()
    {

        // State에 Attack 상태를 and 연산하면 Attack 상태에 속하는 비트의 값만 검출할 수 있다.

        if ((state & ECharacterState.Attack) == ECharacterState.Attack)
        {
            // Attack 상태 처리
        }

        if ((state & ECharacterState.Jump) == ECharacterState.Jump)
        {
            // Jump 상태 처리
        }

        if ((state & ECharacterState.Move) == ECharacterState.Move)
        {
            // Move 상태 처리
        }
    }
}

 

 

 

 

이렇게 중첩이 가능한 상태의 경우에는 여러 개의 상태를 동시에 선택할 수 있어야 하는데 public이나 [SerializeField]로 공개된 enum 타입의 변수의 경우에 유니티 에디터에서 기본적으로는 하나의 값만을 선택할 수 있게 되어있다. 이런 기본적인 상태에서 중첩된 상태를 한 번에 선택하기 위해서는 단순한 방법으로는 중첩상태의 열거형을 일일이 만들어 주는 것이 있다.

 

public enum ECharacterState
{
    Jump                  = 0x00000001,   // 0001
    JumpAndAttack   = 0x00000003,   // 0011
    Attack                = 0x00000002,   // 0010
    AttackAndJump   = 0x00000006,   // 0110
    Move                  = 0x00000004,   // 0100
    MoveAndJump     = 0x00000005,   // 0101
}

 

 

이렇게 중첩된 상태를 일일이 열거형에 추가해서 선택가능하게 만드는 방법이 있지만, 이것은 상태가 지금처럼 3가지 밖에 없는 간단한 상황에서만 가능한 방법일 것이다. 상태가 한 가지 한 가지 추가될 때마다 추가해야되는 중간 값이 엄청나게 많아져서 결국에는 드롭다운의 길이가 끝없이 늘어나는 것이 뻔히 예측될 것이다. 그리고 그렇게 중첩된 상태에 대한 상태가 늘어나면 어떤 상태를 넣었는지 확인하기 힘들어져서 이상하게 입력하게 될 수도 있다. 바로 위의 예시에서 보듯이 Jump And Attack과 Attack And Jump를 넣어버리게 되는 것처럼 말이다.(그리고 Attack And Jump의 값은 또 Attack And Move의 값이 입력되었다.)

 

이런 상황을 막기 위해서는 Inspector에서 enum 타입의 드롭다운에서 여러 개의 값을 선택 가능하게 만들어줘야 한다. 이런 방식의 예시로는 Inspector 뷰에 공개된 Light 컴포넌트의 Culling Mask 변수가 있다.

 

Light 컴포넌트의 Culling Mask는 빛을 받을 레이어를 골라서 선택할 수 있게 해준다.

 

 

 

 

위의 Culling Mask처럼 Inspector 뷰에서 원하는 상태를 여러 가지를 동시에 선택할 수 있다면 열거형에 끝없이 중첩된 상태를 추가할 필요는 없어질 것이다. 이러한 기능을 넣기 위해서는 에디터 기능을 약간 수정할 필요가 있다. 다음과 같이 EnumFlagsAttributeDrawer.cs와 EnumFlagsAttribute.cs 코드를 추가해보자. EnumFlagsAttributeDrawer의 경우, 에디터의 기능을 수정하는 코드기 때문에 Editor 폴더에 넣어주어야 한다.

 

 

using UnityEngine;
using UnityEditor;

[CustomPropertyDrawer(typeof(EnumFlagsAttribute))]
public class EnumFlagsAttributeDrawer : PropertyDrawer
{
    public override void OnGUI(Rect _position, SerializedProperty _property, GUIContent _label)
    {
        _property.intValue = EditorGUI.MaskField(_position, _label, _property.intValue, _property.enumNames);
    }
}

EnumFlagsAttributeDrawer.cs

 

using UnityEngine;

public class EnumFlagsAttribute : PropertyAttribute
{
    public EnumFlagsAttribute() { }
}

EnumFlagsAttribute.cs

 

public class Character : MonoBehaviour
{
    [EnumFlags]
    public ECharacterState state;
}

 

 

 

 

 

그리고 난 이후에 Inspector 뷰에서 여러 개의 값을 선택하고자 하는 enum 타입의 변수의 경우에 새롭게 추가해준 [EnumFlags] 어트리뷰트를 추가해주면아래의 이미지와 같이 우리가 추가한 enum 타입의 변수 역시 여러 개의 값을 선택할 수 있게 된다.

 

 

이렇게 여러 개의 값을 선택할 수 있게 사용하는 경우 Nothing은 0이고 Everything은 -1이다. 열거형에 값을 추가할 때는 각 열거형이 2의 n승이 되게 값을 정해줘야만 한다. 중간에 그 외의 값이 있으면 Inspector 뷰에서 선택한 값과 스크립트 상에서의 값이 일치하지 않는 버그가 발생할 수 있다.

 

public enum ECharacterState
{
    Jump    = 0x00000001,   // 0001
    Attack  = 0x00000002,   // 0010
    Move    = 0x00000004,   // 0100
}

EnumFlags를 사용해서 여러 값을 선택할 수 있게 하려면 중첩 상태를 제외하고 기본 상태만 2의 n승 값을 가지게 정의해줘야 한다.

 

 


참고 :

 

Default Editor : Enum as flags ? - Unity Answers

 

answers.unity.com

 

 

[유니티 어필리에이트 프로그램]

아래의 링크를 통해 에셋을 구매하시거나 유니티를 구독하시면 수익의 일부가 베르에게 수수료로 지급되어 채널의 운영에 도움이 됩니다.

 

에셋스토어

여러분의 작업에 필요한 베스트 에셋을 찾아보세요. 유니티 에셋스토어가 2D, 3D 모델, SDK, 템플릿, 툴 등 여러분의 콘텐츠 제작에 날개를 달아줄 다양한 에셋을 제공합니다.

assetstore.unity.com

 

Easy 2D, 3D, VR, & AR software for cross-platform development of games and mobile apps. - Unity Store

Have a 2D, 3D, VR, or AR project that needs cross-platform functionality? We can help. Take a look at the easy-to-use Unity Plus real-time dev platform!

store.unity.com

 

Create 2D & 3D Experiences With Unity's Game Engine | Unity Pro - Unity Store

Unity Pro software is a real-time 3D platform for teams who want to design cross-platform, 2D, 3D, VR, AR & mobile experiences with a full suite of advanced tools.

store.unity.com

[투네이션]

 

-

 

toon.at

[Patreon]

 

WER's GAME DEVELOP CHANNEL님이 Game making class videos 창작 중 | Patreon

WER's GAME DEVELOP CHANNEL의 후원자가 되어보세요. 아티스트와 크리에이터를 위한 세계 최대의 멤버십 플랫폼에서 멤버십 전용 콘텐츠와 체험을 즐길 수 있습니다.

www.patreon.com

[디스코드 채널]

 

Join the 베르의 게임 개발 채널 Discord Server!

Check out the 베르의 게임 개발 채널 community on Discord - hang out with 399 other members and enjoy free voice and text chat.

discord.com

 

반응형
  1. NaiveLight 2018.05.14 07:22

    좋은 글 잘 읽었습니다~
    그런데 중간에 enum으로 중복된 state를 표현하는 예에서 AttackAndJump가 아닌 AttackAndMove일 것 같네요.

    • wergia 2018.05.14 10:32 신고

      네 새벽시간에 글을 썼더니 약간 헷갈린것 같네요. ㅎㅎ

  2. Cargold 2018.05.17 16:58

    우와아아아앙!! 제가 딱 필요했던 정보네요!! 감사합니다!! ㄲㅑ핫!

    • Cargold 2020.06.16 16:51 신고

      뭔가 본 거 같은 글이다~ 싶었더니 제 덧글이 있었군요 ㅋㅋ
      2년이 지나니 가물가물해서 재검색했네요 ㅠㅠ

    • wergia 2020.06.22 23:44 신고

      ㅎㅎ 한 번 해봤던 것도 자주 해보지 않으면 까먹을 때가 있죠.

+ Recent posts