三角関数のテーブル化

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

三角関数のテーブル化

#1

投稿記事 by gdu » 15年前

龍神録を元に色々弾幕等を増やしていくと
どうしても弾が多くなると処理落ちが発生してプレイしにくくなってしまいます。
そこで三角関数のテーブル化をしてみようとおもいましたが、

数学が基本的に苦手でして特に図形の角度や面積を求める問題等がかなり苦手なので
テーブル化についてググリましたが仕組みがよくわかりません。

どのように勉強、理解したらよいでしょうか?

あとよくテーブル化すれば速度が上がるという話を聞くのですがそれ以外で
具体的にシーティングにおいてどのようなメリット、デメリットがあるのでしょうか?

Dixq (管理人)

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

#2

投稿記事 by Dixq (管理人) » 15年前

PSP等で弾幕を計算するような特殊な場合でない限りテーブル化はあまり必要ではないと思いますよ。
ボトルネックはCPUではなくグラフィック部分である可能性が大きいので、描画まわりを工夫した方が良いと思います。
無駄な描画をしないとか、演算に時間のかかる減算合成はしないとか、広い範囲でアルファブレンドしないとか。

龍神録ではどの関数にどれ位時間がかかっているか表示できるようになっているので、
ボトルネックをそこから絞ってみるといいと思います。

勉強の為にという事であれば、ボトルネック云々関係なく一通り実装してみてもいいかもしれませんね。
int型のみで計算できる部分はintだけで計算する仕組みも考えると良いでしょう。

例えば座標って640やその辺ですから、42億まで数えられるintの範囲は全部使いません。
ですから、小数点の部分を上にシフトしてやって、実数をintで計算するように出来ると思います。

例えば

123.456

という値は x100 してやれば

123456

という値になりますね。計算し終わった時に、 /100 してやれば

123.456

に戻ります。

で、変数の値は何も実数で保持する必要はありません。シフトしたままで保持していてもいいわけです。
表示するときだけ実数にしてやればいいということですね。
計算に必要な変数を全てintで用意することで、計算中はintだけで計算するので早いということです。
(実数を計算する機構のあるPCなどでは恩恵はあまりないでしょうが)

シフトするときは、intの場合16ビットシフトしてやると高速に計算できるでしょう。

Dixq (管理人)

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

#3

投稿記事 by Dixq (管理人) » 15年前

そうもいかないことはありますね;
三平方の定理とかで困りそう・・。
時と場合によりますね;

naohiro19

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

#4

投稿記事 by naohiro19 » 15年前

>シフトするときは、intの場合16ビットシフトしてやると高速に計算できるでしょう。
固定小数点という考え方ですね。

gdu

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

#5

投稿記事 by gdu » 15年前

回答ありがとうございます。
テーブル化は携帯ゲーム機類でしかほとんど効果はないんですね、
よく分かりました。

グラフィック部分の処理を見直してみたところ縮小表示しているところや
二重に表示しているところなどが多数ありました。

それを修正、削除した結果完全とはいきませんが許容範囲のfpsが出ました。
ありがとうございました。

三平方の定理・・・習いましたがもう何がなんだか分からなかった覚えが・・w
一度どこから分からなくなったのか復習してみます。w

gdu

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

#6

投稿記事 by gdu » 15年前

すいません、解決忘れました、

Dixq (管理人)

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

#7

投稿記事 by Dixq (管理人) » 15年前

> 数学が基本的に苦手でして特に図形の角度や面積を求める問題等がかなり苦手なので
> テーブル化についてググリましたが仕組みがよくわかりません。

ということでしたので、一応テーブルが作れるプログラムを作ってみました。
といってもループして表示しているだけです。

NUMの数を変えれば好きな要素数のテーブルが出来ます。
#include <stdio.h>
#include <math.h>

#define PI 3.1415926535

#define NUM 100

int main(){
    int i;
    {//サイン
        printf("float TableSin[ %d ] = {\n\t", NUM);
        for( i=0; i<NUM; i++ ){
            printf("%10.7f", sin(PI*2/NUM*i));
            if( i != NUM-1 ){
                printf(",");
            }
            if( i%10 == 9 ){
                printf("\n\t");
            }
        }
        printf("\n};\n");
    }
    {//コサイン
        printf("float TableCos[ %d ] = {\n\t", NUM);
        for( i=0; i<NUM; i++ ){
            printf("%10.7f", cos(PI*2/NUM*i));
            if( i != NUM-1 ){
                printf(",");
            }
            if( i%10 == 9 ){
                printf("\n\t");
            }
        }
        printf("\n};\n");
    }
    {//タンジェント
        printf("float TableTan[ %d ] = {\n\t", NUM);
        for( i=0; i<NUM; i++ ){
            printf("%10.7f", tan(PI*2/NUM*i));
            if( i != NUM-1 ){
                printf(",");
            }
            if( i%10 == 9 ){
                printf("\n\t");
            }
        }
        printf("\n};\n");
    }
    return 0;
}

出力結果

float TableSin[ 100 ] = {
         0.0000000, 0.0627905, 0.1253332, 0.1873813, 0.2486899, 0.3090170, 0.3681246, 0.4257793, 0.4817537, 0.5358268,
         0.5877853, 0.6374240, 0.6845471, 0.7289686, 0.7705132, 0.8090170, 0.8443279, 0.8763067, 0.9048271, 0.9297765,
         0.9510565, 0.9685832, 0.9822873, 0.9921147, 0.9980267, 1.0000000, 0.9980267, 0.9921147, 0.9822873, 0.9685832,
         0.9510565, 0.9297765, 0.9048271, 0.8763067, 0.8443279, 0.8090170, 0.7705132, 0.7289686, 0.6845471, 0.6374240,
         0.5877853, 0.5358268, 0.4817537, 0.4257793, 0.3681246, 0.3090170, 0.2486899, 0.1873813, 0.1253332, 0.0627905,
         0.0000000,-0.0627905,-0.1253332,-0.1873813,-0.2486899,-0.3090170,-0.3681246,-0.4257793,-0.4817537,-0.5358268,
        -0.5877853,-0.6374240,-0.6845471,-0.7289686,-0.7705132,-0.8090170,-0.8443279,-0.8763067,-0.9048271,-0.9297765,
        -0.9510565,-0.9685832,-0.9822873,-0.9921147,-0.9980267,-1.0000000,-0.9980267,-0.9921147,-0.9822873,-0.9685832,
        -0.9510565,-0.9297765,-0.9048271,-0.8763067,-0.8443279,-0.8090170,-0.7705132,-0.7289686,-0.6845471,-0.6374240,
        -0.5877853,-0.5358268,-0.4817537,-0.4257793,-0.3681246,-0.3090170,-0.2486899,-0.1873813,-0.1253332,-0.0627905

};
float TableCos[ 100 ] = {
         1.0000000, 0.9980267, 0.9921147, 0.9822873, 0.9685832, 0.9510565, 0.9297765, 0.9048271, 0.8763067, 0.8443279,
         0.8090170, 0.7705132, 0.7289686, 0.6845471, 0.6374240, 0.5877853, 0.5358268, 0.4817537, 0.4257793, 0.3681246,
         0.3090170, 0.2486899, 0.1873813, 0.1253332, 0.0627905, 0.0000000,-0.0627905,-0.1253332,-0.1873813,-0.2486899,
        -0.3090170,-0.3681246,-0.4257793,-0.4817537,-0.5358268,-0.5877853,-0.6374240,-0.6845471,-0.7289686,-0.7705132,
        -0.8090170,-0.8443279,-0.8763067,-0.9048271,-0.9297765,-0.9510565,-0.9685832,-0.9822873,-0.9921147,-0.9980267,
        -1.0000000,-0.9980267,-0.9921147,-0.9822873,-0.9685832,-0.9510565,-0.9297765,-0.9048271,-0.8763067,-0.8443279,

・・・・
ただ、図形の角度や面積の求め方が解らないということなので、
テーブル化よりも先に基本的な数学についておさらいした方が良い気がします。
特に角度に関する知識が無いとSTG制作は少し厳しいです。
また、三平方の定理(ピタゴラスの定理)は良く使いますし、
こればベジェ曲線や物理の力学の方程式なども使うでしょう。

テーブル化をしようと思っていらっしゃるのであればsinやcosの理屈はOKですか?
例えば「2.0ラジアンの方向に速度10で飛んでいる弾のX移動成分はいくつですか」と聞かれたら解りますか?

もし解らないけど、作りたいという事であれば関数をブラックボックス化して考えて作るということも可能ですが、応用が厳しくなってくると思います。

Dixq (管理人)

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

#8

投稿記事 by Dixq (管理人) » 15年前

あ、失礼しました。
テーブルの作り方が解らないのではなく、テーブル化の仕組みが解らないということですね。

まず、sin、cosなどの関数を使うときは、角度をラジアンで与えて計算結果を取得します。
ラジアンは
0.1でも0.01でも0.001でも0.0001でも0.000001でも有効範囲の中ならいくつでも計算出来ます。
しかし、これを計算する処理には結構時間がかかります。
と言ってもPCであれば百万回位ならすぐ計算出来ます。

for( i=0; i<1000000; i++ ){
b+=sin(3.141592654/1000000*i);
}

この計算結果は私のPCでは0.02秒でした。
PCには浮動小数点を計算する機構があるので、int型ほどは早くないものの、かなり高速に計算出来ます。
しかし組み込みなど、浮動小数点を計算する機構が含まれないものであれば極端に遅くなります。

そこで、math.h関数は極力呼ばない、浮動小数点は極力使わないなどの方法が必要になってくるわけです。
ここで、先ほど

>0.1でも0.01でも0.001でも0.0001でも0.000001でも計算出来ます

と言いましたが、そこまで必要ない場合があります。
計算誤差が0.1度位まで許されるのであれば、あらかじめ0.1度単位で計算したデータを持っておけばいいじゃないかと考えられるわけです。
そこで、上で紹介したようなデータを予め保持しておくのです。
角度を計算したい時は、その角度にもっとも近いデータをテーブルから探してあてはめるわけです。
例えば

float TableSin[360]

で360個、1度刻みのデータがあるのであれば、20度のsinは

TableSin[20]

を出してくればいいことが解ります。
このように関数を呼ばずに値を取り出す仕組みがテーブル化です。

しかし、どうも組み込みや携帯ゲームではないようですし、龍神録のプログラムのようですから、
処理落ちするのであれば、(既にお気づきのようですが)これがボトルネックになっているのではないと思います。

ただ勉強の為にいろんなプログラムを書いてみるのは大事なことですね。

閉鎖

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