85.내일배움캠프 76일차 TIL <Unity Unity 2D 팀프로젝트- MartialGod:Reborn - 26일차> 07/28

정광훈(Unity_9기)·2025년 7월 28일

TIL (Today I Learned)

목록 보기
86/110
post-thumbnail

[Flags] enum

[Flags] enum은 여러 개의 독립적인 상태를 하나의 정수 변수에 동시에 저장하고 관리할 수 있도록 설계되었습니다. 이는 비트 연산(Bitwise Operation)을 활용합니다.

내부 저장 방식:

각 enum 멤버는 고유한 비트 위치를 나타내는 정수 값(2의 거듭제곱: 1, 2, 4, 8, ...)을 가집니다. 1 << N 연산이 이를 보장합니다.

1 << 0 = 00000001 (1)

1 << 1 = 00000010 (2)

1 << 2 = 00000100 (4)

[Flags] enum 변수에 값을 할당한다는 것은, 내부적으로 이 변수가 여러 비트가 켜진(1) 상태를 나타내는 하나의 정수 값을 가지게 됨을 의미합니다.

0011이면 3이 아닌 0비트와 1비트가 켜졌다고 인식.
일반 enum이면 0001 + 0010 = 0011이지만 숫자 3으로 인식.


<코드 작동 순서>

using System;

/// <summary>
/// NPC의 대화 진행 상태를 나타내는 플래그 열거형입니다.
/// 여러 상태를 동시에 기록할 수 있습니다.
/// </summary>
[Flags]
public enum DialogueFlags
{
    None = 0, // 0000 - 아무 대화 안 했을 때
    HasSpokenFirstDialogue = 1 << 0, // 0001 - Npc와 첫 만남 대화를 진행 했는지
    HasSpokenTrollKill = 1 << 1, // 0010 - 트롤만 처치 후 첫 대화인지
    HasSpokenSpiderKill = 1 << 2, // 0100 - 거미만 처치 후 첫 대화인지
    HasSpokenCentipedeKill = 1 << 3, // 1000 - 지네만 처치 후 첫 대화인지
    HasSpokenTrollSpiderKill = 1 << 4, // 0001 0000 - 트롤, 거미 처치 후 첫 대화인지
    HasSpokenSpiderCentipedeKill = 1 << 5, // 0010 0000 - 거미, 지네 처치 후 첫 대화인지
    HasSpokenCentipedeTrollKill = 1 << 6, // 0100 0000 - 지네, 트롤 처치 후 첫 대화인지
}

<BossHintNpc.cs>

   private static DialogueFlags currentDialogueFlag = DialogueFlags.None; // 현재 대화상태 플래그, 변수 초기화 (0000)
   public static DialogueFlags CurrentDialogueFlag { get { return currentDialogueFlag; } }
    
   private IEnumerator HandleDialogueFlow() // 상황에 맞는 대사 출력
   {
       // 첫 상호작용 대화(한 번만)
       if (!HasDialogueFlag(DialogueFlags.HasSpokenFirstDialogue)) 
       {
           yield return StartCoroutine(Dialogues(firstDialogue));
           SetDialogueFlag(DialogueFlags.HasSpokenFirstDialogue);
       }

       // 하나의 보스만 처치했을 시(한 번만)
       if (isTrollDead && !isSpiderDead && !isCentipedeDead && !HasDialogueFlag(DialogueFlags.HasSpokenTrollKill)) // 트롤 죽인 후
       {
           yield return StartCoroutine(Dialogues(new string[] { bossKillDialogue[0] }));
           SetDialogueFlag(DialogueFlags.HasSpokenTrollKill);
       }
       else if (isSpiderDead && !isTrollDead && !isCentipedeDead && !HasDialogueFlag(DialogueFlags.HasSpokenSpiderKill)) // 거미를 죽인 후
       {
           yield return StartCoroutine(Dialogues(new string[] { bossKillDialogue[1] }));
           SetDialogueFlag(DialogueFlags.HasSpokenSpiderKill);
       }
       else if (isCentipedeDead && !isSpiderDead && !isTrollDead && !HasDialogueFlag(DialogueFlags.HasSpokenCentipedeKill)) // 지네를 죽인 후
       {
           yield return StartCoroutine(Dialogues(new string[] { bossKillDialogue[2] }));
           SetDialogueFlag(DialogueFlags.HasSpokenCentipedeKill);
       }
       
       if (aliveHintDialogues.Count > 0)
       {
           int randIndex = Random.Range(0, aliveHintDialogues.Count); // 랜덤 인덱스
           string[] selectedDialogue = aliveHintDialogues[randIndex]; // 해당 인덱스 보스대화 선택
           yield return StartCoroutine(Dialogues(selectedDialogue));
       }

       dialogueCoroutine = null;
       Player.Instance.playerController.OnInput();
    }
    
    private bool HasDialogueFlag(DialogueFlags flag)
    {
        return (currentDialogueFlag & flag) == flag;
    }

    // 특정 플래그로 설정하는 메서드
    private void SetDialogueFlag(DialogueFlags flag)
    {
        currentDialogueFlag |= flag;
    }

<BossHintNpc 최초 대화 조건>

  1. private static BossDialogueFlags _currentDialogueFlags = BossDialogueFlags.None; // 변수 초기화
    currentDialogueFlags는 (0000) 상태.

  2. 상호작용하여 대화가 시작되면 if (!HasDialogueFlag(BossDialogueFlags.HasSpokenFirstDialogue)) 조건 확인. (HasSpokenFirstDialogue은 0001)

  3. private bool HasDialogueFlag(BossDialogueFlags flag)
    {
    return (_currentDialogueFlags & flag) == flag;
    }

0000 & 0001 == 0001이 아닌 0000이기 때문에 false를 반환.

  1. 그럼 2번 조건이 if(!false)가 되기 때문에 true가 되어 조건문 내부 실행.
    대사가 출력이 끝나면
    SetDialogueFlag(BossDialogueFlags.HasSpokenFirstDialogue); 실행

  2. private void SetDialogueFlag(BossDialogueFlags flag)
    {
    _currentDialogueFlags |= flag;
    }

_currentDialogueFlags |= flag -> currentDialogueFlags = currentDialogueFlags | flag
_currentDialogueFlags = 0000 | 0001 -> _currentDialogueFlags는 0001이 됨.

그러고 랜덤으로 출력하는 대사를 출력하고 끝.

  1. 이후에 아무 보스를 처치 하고 왔을 때(트롤을 예시로 함)

// 하나의 보스만 처치했을 시(한 번만)
if (isTrollDead && !isSpiderDead && !isCentipedeDead && !HasDialogueFlag(BossDialogueFlags.HasSpokenTrollKill)) // 트롤 죽인 후
{
yield return StartCoroutine(Dialogues(new string[] { bossKillDialogue[0] }));
SetDialogueFlag(BossDialogueFlags.HasSpokenTrollKill);
}

if (!HasDialogueFlag(BossDialogueFlags.HasSpokenTrollKill)) -> HasSpokenTrollKill은 0010
이 조건만 따져보면

  1. private bool HasDialogueFlag(BossDialogueFlags flag)
    {
    return (_currentDialogueFlags & flag) == flag;
    }

(_currentDialogueFlags & flag) == flag -> 0001 & 0010 == 0010
원래 0001 & 0010 == 0000이 맞다.
식이 성립하지 않기 때문에 false 반환.

  1. if (!HasDialogueFlag(BossDialogueFlags.HasSpokenTrollKill))는 현재 if(!false)이기 때문에 내부 코드를 실행

  2. 조건문 마지막 코드인 SetDialogueFlag(BossDialogueFlags.HasSpokenTrollKill);

    private void SetDialogueFlag(BossDialogueFlags flag)
    {
        _currentDialogueFlags |= flag;
    }

_currentDialogueFlags = _currentDialogueFlags | flag -> _currentDialogueFlags = 0001 | 0010
_currentDialogueFlags = 0011이 된다.

  1. 이런 식으로 하면 현재 _currentDialogueFlags는 0011인 상태이다.

이후 다시 상호작용(대화)를 하면

if (!HasDialogueFlag(BossDialogueFlags.HasSpokenFirstDialogue))의 조건문에 따라

private bool HasDialogueFlag(BossDialogueFlags flag)
{
    return (_currentDialogueFlags & flag) == flag;
}

(_currentDialogueFlags & flag) == flag -> 0011 & 0001 == 0001이 성립하기 때문에 이 조건문은 넘어간다.

다음 조건인
if (!HasDialogueFlag(BossDialogueFlags.HasSpokenTrollKill))

(_currentDialogueFlags & flag) == flag -> 0011 & 0010 == 0010이 성립하기 때문에 이 조건문도 넘어간다.

이 두 조건은 넘어가지고 다른 조건문을 검사하기 시작한다.

이런 식으로 작동

0개의 댓글