ゲームのフラグ管理はビット演算で効率かつ簡単に管理する
どうも更新をサボりまくりの有末です。 本日はゲームなどのフラグ管理に使えるビット演算について書きたいと思います。
進数
日本人の100人にアンケートを取れば、おそらく9の次は10と答える人が大多数だと思います。 ただしこれは厳密には間違っていて9の次が10になるのは10進数で物事を考えているからです。
進数というのは簡単に言うと「いつ桁があがるか?」という事です。 例えば普段使用している10進数であれば0から数えて10個目(9)までは1桁で表せますが 11個目(10)からは2桁になります。
なかなかイメージがし難いと思うので以下2進数、8進数、10進数、16進数を見て行きます。 桁がいつ増えるか?に注目してください。
2進数 | 8進数 | 10進数 | 16進数 |
---|---|---|---|
0 | 0 | 0 | 0 |
1 | 1 | 1 | 1 |
10 | 2 | 2 | 2 |
11 | 3 | 3 | 3 |
100 | 4 | 4 | 4 |
101 | 5 | 5 | 5 |
110 | 6 | 6 | 6 |
111 | 7 | 7 | 7 |
1000 | 10 | 8 | 8 |
1001 | 11 | 9 | 9 |
1010 | 12 | 10 | A |
1011 | 13 | 11 | B |
1100 | 14 | 12 | C |
1101 | 15 | 13 | D |
1110 | 16 | 14 | E |
1111 | 17 | 15 | F |
10000 | 20 | 16 | 10 |
まぁ2進数は 0 と 1 だけで数字を表しているということだけわかっていただければ結構 です。
ビット演算
さて2進数の説明も終わったのでビット演算について説明したいと思います。
ビット演算とは2進数で表された数値(ビットパターン)を操作する演算のことです。 主にAND、OR、NOTという3つの演算から成り立ちます(XORとシフトはやや こしいのでこの記事では扱いません)。 それぞれどのような演算か見て行きましょう。
AND
AND演算は2つのビットパターンを取り、1つのビットパターンを返す演算子です。 各ビットにおいて双方が1の場合は1を、それ以外の場合は0を返します。 以下例です(1ビットの場合)。
1 AND 1 = 1 0 AND 1 = 0 1 AND 0 = 0 0 AND 0 = 0
またビット演算は各桁毎の演算になるのでビットパターンに対しては下記のように 働きます(筆算のように上下でANDを適用してください)。
1100 AND 1010 ---------- 1000
OR
OR演算も2つのビットパターンを取り、1つのビットパターンを返す演算子です。 各ビットにおいてどちらかが1の場合は1を、どちらも0の場合は0を返します。 以下例です(1ビットの場合)。
1 OR 1 = 1 0 OR 1 = 1 1 OR 0 = 1 0 OR 0 = 0
なお、ビットパターンに対しては下記のように働きます。
1100 OR 1010 ---------- 1110
NOT
NOT演算は1つのビットパターンを取り、1つのビットパターンを返す演算子です。 各ビットにおいて1の場合は0を、0の場合は1を返します。 以下例です(1ビットの場合)。
NOT 1 = 0 NOT 0 = 1
なお、ビットパターンに対しては下記のように働きます。
NOT 1010 ---------- 0101
ビット演算を使ったフラグ管理
数値をビットパターンで表すと、各桁が 0 か 1 で表されます。 したがって 0 を OFF、1 を ON と定義すると大量の状態(フラグ)を一つの数字として 管理することができます。
具体的に考えます。 RPGなどにおいて、キャラクターごとに「眠り」や「麻痺」などの状態が存在します。 これを(オブジェクト指向を使わずに)4人パーティーで普通に管理すると以下のような コードになるでしょう(Python的仮想言語)。
player1_sleep = false # 眠り player1_palarysis = false # 麻痺 player1_poison = false # 毒 player1_excitation = false # 興奮 player2_sleep = false player2_palarysis = false player2_poison = false player2_excitation = false player3_sleep = false player3_palarysis = false player3_poison = false player3_excitation = false player4_sleep = false player4_palarysis = false player4_poison = false player4_excitation = false
この例では状態異常が4つだけなので何とかなりますが、数が増えると面倒ですね。 そんな時はビットパターンを使って状態を管理することを考えます。 以下のようなイメージです。 0はその桁の状態異常は無いことを表し、1はその桁の状態異常であることを表します。
sleep palarysis poison excitation player1_state = 0 0 0 0
このようにビットパターンの各桁を状態として定義しておくと、以下のように非常に簡単に 状態を変更できます。
フラグを立てる(状態異常を追加)
フラグを立てるにはOR演算子を使います。 フラグを立てたい桁が 1 のビットパターンをORすることで ステータス変数の中の数値がどのような数値であっても確実に任意の桁を 1 にすること ができます。
# 状態を初期化 player1_state = 0000 # 毒状態にする # 0000 OR 0010 = 0010 player1_state = player1_state OR 0010 # 麻痺状態にする # 0010 OR 0100 = 0110 player1_state = player1_state OR 0100
フラグを下げる(状態異常を削除)
フラグを下げるにはANDとNOTを組み合わせます。
フラグを下げたい桁が 1 のビットパターンをNOTすることで
フラグを下げたい桁以外がすべて1のビットパターンにします。
その後ANDをかけるとフラグを下げたい桁は? AND 0 = 0
で確実に 0 になり、
他の桁はフラグが立っていた場合は1 AND 1 = 1
、フラグが立っていなかった場合は
0 AND 1 = 0
となるので確実に保存されます。
# 状態を初期化(麻痺・毒状態) player1_state = 0110 # 毒状態を解除 # 0110 AND (NOT 0010) = 0110 AND 1101 = 0100 player1_state = player1_state AND (NOT 0010) # 麻痺状態を解除 # 0100 AND (NOT 0100) = 0100 AND 1011 = 0000 player1_state = player1_state AND (NOT 0100)
フラグを判別する(状態異常か判別する)
フラグを判別するにはANDを使用します。
判別したい桁が 1 のビットパターンをANDするとフラグが立っていた場合は
1 AND 1 = 1
でフラグが立っていなかった場合は0 AND 1 = 0
となります。
また判別したい桁以外は 0 なので? AND 0 = 0
となるのでANDをかけた
ビットパターンと等しくなるかどうかで判別できます。
# 状態を初期化(麻痺・毒状態) player1_state = 0110 # 眠りか? # 0110 AND 1000 = 0000 sleep = player1_state AND 1000 if sleep == 1000: 眠り # 麻痺か? # 0110 AND 0100 = 0100 palarysis = player1_state AND 0100 if palarysis == 0100: 麻痺
このようにビット演算を使うことで簡単に大量のフラグを管理することができます。 またビット演算は単純な論理式で成り立つので処理も高速です。 RPGなどゲーム制作において知っていると便利な方法なのでぜひ頭の片隅においておいて ください。