bdfgdfg
비트 플래그와 비트 연산 본문
비트 플래그
비트 플래그란 여러 옵션, 예로 들어 현재 캐릭터의 상태를 저장하는 bool 변수들이 있다고 보자.
1. 캐릭터가 무기를 장착했는지 안 했는지
2. 캐릭터가 방어구를 장착했는지 안 했는지
3. 캐릭터가 전투 중인지 아닌지
... 등등 게임의 특징에 따라 매우 많은 옵션들이 달릴 수 있다.
하지만 이렇게 매번 캐릭터의 상태를 확인하기 위해서 bool변수를 선언해놓고 가져다 쓰는 것은 메모리 관리측면에서는 별로 좋지 못하다.
이때 사용할 수 있는 방법이 비트 플래그.
이름에서도 알 수 있듯이 비트를 통해 어떤 상태의 유무(0과 1)를 나타낸다.
unsigned int형의 경우는 32개의 상태를 나타낼 수 있는 것.
이제 캐릭터의 상태를 32개가 필요한 상황에서 bool변수 32개(32바이트)를 사용하는 것보다는
unsigned int 자료형(4바이트)의 비트의 개수를 이용하여 상태 정보를 저장한다면 bool변수를 따로 사용하는 거보다 메모리를 더 절약해서 사용할 수 있다는 것을 알게 된다.
이제 사용 예를 들어보자. (간단하게 하기위해 unsigned char형으로 사용한다)
#pragma once
enum class PLAYER_STATE
{
STATE_IDLE = 1,
STATE_RUNNING = 1 << 1,
STATE_INVINCIBLE = 1 << 2,
STATE_HIDING = 1 << 3,
STATE_STUN = 1 << 4,
STATE_ONDAMAGED = 1 << 5,
STATE_FLYING = 1 << 6,
STATE_DIE = 1 << 7
};
class Flag
{
public:
Flag() : m_flags(0) {}
bool Check(PLAYER_STATE state);
void Set(PLAYER_STATE state);
void Reset(PLAYER_STATE state);
private:
unsigned char m_flags;
};
class Player
{
Player() : m_playerState() {}
~Player() {}
private:
Flag m_playerState;
};
위와 같이. 플레이어의 상태를 정의한 enum class와 Flag정보를 들고있고 계산하기 위한 Flag클래스. 그리고 Player클래스이다.
아직 플래그 클래스에 비트 연산과 관련된(플레이어 상태처리)정보가 없으니 추가해보자.
우선 플레이어가 현재 어떤 상태인지 Check하는 함수.
class Flag
{
public:
Flag() : m_flags(0) {}
bool Check(PLAYER_STATE state)
{
return m_flags & static_cast<unsigned char>(state);
}
private:
unsigned char m_flags;
};
위 코드를 보면 m_flags라는 상태를 저장하는 변수에 AND(&)연산을 해주고 있다.
현재 플레이어의 상태가 00000001(idle상태)라고 보자. 만약 인자로 넘어오는 state정보가 idle이라면
00000001
&
00000001
= 1이 나오게 된다.
현재 m_flag의 상태가 여러개임에도 상관이없다.
00101001
&
00000001
------------
00000001 (1)-> 현재 플레이어의 상태중 IDLE비트가 켜져있다면 0이 아닌 값이 반환이 될테니.
그렇기에 다른 상태비트가 켜져있다 한들 내가 원하는 비트가 켜져있지 않다면
11111110
&
00000001
------------
00000000 (0) 0을 반환하여 FALSE가 된다.
Check는 매우 간단하다. 이번에는 Set함수. 비트에 상태를 추가하는(비트의 상태를 ON(1)하는) 함수를 보자.
class Flag
{
public:
void Set(PLAYER_STATE state)
{
m_flags |= static_cast<unsigned char>(state);
}
private:
unsigned char m_flags;
};
|는 OR연산을 의미한다. 근데 |연산자 뿐만이 아니라 |=도 붙여주고 있는 모습.
(당연히 OR연산을 한 후 다시 저장해야하므로)
이 연산도 매우 간단하면서 쉽다.
예로들어 현재 Player의 상태가 00000010(달리는 상태)라고 할 때 ONDAGED. 즉 피격상태를 추가하고싶다면
00000010
|
00100010 (STATE_ONDAMAGED)
-----------
00100010 가 된다.
이제 Reset함수를 본다.
Reset함수는 해당 비트를 ON->OFF하는 것.
예로들어 11011110 과 같은 비트에서 64자리만 0으로 만들려면 어떻게 할까.
어렵지 않다. 64자리만 0이고 나머지 비트는 1인 2진수 비트를 준비해 &연산을 하면 된다.
즉
11011110
&
10111111
-----------
10011110
(64자리만 0으로 바뀜)
원래의 비트에서 11111111을 &연산 해도 원래 자기자신의 비트가 반환이 된다.
이 특성을 이용해서 원하는 비트를 OFF시키면 된다.
다만 해당 위치 이외에 1인 수를 따로 준비해두는건 깔끔하지가 않기에 다음과 같이 변환 해주는 연산이 추가로 진행되어야 한다.
ex) 01000000 -> 10111111
이때는 xor연산(두 비트가 다를때만 1)을 사용하면 된다
ex) 01000000 xor 11111111 = 10111111
즉 내가 작성한 코드에서 이걸 반영한다면 m_flags &= (static_cast<unsigned char>(state) ^ 0xff))가 되겠다.
0xff는 16진수. 10진수로 255(1111/1111)
하지만 실제로는 더욱 간단하게 할 수 있는데 바로 ~연산자를 이용하여(비트반전)사용하면 된다.
즉 내가 00000001(idle)상태를 제거하고싶다면
m_flags &= ~(static_cast<unsigned char>(state))로 하면 끝이다.
~(static_cast(state))는 00000001 -> 11111110으로 바꿔주기에.
class Flag
{
public:
void Reset(PLAYER_STATE state)
{
m_flags &= ~(static_cast<unsigned char>(state));
}
private:
unsigned char m_flags;
};
여러 플래그를 조작하기
현재 무적상태인지 은신상태인지를 한번에 체크하고싶은데 Check함수를 두번호출하는게 귀찮다.
이럴 때는 두 상태를 or연산한 후 Check함수에 넘겨주면 된다.
ex)
00000001 ->Idle상태
|
00000100 -> 무적상태
-------------
00000101 -> 무적과 idle상태.
check함수에서 true를 반환한다면 무적과 idle상태가 같이 ON인상태.
'게임프로그래밍' 카테고리의 다른 글
델타타임-deltaTime (0) | 2021.09.22 |
---|---|
배달맨 게임코딩 (0) | 2021.09.18 |