例えば「自機をナナメに移動させた際に、普通の縦/横移動のみの時よりも約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ドット * 三角比]の移動量になります。