円運動の実現

シューティングにも円運動を!

例えば「自機をナナメに移動させた際に、普通の縦/横移動のみの時よりも約1.41421356倍速く進む」とか、「敵が7WAY弾を撃つ際にも以下のように左図とならず、右図になってしまう」というのは、個人的にアツくなかったりします。ここでの紹介は方向感覚をより自然に、より奇麗に見せるための手法です。固定小数点を使用した計算を前提としていますので、まだ読んでない方は予習して来てください。

#ref(): File not found: "clip_1.png" at page "Ex.01"

増分値のテーブル化

円運動の増分値を算出するには、三角比を避けて通れません。しかし三角関数の計算はとりわけ時間を要します。そこでまず、方向数を有限個(ここでは256個)に限定して、予め全方向の解データをテーブルで持ってします。方向値と移動方向の関係は次の通りです。

    192
     ↑
128 ← ○ → 000
     ↓
    064

256方向もあれば十分でしょうがPCゲームの場合、360方向では精度が甘いとのことで4096にしていることもあるそうです。このようにして最初に「テーブル定義」してしまえば、あとは配列データから値を引っ張り出すだけで解が求まるので、超高速になります。

テーブルの製作方法

さっそく「256を1ドットとみなす固定小数点方式」でテーブルを出力してみることにしましょう。

#include <stdio.h>
#include <math.h>

#define PI		(3.1415926536)


int main(int argc, char* argv[])
{
	FILE* fp;
	int   i;
	short val;
	char  str[80];

	// コサインテーブルを出力します
	fp = fopen("cosdata.txt","w");
	if(fp == NULL)
	{
		return -1;
	}

	fprintf(fp,"cos:\n");

	for(i=0; i<256; i++)
	{
		val = (short)(cos(PI * ((double)i / 128)) * 256);
		val = (short)(cos(((double)i / 128) * PI) * 256);

		sprintf(str, "\t,%5d\t\t/* no.%3d */\n", val, i);
		fprintf(fp, str);
	}
	fclose(fp);


	// サインテーブルを出力します
	fp = fopen("sindata.txt","w");
	if(fp == NULL)
	{
		return -1;
	}

	fprintf(fp,"sin:\n");

	for(i=0; i<256; i++)
	{
		val = (short)(sin(PI * ((double)i / 128)) * 256);
		val = (short)(sin(((double)i / 128) * PI) * 256);

		sprintf(str, "\t,%5d\t\t/* no.%3d */\n", val, i);
		fprintf(fp, str);
	}
	fclose(fp);


	return 0;
}

上記のcos()とsin()が、円運動のための移動増分を算出している部分です。実は!・・・自分自身、何故三角比で求まるのか、そのメカニズムの詳細はよく分かっていません(爆)。・・・すいません数学苦手なもので。まぁ、難しく考えるよりも、「こんな呪文で、あーら不思議!」・・・そういうものだと思っておいたほうが気楽で良いですよ。

cos:
	,  256		/* no.  0 */
	,  255		/* no.  1 */
	,  255		/* no.  2 */
(中略...)
	,  255		/* no.253 */
	,  255		/* no.254 */
	,  255		/* no.255 */
sin:
	,    0		/* no.  0 */
	,    6		/* no.  1 */
	,   12		/* no.  2 */
(中略...)
	,  -18		/* no.253 */
	,  -12		/* no.254 */
	,   -6		/* no.255 */

#ref(): File not found: "cosdata.txt" at page "Ex.01"

#ref(): File not found: "sindata.txt" at page "Ex.01"

増分値テーブルの利用法

上記の解答データをカットアンドペースト(と言ってもno.0の最初の「,」は削除)して、少々整形します。

const s16 CosTbl[] = {                                                     
        :
   (cosデータ)
        :
};
const s16 SinTbl[] = {
        :
   (sinデータ)
        :
};
int main()
{
	u16 x,y;
	u8  angle;

	// 初期処理

	for(;;)
	{
		// もろもろの処理

		// 円運動
		x += CosTbl[angle];
		y += SimTbl[angle];
		angle++;

		// 表示処理
	}

	return 0;
}

・・・とすれば、円運動ぽい動きになる気がしませんか?

メモリ節約

上記説明ではsin用、cos用でデータをそれぞれ用意していましたが、実はcos分だけでOKだったりします。なぜならば!

SinTbl[angle] = CosTbl[(angle + 192) & 0xff]

という式が成り立つためです。先ほどのデータを見比べてみて下さい。マクロを使ってみると簡単になります。

#define SQR_VX(a, b)    (((s32)CosTbl[(a)]              * (b)) >> 8)
#define SQR_VY(a, b)    (((s32)CosTbl[(192+(a)) & 0xff] * (b)) >> 8)
		// 円運動(変更案)
		x += SQR_VX(angle, speed);
		y += SQR_VY(angle, speed);
		angle++;

に置換えれば、見事CosTblのデータだけで同じ動作が可能になっています。ちなみに、SQR_VX(a, b)の b にはスピード値が入り、ここでも「256を1とみなす固定小数点」を使っています。したがって、例えばここに256を設定すれば、[1ドット * 三角比]の移動量になります。

出典元

履歴


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