bdfgdfg

비트 플래그와 비트 연산 본문

게임프로그래밍

비트 플래그와 비트 연산

marmelo12 2021. 9. 20. 13:52
반응형

비트 플래그

비트 플래그란 여러 옵션, 예로 들어 현재 캐릭터의 상태를 저장하는 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
Comments