#author("2023-04-17T22:21:39+09:00","","")
* 固定小数点と構造体ワークエリア [#q2e864fe]
アクション系ゲームのキャラクタ移動など、1ドット単位よりも細かな座標指定を擬似的に扱うにはどうすればよいのでしょうか。ストレートに考えれば、小数点を変数に持たせることが必要になると思います。そこでfloat型を使用して・・・となりますが、ちょっと待ってください。浮動小数点の取扱いはCPUの処理に時間が掛かり、整数計算の数十倍は時間が掛かるといわれています。実は整数型を使った良い方法があるのです。方法は単純で''256を1ドット分とみなす''ようにするだけです。この決まり事を事前にプログラム上の約束にすることで「座標 % 256」という結果の「0~255までの値」を小数として利用できます。
#freeze
#author("2023-05-30T00:33:45+09:00","","")
* 固定小数点 [#q2e864fe]
アクション系ゲームのキャラクタ移動など、1ドット単位よりも細かな座標指定を扱うにはどうすればよいのでしょうか。ストレートに考えれば、小数点を変数に持たせることが必要になると思います。そこでfloat型を使用して・・・となりますが、ちょっと待ってください。浮動小数点の取扱いはCPUの処理に時間が掛かり、整数計算の数十倍は時間が掛かるといわれています。実は整数型を使った良い方法があるのです。~

|10進数|0|RIGHT:1|RIGHT:2|RIGHT:3|RIGHT:4|RIGHT:5|RIGHT:6|RIGHT:7|RIGHT:8|RIGHT:9|RIGHT:10|RIGHT:11|RIGHT:12|RIGHT:13|RIGHT:14|RIGHT:15|RIGHT:16|...|RIGHT:254|RIGHT:255|RIGHT:256|
|16進数|0|0x1|0x2|0x3|0x4|0x5|0x6|0x7|0x8|0x9|0xA|0xB|0xC|0xD|0xE|0xF|0x10|...|0xFE|0xFF|0x100|
方法は単純で''16を1ドット分とみなす''ようにするだけです。この決まり事を事前にプログラム上の約束にすることで「座標 % 16」という結果の「0~15までの値」を小数として利用できます。16進数でいう4bit分、つまりs16だと下位4ビット=小数点、上位12ビット=実際の表示ピクセル座標という扱いになります。

内訳としては、
-上位8ビット = 実際の表示ピクセル座標~
-下位8ビット = 小数点~
という扱いになります。
|RIGHT:|RIGHT:|RIGHT:|RIGHT:|RIGHT:|RIGHT:|RIGHT:|RIGHT:|RIGHT:|RIGHT:|RIGHT:|RIGHT:|RIGHT:|RIGHT:|RIGHT:|RIGHT:|RIGHT:|RIGHT:|RIGHT:|RIGHT:|RIGHT:|c
|10進数|0|1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|COLOR(red):16|17|18|省略・・・|
|16進数|0x0|0x1|0x2|0x3|0x4|0x5|0x6|0x7|0x8|0x9|0xA|0xB|0xC|0xD|0xE|0xF|COLOR(red):0x10|0x11|0x12||
| 2進数|0|1|10|11|100|101|110|111|1000|1001|1010|1011|1100|1101|1110|1111|COLOR(red):10000|10001|10010||


 typedef struct {
 	u16 x;
 	u16 y;
 } ST_WORD;
 s16 x, y;
 
 typedef struct {
 	u8 xl;
 	u8 xh;
 	u8 yl;
 	u8 yh;
 } ST_BYTE;
 // 座標の代入
 x = ((GBAの横サイズ240 / 2) - (キャラサイズ / 2)) << 4;
 y = ((GBAの縦サイズ160 / 2) - (キャラサイズ / 2)) << 4;
 
 typedef union {
 	ST_WORD w;
 	ST_BYTE b;
 } ST_CHR_POS;

unionを使用していることで、たとえばST_WORD にx=16384、y=32768 が格納されていたとします。次にST_BYTE のxh, yh(上位ビット) を見ることで、表示座標の64、128を求めることが可能になります。

 	ST_CHR_POS chr;
 // 座標の加算
 x = x + 1;
 
 	chr.w.x = 16384;
 	chr.w.y = 32768;
 
 	DispChr(chr.b.xh, chr.b.yh);
 // 座標の表示
 SpriteDraw(x >> 4, y >> 4);


1/256精度もあれば、大抵は実用に耐えると思います。ところが精度は半分でもいいから、画面外処理のために移動範囲を余裕に持ちたい、ということなら「128を1とみなす」ようにしても良いでしょう。さらにST_WORDやunionを使用せずu16だけを使い、表示座標がほしいときだけ「座標>>7」に変換するだけでもかまわないと思います。
%は動作が重いため、シフト演算を使います。

 IWRAM_CODE u8 ChrGetTransPos(u16 src)
** サンプルプログラム [#t5219b78]
 int main(void)
 {
 	return src >> 7;
 	REG_WSCNT = 0x4317;
 
 	BgInit();
 	IrqInit();
 	KeyInit();
 
 	SprInit();
 	SpriteSetAlpha();
 
 
 	char strX[32], strY[32], strM[32];
 	u16 cnt;
 
 	s16 x = ((240 / 2) - (8 / 2)) << 4;
 	s16 y = ((160 / 2) - (8 / 2)) << 4;
 	s16 m = 25;
 
 	for(;;)
 	{
 	    VBlankIntrWait();
 
 		KeyExec();
 		cnt = KeyGetCnt();
 
 		if(cnt & KEY_UP   ) y-=m;
 		if(cnt & KEY_DOWN ) y+=m;
 		if(cnt & KEY_LEFT ) x-=m;
 		if(cnt & KEY_RIGHT) x+=m;
 		if(cnt & KEY_R    ) m+=1;
 		if(cnt & KEY_L    ) m-=1;
 
 		if((x>>4) <      0) x = 0;
 		if((x>>4) >= 240-8) x = (240-8) << 4;
 		if((y>>4) <      0) y = 0;
 		if((y>>4) >= 160-8) y = (160-8) << 4;
 
 		if(m <=  0) m =  1;
 		if(m >= 50) m = 50;
 
 		SpriteMoveAlpha(x>>4, y>>4);
 
 		_Sprintf(strX, "x:%3d %4d 0x%04x", x>>4, x, x);
 		_Sprintf(strY, "y:%3d %4d 0x%04x", y>>4, y, y);
 		_Sprintf(strM, "m:%3d", m);
 
 		BgAsciiDrawStr(0, 17, strX);
 		BgAsciiDrawStr(0, 18, strY);
 		BgAsciiDrawStr(0, 19, strM);
 	}
 }

** 動作画面 [#t9a62457]
#ref(1.png,nolink)

GBAならイチオシの使い方なので、よかったら使用してみてください。ただし最近のCPUなどでは、double型を直接使用したほうが有益とされています。何故有益かというと、整数と小数の速度の差異自体、もはや気にするレベルでは無いですし、こっちの方が正確だし、組んでていちいち256を意識する必要が無いからです。


** 出典元 [#u576450d]
** 出典元 [#pb4ac969]
- M-KAIさんの「Witchシューティングゲーム制作記帳」


** 履歴 [#k0edec0a]
** 履歴 [#j0abcb62]
- 2023/04/18
- 2008/06/30


トップ   一覧 検索 最終更新   ヘルプ   最終更新のRSS