-3

編集:解決策が見つかりました。質問の後に追加しました。

私は Android 用のゲームを設計しており、レンダリング段階での計算を減らす方法を考え出そうとしています。現時点では、レベル内のすべての金属とゴムのブロックを取得し、テクスチャ ID を int[][] グリッドに格納して、フレームごとにすべてのブロックのタイル張りテクスチャを計算する代わりにレンダラーがそれを読み取るようにするメソッドを持っています。

これは正常に機能しますが、レベルのエッジとレベル内のレーザー ブロックの角と直線部分のリストを作成しようとしています。レベル境界とレーザー ブロックは、一連のストレート レーザー テクスチャとコーナー テクスチャを使用して描画されます。ブロックが他のブロックやレベル エッジと重なっている場所でレーザーをレンダリングしない場所を解決する最善の方法がわかりません。以下の写真は、私が何を意味するかを示しています。

ゲームで

ここでは、レベル エッジ (写真の端を越えて伸びる L 字型のレーザー パス) と 3 つまたは 2 つの内部レーザー ブロック (それぞれ写真の順序) を見ることができます。私が知る限り、上記と同様のグリッドを作成する必要がありますが、ブール値を使用して、タッチするとプレーヤーを殺す正方形 (赤で強調表示) は true であり、安全な正方形は false です。

次に、グリッド内のすべての真の (赤) セルを調べて、隣接するグリッド セルを使用してレーザーの輪郭がどのように見えるかを最初に考えましたが、これは非常に難しいことに気付きました。それを見つけるための正方形。レベル境界の左下の四角形から始めて、(最初の四角形が偽でない限り) 偽のタイルが見つかるまでグリッドを反復処理し、グリッドを右に移動するまで、大まかな解決策を得ることができると確信しています。右の真のセルに到達したので、左に曲がってグリッドを上に進み、上の真が見つかって左に曲がるか、右に偽が見つかって右に曲がるまで続けます。最初の偽のセルに到達するまで、このプロセスを繰り返します。

この質問を書いているときにこれを思いつきました笑。それは最も簡単な方法のように思えるので、私の質問はこれがそれを行うための良い方法であり、上記の方法は最も外側のレーザーのみをトレースするため、互いに接触しているがレベル境界には触れていないレーザーブロックをどのように解決するかということだと思います道。

ここまでお読みいただきありがとうございました。私はそれを十分に説明したことを願っています.

4

1 に答える 1

0

解決:

    public static boolean[][] laserField = new boolean[(int) Static.WORLD_SIZE][(int)Static.WORLD_SIZE];
    public static List<LaserData> laserData = new ArrayList<LaserData>();
    public static void calcLaserBoundryAreas() {
        laserField = new boolean[(int) Level.levelBounds.bounds.width+5][(int) Level.levelBounds.bounds.height+5];
        for (int i=0;i<laserField.length;i++) {
            for (int j=0;j<laserField[i].length;j++) {
                if(i==0 || i==laserField.length-1 || j==0 || j==laserField[i].length-1)
                    laserField[i][j] = true;
                else
                    laserField[i][j] = false;
            }
        }
        for (LaserBlock lBlock : lBlocks) {
            int cols = (int)lBlock.bounds.width;
            int rows = (int)lBlock.bounds.height;
            float startX = lBlock.position.x - (cols-1f)/2f;
            float startY = lBlock.position.y - (rows-1f)/2f;
            for (int i=0;i<cols;i++) {
                for (int j=0;j<rows;j++) {
                    addLaserCell(startX+i, startY+j);
                }
            }
        }
        addLaserData();
    }

private static void addLaserCell(float x, float y) {
    int cellX = (int)(x- Level.levelBounds.bounds.lowerLeft.x+2);
    int cellY = (int)(y- Level.levelBounds.bounds.lowerLeft.y+2);
    if (cellX < 0 || cellX > laserField.length-1)           return;
    if (cellY < 0 || cellY > laserField[cellX].length-1)    return;
    laserField[cellX][cellY] = true;
}

private static void addLaserData() {
    laserData = new ArrayList<LaserData>();
    for (int i=1;i<laserField.length-1;i++) {
        for (int j=1;j<laserField[i].length-1;j++) {
            if (!laserField[i][j]) {
                checkNeighbours(i,j);
            }   
        }
    }
    optimiseLaserData();
}

private static void checkNeighbours(int x, int y) {
    boolean u = laserField[x][y+1];
    boolean ul = laserField[x-1][y+1];
    boolean l = laserField[x-1][y];
    boolean bl = laserField[x-1][y-1];
    boolean b = laserField[x][y-1];
    boolean br = laserField[x+1][y-1];
    boolean r = laserField[x+1][y];
    boolean ur = laserField[x+1][y+1];

    /*
     *  TOP LEFT CORNER 
     */
    float posX, posY;
    posX = Level.levelBounds.bounds.lowerLeft.x+x-2.5f;
    posY = Level.levelBounds.bounds.lowerLeft.y+y-1.5f;
    if(u && ul && l)
        laserData.add(new LaserData(posX, posY, true, 0, 0));
    else if(!u && ul && l)
        laserData.add(new LaserData(posX, posY, false, 1, 0));
    else if(!u && ul && !l)
        laserData.add(new LaserData(posX, posY, true, 0, 2));

    /*
     *  BOTTOM LEFT CORNER 
     */
    posX = Level.levelBounds.bounds.lowerLeft.x+x-2.5f;
    posY = Level.levelBounds.bounds.lowerLeft.y+y-2.5f;
    if(l && bl && b)
        laserData.add(new LaserData(posX, posY, true, 0, 1));
    else if(!l && bl && b)
        laserData.add(new LaserData(posX, posY, false, 1, 1));
    else if(!l && bl && !b)
        laserData.add(new LaserData(posX, posY, true, 0, 3));

    /*
     *  BOTTOM RIGHT CORNER 
     */
    posX = Level.levelBounds.bounds.lowerLeft.x+x-1.5f;
    posY = Level.levelBounds.bounds.lowerLeft.y+y-2.5f;
    if(b && br && r)
        laserData.add(new LaserData(posX, posY, true, 0, 2));
    else if(!b && br && r)
        laserData.add(new LaserData(posX, posY, false, 1, 2));
    else if(!b && br && !r)
        laserData.add(new LaserData(posX, posY, true, 0, 0));

    /*
     *  TOP RIGHT CORNER 
     */
    posX = Level.levelBounds.bounds.lowerLeft.x+x-1.5f;
    posY = Level.levelBounds.bounds.lowerLeft.y+y-1.5f;
    if(r && ur && u)
        laserData.add(new LaserData(posX, posY, true, 0, 3));
    else if(!r && ur && u)
        laserData.add(new LaserData(posX, posY, false, 1, 3));
    else if(!r && ur && !u)
        laserData.add(new LaserData(posX, posY, true, 0, 1));

}

private static void optimiseLaserData() {
    List<LaserData> optiLaserData = new ArrayList<LaserData>();
    for(LaserData ld : laserData) {
        if(ld.cornerPiece)
            optiLaserData.add(ld);
        else if(ld.dir == 0 || ld.dir == 2){
            float x = ld.x;
            float bottomY = ld.y;
            float topY = ld.y;
            float count = 1;
            while (searchStraightLaserData(laserData, x, topY+1, ld.dir)) {
                count++;
                topY++;
            }
            while (searchStraightLaserData(laserData, x, bottomY-1, ld.dir)) {
                count++;
                bottomY--;
            }
            float centerY = bottomY + (topY-bottomY)/2;
            if(!searchStraightLaserData(optiLaserData, x, centerY, ld.dir))
                optiLaserData.add(new LaserData(x, centerY, false, count, ld.dir));
        } else {
            float y = ld.y;
            float leftX = ld.x;
            float rightX = ld.x;
            float count = 1;
            while (searchStraightLaserData(laserData, rightX+1, y, ld.dir)) {
                count++;
                rightX++;
            }
            while (searchStraightLaserData(laserData, leftX-1, y, ld.dir)) {
                count++;
                leftX--;
            }
            float centerX = leftX + (rightX-leftX)/2;
            if(!searchStraightLaserData(optiLaserData, centerX, y, ld.dir))
                optiLaserData.add(new LaserData(centerX, y, false, count, ld.dir));
        }
    }
    laserData = optiLaserData;
}

private static boolean searchStraightLaserData(List<LaserData> data, float x, float y, int dir) {
    for(LaserData ld : data)
        if(ld.x == x && ld.y == y && ld.dir == dir && !ld.cornerPiece)
            return true;
    return false;
}

これらのメソッドは、最初にレベル エッジ境界のサイズであるブール グリッドを作成し、両側に 1 平方の余分なエッジを作成します。これは、安全な領域を表すために false に初期化され、余分なエッジは true に設定されているため、一種の中空ボックスができます。追加のエッジは、laserField の不適切なインデックスをチェックする必要をなくすことで、後で役立ちます。

レベルの範囲がグリッドにマップされた後、個々のセルは、レーザー ブロックで覆われている場所で true に変更されます。

ブール グリッドが完全にマッピングされると、各グリッド セルを反復処理し、偽のセルが見つかると、グリッド座標を次のメソッドに渡します。次のメソッドは、12 の異なる隣接パターンを調べて、このセルの周りにレーザーをレンダリングする必要があるかどうかを判断します。 . LaserData コンストラクターは次の引数を取ります (float x、float y、boolean cornerPiece、float length、int 方向)

最後のセクションでは、ブルート フォース検索を実行して、余分なスプライトのレンダリングを節約するために、隣接する直線部分を 1 つの長い直線部分に置き換えることができるかどうかを確認します。

その後、レンダラーは各フレームの laserData リストを読み取るだけで、正しいテクスチャ、その位置、長さなどをレンダリングするために必要なすべての情報を取得できます...

注: レベル境界の幅と高さは、境界外のプレイヤーの幅を考慮して、実際のプレイ エリアよりも 3 単位小さくなっています。それが levelBounds.lowerleft+5、+2、+ 1.5f などの由来です。少しハッキーなのはわかっていますが、古いコードであり、あえて触れませんxD

于 2012-11-26T01:15:42.880 に答える