三角関数のテーブル化

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
ft

三角関数のテーブル化

#1

投稿記事 by ft » 15年前

こんにちは。龍神録を参考にPSPにて弾幕シューを再現しようと考えております。
龍神録プログラミングの館でいうと第14章あたりなのですが、敵弾の描画が増えると処理落ちをしてしまいます。
以前質問をした際に皆様のご回答を参考に除算やfloatや画像の処理方法などを変えていき、多少良くなりました。
しかしどうやらshot.c内の以下の計算において三角関数がネックとなっていると思われ、弾が増えると落ちてしまいます。
(試しにsin、cosを1、0にして弾を真下へ撃たせた場合は結構な数が描画できたので、恐らく三角関数がネックとなっていると考えました)
void shot_calc(int n){
	int i,max=0;
	if(enemy[shot[n].num].flag!=1)//敵が倒されたら
		shot[n].flag=2;//それ以上ショットを登録しないフラグに変える
	for(i=0;i<SHOT_BULLET_MAX;i++){//n番目の弾幕データの弾を計算
		if(shot[n].bullet.flag>0){//その弾が登録されていたら
			shot[n].bullet.x+=cos(shot[n].bullet.angle)*shot[n].bullet.spd;
			shot[n].bullet.y+=sin(shot[n].bullet.angle)*shot[n].bullet.spd;
			shot[n].bullet.cnt++;
			if(shot[n].bullet.x<-60 || shot[n].bullet.x>FMX+60 ||
				shot[n].bullet[i].y<-60 || shot[n].bullet[i].y>FMY+60){//画面外に外れたら
				if(shot[n].bullet[i].till<shot[n].bullet[i].cnt)//最低消えない時間より長ければ
					shot[n].bullet[i].flag=0;//消す
			}
		}
	}
	//現在表示中の弾が一つでもあるかどうか調べる
	for(i=0;i<SHOT_BULLET_MAX;i++)
		if(shot[n].bullet[i].flag>0)
			return;
	//現在表示中の弾が一つもなければ
	if(enemy[shot[n].num].flag!=1){
		shot[n].flag=0;//終了
		enemy[shot[n].num].flag=0;
	}
}

そこで三角関数をテーブル化することで計算を高速化できると聞いたのですが、私プログラミング初心者でして、独自にファイルを追加して計算をさせる方法が思いつきませんでした。
具体的にどのようにすればいいのでしょうか?よろしくお願いします。
ファイル構成などは龍神録さんのサンプルとほぼ同じようになっているので、できればこのサンプルに倣った方法で教えてもらえるとありがたいです。

GPGA

Re:三角関数のテーブル化

#2

投稿記事 by GPGA » 15年前

static const int cosTable[/url] =
{
	 65536, 65534, 65531, 65524, 65516, 65505, 65491, 65475,
	 65457, 65436, 65412, 65386, 65358, 65327, 65294, 65258,
	 65220, 65179, 65136, 65091, 65043, 64992, 64939, 64884,
	 64826, 64766, 64703, 64638, 64571, 64501, 64428, 64353,
	 64276, 64197, 64115, 64030, 63943, 63854, 63762, 63668,
	 63571, 63473, 63371, 63268, 63162, 63053, 62942, 62829,
	 62714, 62596, 62475, 62353, 62228, 62100, 61971, 61839,
	 61705, 61568, 61429, 61288, 61144, 60998, 60850, 60700,
	 60547, 60392, 60235, 60075, 59913, 59749, 59583, 59414,
	 59243, 59070, 58895, 58718, 58538, 58356, 58172, 57986,
	 57797, 57606, 57414, 57219, 57022, 56822, 56621, 56417,
	 56212, 56004, 55794, 55582, 55368, 55152, 54933, 54713,
	 54491, 54266, 54040, 53811, 53581, 53348, 53114, 52877,
	 52639, 52398, 52155, 51911, 51665, 51416, 51166, 50914,
	 50660, 50403, 50146, 49886, 49624, 49360, 49095, 48828,
	 48558, 48288, 48015, 47740, 47464, 47186, 46906, 46624,
	 46340, 46055, 45768, 45480, 45189, 44897, 44603, 44308,
	 44011, 43712, 43412, 43110, 42806, 42501, 42194, 41885,
	 41575, 41263, 40950, 40636, 40319, 40002, 39682, 39362,
	 39039, 38716, 38390, 38064, 37736, 37406, 37075, 36743,
	 36409, 36074, 35738, 35400, 35061, 34721, 34379, 34036,
	 33692, 33346, 32999, 32651, 32302, 31952, 31600, 31247,
	 30893, 30538, 30181, 29824, 29465, 29105, 28745, 28383,
	 28020, 27656, 27291, 26925, 26557, 26189, 25820, 25450,
	 25079, 24707, 24334, 23960, 23586, 23210, 22833, 22456,
	 22078, 21699, 21319, 20938, 20557, 20175, 19792, 19408,
	 19024, 18638, 18253, 17866, 17479, 17091, 16702, 16313,
	 15923, 15533, 15142, 14751, 14359, 13966, 13573, 13179,
	 12785, 12390, 11995, 11600, 11204, 10807, 10410, 10013,
	  9616,  9218,  8819,  8421,  8022,  7623,  7223,  6823,
	  6423,  6023,  5622,  5222,  4821,  4420,  4018,  3617,
	  3215,  2814,  2412,  2010,  1608,  1206,   804,   402,
		 0,
};

/*!
 * @brief cosを求める
 */
int mycos(int num)
{
	num &= 0x3ff;
	if (num < 256)		return cosTable[num];
	if (num < 512)		return -cosTable[512-num];
	if (num < 768)		return -cosTable[num-512];
	if (num < 1024)	return cosTable[1024-num];

	return 0;
}

/*!
 * @brief sinを求める
 */
int mysin(int num)
{
	return -mycos(num - 256);
}

一周を1024、返す値は-65536~65536にした三角関数テーブルです。
上記のプログラムに適応させると

const int angle = shot[n].bullet.angle * 1024 / 6.283185307179586476925286766559; // angleをラジアンから1周1024の値に変換
shot[n].bullet.x += (__int64)mycos(angle) * shot[n].bullet.spd >> 16; // 通常のcosは-1~1が返ってくるのに対して
                                                                            // mycosは-65536~65536が返ってくるため
                                                                            // 最後に65536で除算する( >> 16 の処理、シフト演算子のほうが÷より処理速度が速い)

となります。
 

ft

Re:三角関数のテーブル化

#3

投稿記事 by ft » 15年前

GPGAさん有難うございます。

static const int cosTable[/url] =

}
>一周を1024、返す値は-65536~65536にした三角関数テーブルです。
の直前までの部分をvoid shot_calc(int n)の直前に書き、そして
void shot_calc(int n){
        int i=0;
		const int angle = shot[n].bullet.angle * 1024 / 6.283185307179586476925286766559;
        if(enemy[shot[n].num].flag!=1)//敵が倒されたら
                shot[n].flag=2;//それ以上ショットを登録しないフラグに変える
        for(i=0;i<SHOT_BULLET_MAX;i++){//n番目の弾幕データの弾を計算
                if(shot[n].bullet.flag>0){//その弾が登録されていたら
			shot[n].bullet.x += (float)mycos(angle) * shot[n].bullet.spd/65536;
			shot[n].bullet.x += (float)mysin(angle) * shot[n].bullet.spd/65536;
                        shot[n].bullet.cnt++;
                        if((shot[n].bullet.x<-50 || shot[n].bullet.x>FIELD_MAX_X+3 ||
                                shot[n].bullet.y<-50 || shot[n].bullet[i].y>FIELD_MAX_Y+3 )&&shot[n].bullet[i].till<shot[n].bullet[i].cnt){//画面から外れたら
                                                   shot[n].bullet[i].flag=0;//消す
						}
                }
        }
        //現在表示中の弾が一つでもあるかどうか調べる
        for(i=0;i<SHOT_BULLET_MAX;i++)
                if(shot[n].bullet[i].flag>0)
                        return;
        if(enemy[shot[n].num].flag!=1){
                shot[n].flag=0;//終了
                enemy[shot[n].num].flag=0;
        }
}

(__int64)やシフト演算はうまくビルドしてくれないため取りあえず除いてしまいました。
このようにしてみたところ、敵弾が全部真横にしか飛ばない状態になりました。
どこを間違えてしまったのでしょうか?

yu

Re:三角関数のテーブル化

#4

投稿記事 by yu » 15年前

shot[n].bullet.x += (float)mycos(angle) * shot[n].bullet.spd/65536;
shot[n].bullet.x += (float)mysin(angle) * shot[n].bullet.spd/65536;


ではなくて

shot[n].bullet.x += (float)mycos(angle) * shot[n].bullet.spd/65536;
shot[n].bullet.y += (float)mysin(angle) * shot[n].bullet.spd/65536;


ではないですか?

ft

Re:三角関数のテーブル化

#5

投稿記事 by ft » 15年前

ご指摘ありがとうございます。
すみません、ケアレスミスでした;

直したところ、自機を狙うようになったのですが、後の弾も初弾が狙いをつけた場所に撃つようになってしまいました。
登録している弾幕は常に自機狙いのはずなのですが、他の箇所がおかしいのかもしれないので少し見直してみます。

GPGA

Re:三角関数のテーブル化

#6

投稿記事 by GPGA » 15年前

>(__int64)やシフト演算はうまくビルドしてくれないため取りあえず除いてしまいました。
bcc使用ですか。
shot[n].bullet.x += (float)((long long)mycos(angle) * (long long)shot[n].bullet.spd >> 16);
に変更してみてください。

>後の弾も初弾が狙いをつけた場所に撃つようになってしまいました。
const int angle = shot[n].bullet.angle * 1024 / 6.283185307179586476925286766559;
の位置がおかしい

たかぎ

Re:三角関数のテーブル化

#7

投稿記事 by たかぎ » 15年前

> bcc使用ですか。

PSPとのことなので、GCCでは?
それなら、long long型を使うのが妥当です。
最近はVisual C++でもBorland C++ Compilerでも(5.5.1はダメですが)long longを使えるので、__int64を使う必要性はほとんどありません。

GPGA

Re:三角関数のテーブル化

#8

投稿記事 by GPGA » 15年前

>PSPとのことなので、GCCでは?
すっかり見落としていました。

PSPでやるなら、以前にも回答した通り、全て固定小数で行うか
FPU命令を使用しないとまともな速度が出ないと思うのですがねぇ。

ft

Re:三角関数のテーブル化

#9

投稿記事 by ft » 15年前

GPGAさんありがとうございます。
まずbccがよく分からないのでこれに対する物か分かりませんが、
使用しているのはVisual C++ 2008 Express EditionとPSPSDKとDXLibraryPortableです。
次に、
>shot[n].bullet.x += (float)((long long)mycos(angle) * (long long)shot[n].bullet.spd >> 16);
のようにしてみたところエラーは消えました。また、
>const int angle = shot[n].bullet.angle * 1024 / 6.283185307179586476925286766559;
の位置が結局よくわからなかったため、
//n番目のショットを登録した敵と自機との角度を返す
float shotatan2(int n){
        return atan2(ch.y-enemy[shot[n].num].y,ch.x-enemy[shot[n].num].x)* 1024 / 6.283185307179586476925286766559;
}

のようにshotatan2に直接書きました。
以上のようにしてみたのですが、弾は左下方向に直進したりする状態となりました。


そこでshotatan2をそのままに、
shot[n].bullet.x += (float)((long long)mycos(angle) * (long long)shot[n].bullet.spd >> 16);
の部分を
shot[n].bullet.x +=mycos(shot[n].bullet.angle) * shot[n].bullet.spd /65536;
のように戻してみたところ自機を狙うようになり、所望の動作が得られました。(sinは方向が逆転していたので-をつけました)
ただ偶然この動作になったてくれただけで、また特にこのようにした理由もなく、計算処理も勝手に変えてしまっている箇所があるので良いとは思えません。

GPGA

Re:三角関数のテーブル化

#10

投稿記事 by GPGA » 15年前

とりあえず、atan2も内部でsinやcos使ってたと思うので結構重いです。
そっちもテーブル化してしまうほうが処理速度は稼げます。

static const int atanTable[/url] =
{
		0,   40,   81,  122,  162,  203,  244,  285,
	  325,  366,  407,  447,  488,  529,  569,  610,
	  651,  691,  732,  772,  813,  853,  894,  934,
	  974, 1015, 1055, 1096, 1136, 1176, 1216, 1256,
	 1297, 1337, 1377, 1417, 1457, 1497, 1537, 1576,
	 1616, 1656, 1696, 1735, 1775, 1814, 1854, 1893,
	 1933, 1972, 2011, 2051, 2090, 2129, 2168, 2207,
	 2246, 2285, 2323, 2362, 2401, 2439, 2478, 2516,
	 2555, 2593, 2631, 2669, 2708, 2746, 2784, 2821,
	 2859, 2897, 2935, 2972, 3010, 3047, 3084, 3122,
	 3159, 3196, 3233, 3270, 3307, 3343, 3380, 3416,
	 3453, 3489, 3526, 3562, 3598, 3634, 3670, 3706,
	 3742, 3777, 3813, 3848, 3884, 3919, 3954, 3989,
	 4024, 4059, 4094, 4129, 4164, 4198, 4233, 4267,
	 4301, 4335, 4369, 4403, 4437, 4471, 4505, 4538,
	 4572, 4605, 4638, 4671, 4704, 4737, 4770, 4803,
	 4836, 4868, 4901, 4933, 4965, 4997, 5029, 5061,
	 5093, 5125, 5156, 5188, 5219, 5251, 5282, 5313,
	 5344, 5375, 5406, 5436, 5467, 5497, 5528, 5558,
	 5588, 5618, 5648, 5678, 5708, 5738, 5767, 5797,
	 5826, 5855, 5884, 5913, 5942, 5971, 6000, 6028,
	 6057, 6085, 6114, 6142, 6170, 6198, 6226, 6254,
	 6282, 6309, 6337, 6364, 6391, 6419, 6446, 6473,
	 6500, 6527, 6553, 6580, 6606, 6633, 6659, 6685,
	 6711, 6737, 6763, 6789, 6815, 6841, 6866, 6892,
	 6917, 6942, 6967, 6992, 7017, 7042, 7067, 7092,
	 7116, 7141, 7165, 7190, 7214, 7238, 7262, 7286,
	 7310, 7333, 7357, 7381, 7404, 7428, 7451, 7474,
	 7497, 7520, 7543, 7566, 7589, 7611, 7634, 7657,
	 7679, 7701, 7724, 7746, 7768, 7790, 7812, 7833,
	 7855, 7877, 7898, 7920, 7941, 7963, 7984, 8005,
	 8026, 8047, 8068, 8089, 8109, 8130, 8151, 8171,
};
int atan2_(int x, int y) {
	if (x == 0) return 0;
	return atanTable[(int)(((long long)y << 8) / x)];
}
int atan1_(int x, int y) {
	if (x == y) return (1 << 13);
	if (y  > x) return (1 << 14) - atan2_(y, x);
	return atan2_(x, y);
}
int atan0_(int x, int y) {
	if (x < 0) return atan1_(y, -x) + (1 << 14);
	return atan1_(x, y);
}


/*!
 * @brief atanを求める
 */
int myatan(int x, int y)
{
	if (x == 0 & y == 0) {
		return 0;
	}
	if (y < 0) {
		return (atan0_(-x, -y) + (1 << 15)) >> 6;
	}

	return atan0_(x, y) >> 6;
}

int shotatan2(int n){
	const int x = ch.x - enemy[shot[n].num].x;
	const int y = ch.y - enemy[shot[n].num].y;
	return myatan(x, y);
}


>shot[n].bullet.x += (float)((long long)mycos(angle) * (long long)shot[n].bullet.spd >> 16); 
>の部分を 
>shot[n].bullet.x +=mycos(shot[n].bullet.angle) * shot[n].bullet.spd /65536; 
>のように戻してみたところ自機を狙うようになり、所望の動作が得られました。
shot[n].bullet.spdがfloatだからですね。shot[n].bullet.xもfloatだし。
浮動小数を使うとビット演算子は使用できないので、個人的には座標もスピードも固定小数推奨ですが
作りが多少面倒になるし、/ 65536で処理速度として問題なければ、今のままでもいいと思います。
 

ft

Re:三角関数のテーブル化

#11

投稿記事 by ft » 15年前

>>GPGAさん
わざわざ助かります。
個人的に一通り問題点の原因と改善法が纏まってきたので、
ご助言くださいました固定小数による計算も含めて組み直してみたいと思います。
本当にありがとうございました!

閉鎖

“C言語何でも質問掲示板” へ戻る