6

マップに惑星全体を使用するゲームを作成しています。この手法を使用して球形の惑星をテッセレーションし、カメラコントロールを追加しています。

球の次元は1から-1であるため、球上の各点も正規化されたベクトルです。いつでも、球を構成する六角形のタイルの1つは、「選択された」タイルです。その後、プレーヤーは十字キーを使用して選択範囲を隣接するタイルに移動できます。また、アナログスティックを使用してカメラを個別に回転させることもできます

選択したタイルとカメラに関して2つのことを行う必要があります。まず、カメラに最も近いタイルに選択を切り替えることができる必要があります。次に、ハイライトされたタイルの中央にカメラを配置する必要があります

球は原点に配置され、カメラは点(0,0,1)に配置されます。グラフィックエンジンでは、カメラをX軸とY軸を中心に回転させることしかできないため、最初の問題を解決するために、クォータニオンを使用して点(0,0,1)をx軸とy軸を中心に回転させ、カメラがある3D空間:

    private Quaternion quat = new Quaternion(0,0,0,0);
    private double[] output = new double[4];
    private double[] cam = new double[3];

    private double camX = 0;
    private double camY = 0;
    private double camZ = 1;


    private double[] getCamPosition(){

        quat.setAxisAngle(1, 0, 0, Math.toRadians(-graphicsEngine.getRotationX()));
        quat.RotateVector(camX, camY, camZ, output);            
        cam[0] = output[0];
        cam[1] = output[1];
        cam[2] = output[2];   

        quat.setAxisAngle(0, 1, 0, Math.toRadians(-graphicsEngine.getRotationY()));
        quat.RotateVector(cam[0], cam[1], cam[2], output);          
        cam[0] = output[0];
        cam[1] = output[1];
        cam[2] = output[2];

        return cam;
    }

次に、各タイルの図心とカメラの位置の間の距離を比較し、新しく選択したタイルに最も近いタイルを取得します。

しかし、2番目の問題を解決するために、私は逆のことをしたいと思います。図心(すでに正規化されたベクトルの形式になっています)を取得し、カメラを中心に配置するために必要なXの周りの回転とYの周りの回転を調べます。

現時点では、カメラを(0,0,1)に回転させて戻し、X軸とY軸の(0,0,1)と図心の間の角度を取得し、それを使用してカメラを回転させています。 2回目:

private double[] outputArray = new double[2];

/**
 * Cam is always (0,0,1)
 */
public void centreOnSelected(double camX, double camY, double camZ){        

    selectedTile.getCentroidAngles(outputArray);

    outputArray[0] -= Math.atan2(camZ, camY);
    outputArray[1] -= Math.atan2(camX, camZ);

    // this determines if the centroid is pointing away from the camera
    // I.e. is on the far side of the sphere to the camera point (0,0,1)
    if(!selected.getCentroidDirectionY(camX, camZ)){
        outputArray[0] = -Math.PI - outputArray[0];         
    }

    graphicsEngine.rotateCam(Math.toDegrees(outputArray[0]), Math.toDegrees(outputArray[1]));


}

およびselected(タイルクラス)

void getCentroidAngles(double[] outputArray){
    outputArray[0] = Math.atan2(centroidZ, centroidY);
    outputArray[1] = Math.atan2(centroidX, centroidZ);

}

問題は、これが機能しないことです(x軸は常に外れているようです)。角度を取得して回転する数学と関係があると確信しています。

注:グラフィックエンジンは最初にX軸を中心に回転し、次にY軸を中心に回転します。

        gl.glRotatef(mRotateX, 1, 0, 0);
        gl.glRotatef(mRotateY, 0, 1, 0);

図心はすべて正しい場所にあり、カメラは間違いなく正しい量だけ回転しているので、問題はグラフィックエンジンにあるのではないと確信しています。また、プログラムをステップスルーしてこれが機能することを確認したので、カメラを(0,0,1)に再配置することでもありません。

問題を説明するためのビデオも作成しました。

http://www.youtube.com/watch?v=Uvka7ifZMlE

これは何日も私を悩ませてきたので、これを修正するのに助けがあれば大歓迎です!

ありがとうジェームズ

4

1 に答える 1

2

残念ながら、私はここで何が起こっているのかを完全には理解しておらず、完全な解決策を提供することはできませんが、少なくともいくつかの問題を指摘することはできます。

私はglRotatefを使用したことがありませんが、ここでの変換の順序と符号について少し混乱しています。たとえば、glRotatef呼び出しの順序を指定して、最初にx回転を実際に実行していますか?

とにかく、ここでの問題の少なくとも一部はこれらの方程式から来ています

outputArray[0] = Math.atan2(centroidZ, centroidY);
outputArray[1] = Math.atan2(centroidX, centroidZ);

まず、最初の意味(centroidY, centroidZ)ですか?さらに深刻なことに、ここで得られる角度を使用して、重心を運ぶ回転を構築することはできません(0,0,1)。その逆も同様です。たとえば、重心ベクトルをに回転させたいとします(0,0,1)。2つの回転のそれぞれを個別に使用して、1つのコンポーネントをゼロに設定する単一の軸を中心に回転させることができます。たとえばoutputArray[0]、適切な符号のx軸を中心に回転すると、yコンポーネントがゼロに設定されます(の引数atan2が交換されたと想定)。または、outputArray[1]y軸を中心とした右符号の場合、xコンポーネントを0に設定できます。ただし、最初にx回転を実行して(たとえば)yコンポーネントを0に設定すると、重心ベクトルが変化します。これで、xを設定するy軸を中心とした回転になります。 0へのコンポーネントは、によって記述されなくなりましたoutputArray[1]

これらのものの適切な式には、常にatan2一方の角度ともう一方の角度acosがありasinます。たとえば、ベクトル(1,0,0)を運ぶアクティブな回転の角度が必要な場合(x,y,z)は、

first_angle_around_x  = -asin(y)
second_angle_around_y = atan2(x, z)

あなたに続けるために使用(x,y,z)(1,0,0)ます

first_angle_around_x  = atan2(y, z)
second_angle_around_y = -asin(x)

(これらの角度は、軸が「目を突っ込んでいる」ときに反時計回りに回転するx、y軸の周りの回転を表します。)

別の問題はここにあります

outputArray[0] -= Math.atan2(camZ, camY);
outputArray[1] -= Math.atan2(camX, camZ);

このような角度の減算は、単一軸を中心とした回転に対してのみ機能します。異なる軸の周りの回転から複合変換を構築すると、関係ははるかに複雑になります。これが、行列とクォータニオンが役立つ理由です。メソッドの前のコメントで示唆されているように、camX-Z入力が冗長である場合、このコードは重要ではないと思います(ただし、現時点では、ZコンポーネントとYコンポーネントがここにあるので、ゼロ以外の結果が得られる可能性があります。私が言及した最初の方程式で「間違った」方法であるそれらを補う;これが意図的であるかどうかはわかりません)。

カメラを選択したタイルに移動する方法にも根本的な問題がありますが、それが実際に問題であるかどうかは少し主観的です。x軸とy軸を中心に回転するだけなので、惑星上の各ポイントに固有のカメラの向きがあります。問題は、そのような方向を継続的に選択する方法がないことです。これを確認するには、カメラがそのポイントより上にあるときに、各ポイントのベクトルが画面のx方向に沿った単位ベクトルである惑星上のベクトル場を想像してください。このフィールドは、毛の生えたボールの定理によって連続することはできません。これが実際に意味することは、カメラの位置を少し調整するように見える惑星上で、惑星のホイールが画面上で最大180度回転するポイントがあるということです。これを避けたい場合は、代わりに現在のカメラ位置にある向きを選択して、回転量を最小限に抑えることができます。これは、たとえば、カメラが閉ループで移動する場合、惑星が画面上で「転がる」可能性があることを意味します。これは、ゲームでは望ましくない場合があります。また、エンジンが3軸すべてで回転できる必要があります。

于 2013-02-17T12:45:37.853 に答える