ビット演算で処理内容を分岐する

アバター
せんちゃ
記事: 50
登録日時: 15年前
住所: 江別市東野幌町
連絡を取る:

ビット演算で処理内容を分岐する

投稿記事 by せんちゃ » 14年前

if文のなかに処理内容をダダっとベタ書きするのは読みづらくなるので、
できる限りそういったものはメッセージにしていきたいです。

例えばDAMAGEというメッセージがオブジェクトに送られたらダメージ処理を行う、
EFFECTというメッセージが送られたらエフェクト処理を行う、といった具合です。

そしてDAMAGE | EFFECTとして渡したらダメージとエフェクトを同時に処理します

そんな感じの処理を書いてみました。

CODE:

	for( int i = 0 ; i position.x;
			float* y = &enemy[i].Pos()->position.y;


			action[i].get()->update();
			animation[i].get()->update();

			if( enemy[i].Hp() update();   //ショット計算
		effect[i].get()->update(); //エフェクト計算

		/****************/
		/*メッセージ処理*/
		/****************/
		onMessageEvent( i );
	}
これはシューティングの敵の動き関係の処理となっています。
エネミーに何かしらのイベントがあった場合、
そのイベント情報をメッセージとして渡します。
onMessageEvent( )という関数でそのメッセージの処理を行います。

CODE:

void ArcadeEnemy::onMessageEvent( int i )
{
	/*
		メッセージを右シフトして0になるまで
		(オーダーがない地点に達するまで)処理を行う
	*/
	for( int bitShift = 0 ; ( enemy[i].Message() >> bitShift ) != 0 ; bitShift++ ){
		switch( enemy[i].Message() & ( 0x01 > bitShift ) != 0 ; bitShift++ ){
}
でメッセージの値が0ではない間、右シフトしてメッセージを探し続けます。
enemy[i].Message() & ( 0x01 << bitShift ) 
で、何番目のメッセージであるかを取得します。
0回目のループの場合は右の値が0x01になり、
1回目なら0x02、
そこから0x04、0x08、0x10。。。といった具合にシフトされていき、
メッセージと&演算して求めます。


仮に
EM_HIT          = 0x01 ,    0001
EM_DEATH        = 0x02 , 0010
EM_EFFECT_ORDER = 0x04 , 0100

として、オーダーする内容が EM_DEATH | EM_EFFECT_ORDERだとするなら
二進数では 0110 となります。

最初のループでは
0110
0001 (&
=======
0000

で評価されず、


次のループで0x01が1ビット左にシフトされ、
0110
0010 (&
=======
0010

となるので死亡処理が呼ばれます。

次のループで0x01が1ビット左にシフトされ、
0110
0100 (&
=======
0100

となり、エフェクトがオーダーされます。


という感じにするとより感覚的にプログラムが組み立てれるようになってきたかも
最後に編集したユーザー せんちゃ on 2011年10月08日(土) 22:04 [ 編集 1 回目 ]

アバター
へろりくしょん
記事: 92
登録日時: 15年前

Re: ビット演算で処理内容を分岐する

投稿記事 by へろりくしょん » 14年前

イベントに応じてメッセージを飛ばすという設計については何も言いませんが。
メッセージハンドラの中身が少々トリッキーな気がしますね。

ここは素直に、

CODE:

void ArcadeEnemy::onMessageEvent( int i )
{
	unsigned int msg = enemy[i].Message();

	if(msg & R.HIT){
	}

	if(msg & R.EM_DEATH){
	}

	if(msg & R.EM_EFFECT_ORDER){
	} 
}
と、if() 文を並べる方が、メンテナンス性・可読性よりは高いと思いますよ。

さらに言うと、変数 enemy はグローバル変数のようですが、このようにしてしまうと、誰が管理しているのか不明瞭です。
また、onMessageEvent() メンバ関数の引数に、要素数を与えるのも、ふとした拍子に異常値が紛れ込む可能性があり、よろしくありません。
こういう場合は、構造化を意識するという意味でも、インスタンスへのポインタを渡すのが常套手段です。

ついでに、オブジェクト指向を意識するなら、メッセージは1:1で対応するメンバ関数を用意し、必要に応じて呼び出すのがセオリーでは無いでしょうか。

アバター
せんちゃ
記事: 50
登録日時: 15年前
住所: 江別市東野幌町
連絡を取る:

Re: ビット演算で処理内容を分岐する

投稿記事 by せんちゃ » 14年前

へろりさん
>>メッセージは1:1で対応するメンバ関数を用意し、必要に応じて呼び出すのがセオリーでは無いでしょうか。
ごもっともだと思います。
今回はちょっとこういったビット演算を使ったメッセージ処理をしてみたかったのでこういう感じになってしまいました。
(ちょっとしたビット演算ネタみたいなものですね^^;)

引数に要素数を入れていたのは確かに変な値が入らないように考慮するべきですね...

変数enemyに関しては一応ArcadeEnemyというクラスのメンバという扱いです。
このクラス内で一通りのEnemyの処理を統括するような仕組みになっています。

ISLe
記事: 2650
登録日時: 15年前

Re: ビット演算で処理内容を分岐する

投稿記事 by ISLe » 14年前

ArcadeEnemy::onMessageEvent関数のforループの終了条件は未定義の動作を含んでますよ。
言語規格上、例えば符号無し32ビット整数で、右シフト可能なのは31ビットまでです。
最上位桁の有効ビットを追い出して0にするということはできません。
できたとしてもたまたま運が良かっただけということになります。

今回は鼻から悪魔が召喚されることは無かったようですがお気を付け下さい。
最後に編集したユーザー ISLe on 2011年10月09日(日) 17:27 [ 編集 1 回目 ]