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