Работа с флагами в играх

Опубликовал – 09.02.2011

Доброе время суток! Вот уже очень давно, зрела мысль в моей голове, написать на эту тему статейку… И, наконец, совпало свободное время с уже зрелой мыслью – получайте плод воображения автора, надеюсь старался не зря.

Как Вы поступаете, к примеру, если вам нужно хранить много разных флажков в Вашей игре? Создаете кучу bool переменных? Когда то у меня был некий объект, который хранил базовые флаги всех объектов в игре, это выглядело примерно так:

class CGameObject{
protected:
//..
	bool				visible;
	bool				run;
	bool				jump;
	bool				attack;
	bool				stop;
	bool				find;
	bool				damaged;
//..
};

Только флагов было куда больше. Это, конечно, наглядно, но затратно для памяти и не очень удобно, если надо изменить, к примеру, группу флагов, которые относятся к одному типу. Позже я расскажу о чем речь.
Сейчас же хочу рассказать о способе хранения флажков в обычной 32(64)-х разрядной переменной, используя простейшую булеву алгебру. Думаю, никому не надо объяснять основы самой алгебры. Я подготовил несколько функций для удобства работы с нашими флагами, встречайте:

Установка флагов

Функция setbit устанавливает нужный нам флаг в 32-х разрядной переменной (можно так же использовать и 64-х разрядные переменные)
в C++ это выглядит так:

inline void setbit(UINT *dst, UINT src)
{
	*dst |= src;
};

В Delphi для 64-х разрядных переменных так:

procedure setbit(var dst: int64; src: int64);
begin
   dst:= dst or src;
end;

Либо можно записать этот способ в С++ с использованием ассемблера (для новых компиляторов не принципиально, они генерируют код не хуже):

inline void setbit(_4byte *dst, _4byte src)
{
	__asm{
		mov		eax, dst
		mov		ebx, src
		mov		edx, dword ptr[eax]
		or		ebx, edx
		mov		dword ptr[eax], ebx
	};
};

Проверка флагов

Функция tstbit проверяет, установлен ли флаг в переменной. Вот вариант в С++:

inline bool tstbit(UINT dst, UINT src)
{
	return (dst & src) != 0;
};

Аналогично только для Delphi:

function tstbit(dst: int64; src: int64): Boolean;
begin
  result := (dst and src) <> 0;
end;

Ну и для извращенцев:

inline bool tstbit(_4byte dst, _4byte src)
{
	bool val = false;
	__asm{
		mov		eax, dst
		mov		ebx, src
		lea		edx, val
		and		eax, ebx
		cmp		eax, 0
		je		_ext
		mov		byte ptr [edx], 1
_ext:
	};
	return val;
};

Удаление флагов

Функция clrbit удаляет нужный флажок. Вариант в С++:

inline void clrbit(UINT *dst, UINT src)
{
	*dst&= ~src;
};

Соответственно в Delphi:

procedure clrbit(var dst: int64; src: int64);
begin
   dst:= dst and (not src);
end;

Ну и по традиции, на ассемблере:

inline void clrbit(_4byte *dst, _4byte src)
{
	__asm{
		mov		eax, dst
		mov		ebx, src
		not		ebx
		mov		edx, dword ptr[eax]
		and		ebx, edx
		mov		dword ptr[eax], ebx
	};
};

Пример использования

Тут Вас ограничивает только Ваша фантазия! Я покажу пару примеров. Например, в нашей стратегии есть несколько видов объектов – наземные, летающие, плавающие, которые так же разделяются на наших и не наших. :) Определим константы:

#define ID_GROUND		0x01000000 //типы объектов
#define ID_WATER		0x02000000
#define ID_FLYING		0x04000000
#define ID_MIDOBJCLASS	0x0F000000 //возможные типы объектов

#define ID_SHOT			0x10000000 //пули
#define ID_EXP			0x20000000 //взрывы

#define ID_PLAYER		0x00000001 //типы объектов
#define ID_FRIENDLY		0x00000002
#define ID_ENEMY		0x00000004
#define ID_NEUTRAL		0x00000008

При создании объекта, его ID складывается из этих констант нашими инструментами по работе с флагами. То есть мы создаем, например, дружественный корабль, в конструкторе собираем его ID:

//..конструктор объекта
 setbit(id, ID_WATER | ID_FRIENDLY);

Удобно? Еще бы! Теперь, например, нам нужно вытащить из списка все «живые» объекты, так как объектов в игре очень много, от выстрелов, до кораблей, то нам надо получить именно те самые наземные, воздушные и водяные. Кто помнит булеву алгебру, сразу поймет, что надо использовать маску. В этом нам поможет функция tstbit и сама маска, определенная в константах как #define ID_MIDOBJCLASS 0×0F000000:

if (tstbit(id, ID_MIDOBJCLASS)) {
  //нашли подходящий объект
}

Это как раз то, о чем я говорил в начале статьи, мы используем проверку сразу для группы объектов. С bool переменными было бы все куда сложнее.

Подведение итогов

Итак, мы научились рационально использовать ресурсы памяти, а так же работать с флажками в играх. Тут можно применить свою фантазию и использовать этот метод не только в играх. Вот, например, усовершенствованная функция setbit, которая устанавливает флаги но только когда было выполнено какое-либо условие:

inline void setbitbool(UINT *dst, UINT src, bool cond)
{
	if (cond) *dst |= src;
};

Применять, опять таки, легко, например, мы хотим объекту присвоить статус прыжка, если нажат пробел:

setbitbool(state, ST_JUMP, pressed[KEY_SPACE]);

Надеюсь статья была полезной, жду комментариев.

Рассказать друзьям:
  • Добавить ВКонтакте заметку об этой странице
  • Мой Мир
  • Facebook
  • Twitter
  • Яндекс.Закладки
  • В Живую Ленту Google
  • Сто закладок
Комментарии (6) - Работа с флагами в играх

Ответ

  1. 1101.debian:

    typedef struct{
    int a1:1;
    int a2:1;
    }flags;

    Thumb up Thumb down 0

  2. xrnd:

    А я бы разные типы объектов разделил наследованием. Странная программа, если пули и корабли отличаются только флагами ;)

    Код ассемблера можно улучшить
    В tstbit команда cmp не нужна, флаг нуля изменяет команда and.

    Thumb up Thumb down 0

    • Объекты разделены, но у всех один общий абстрактный класс.. который имеет например ID.. это позволяет засунуть все объекты в один список не прибегая к MAP’ап..
      Код ассемблера я ваял лет ‘цать назад :)

  3. Никитеныш:

    яву забыли господи

    Thumb up Thumb down 0

    • Александр Сергеевич:

      public class BitHelper {
      public static void setBit(Long dst, long src) {
      dst |= src;
      }

      public static void setBitBool(Long dst, long src, boolean condition) {
      if (condition) dst |= src;
      }

      public static boolean tstBit(long dst, long src) {
      return (dst & src) != 0;
      }

      public static void clrBit(Long dst, long src) {
      dst &= ~src;
      }
      }

      для Java

Ответить

Comments

Перед отправкой формы: