Andengine:テクスチャ付きのカスタム地形

ここで人々はAndroid用のゲームを書き始め、このタスクでAndengine(誰も知らない、これは最も人気のあるグラフです。Android用の2Dエンジン)に直面しました:風景を表す相互接続された線のセットがあります(生成方法、ここで読むことができます- gameprogrammer.com/fractal.html )。 次のようになりました。



画像



しかし、私たちは「橋」を必要とせず、表面が必要であり、一般的にはテクスチャーさえ必要なので、このようになります...





画像



AndEngineが掘り始めたのは、2つの三角形で構成されたスプライトと同じようにしか機能できないテクスチャでした。 風景のサイズが事前にわからないので、これは私たちには決して適していません。したがって、UV座標1:1の比率は気にしません。 そして、原則として、ここではスプライトはありませんが、表面は非凸多面体です。 したがって、自転車を作成する必要があります。 グーグルはメインとエンジンのブランチに通常の結果を与えませんでした。 彼が適切なクラスインターフェイスを持ち、すべてが論理的であることは良いことです。あなたはそれを理解するだけです。 三角形とそれに対応するUV座標用の頂点バッファーを持つ独自のクラスが必要です。 なぜ多くの機能が過負荷にならないのか、なぜ定義でいくつかのことが行われるのかについては説明しません。 そして、エンジンは建築用語で複雑に織り込まれた森全体であり、エンジンエンジン全体を整理するには10個の記事と半年の人生が必要になるので、私はそれが機能するポーズで物事を残しました。

行こう...



まず、表面を構成するすべての線を含むリストが既にあることに同意します。 最初の画面に描かれているのと同じ「橋」。



表面を表すクラスの説明を始めます。

private abstract class GroundShape extends Shape {
      
      





便宜上、空間とUVの2次元座標を格納する各頂点のオブジェクトを作成します。

  protected class Vertex { float x, y; float u, v; }; protected class MorphVertexBuffer extends VertexBuffer { public MorphVertexBuffer(int capacity) { //    ,  . super(capacity, GL11.GL_STATIC_DRAW, true); } //        . public void update(Vertex[] vertexes) { int j = 0; final float[] bufferData = new float[vertexes.length*2]; for (int i = 0; i < vertexes.length; ++i) { bufferData[j++] = vertexes[i].x; bufferData[j++] = vertexes[i].y; } final FastFloatBuffer buffer = this.getFloatBuffer(); buffer.position(0); buffer.put(bufferData); buffer.position(0);//,       :) super.setHardwareBufferNeedsUpdate(); } }
      
      





上記のコードは、エンジンに供給することができる頂点を持つバッファーである内部クラスを説明しています。 VertexBufferを継承して標準アーキテクチャにとどまり、頂点バッファーに値を設定するupdate()メソッドを説明します。

次のステップでは、UV座標データとテクスチャマッピングメソッドを使用してバッファを記述する型を作成します。

  protected class MorphTexture extends BufferObject { //ITexture  ,    . final ITexture mTexture; public MorphTexture(ITexture tex, int pCapacity) { super(pCapacity, GL11.GL_STATIC_DRAW, true); mTexture = tex; } public void ApplyUV(Vertex [] vertexes) { final float[] bufferData = new float[vertexes.length*2]; for (int i = 0, j = 0; i < vertexes.length; ++i) { bufferData[j++] = vertexes[i].u; bufferData[j++] = vertexes[i].v; } final FastFloatBuffer buffer = this.getFloatBuffer(); buffer.position(0); buffer.put(bufferData); buffer.position(0);//,       :) super.setHardwareBufferNeedsUpdate(); }
      
      





その後、再びバッファを形成し、それをエンジンに供給することができます。

以下は、テクスチャをオブジェクトに「適用」し、UV座標の頂点のポインタバッファをApplyUV()で形成したものに設定する関数について説明しています。

  public void onApply(final GL10 pGL) { this.mTexture.bind(pGL);//   ,    glBindTexture() if(GLHelper.EXTENSIONS_VERTEXBUFFEROBJECTS) { final GL11 gl11 = (GL11)pGL; selectOnHardware(gl11); GLHelper.texCoordZeroPointer(gl11); } else { GLHelper.texCoordPointer(pGL, getFloatBuffer()); } } }
      
      





次に、上記の頂点とUV座標バッファーオブジェクトを開始します。

  MorphVertexBuffer m_Buffer; MorphTexture m_TextureRegion; int vertexesLimit; //    . protected BitmapTextureAtlas m_Texture;//,    
      
      





上記のクラスはGroundShapeの内部クラスであるため、説明はコンストラクターで続行します。コンストラクターはそれ自体は簡単であり、転送されるもの、課す必要のあるテクスチャーのみに関心があります。

  public GroundShape(BitmapTextureAtlas texture) { super(0, 0); m_Texture = texture; }
      
      





次に、頂点バッファーとUV座標を初期化するために子孫で呼び出す必要がある初期化関数について説明します。

  protected void Init() { Vertex[] vertexes = buildVertexBuffer();//    . if (vertexes == null) return; //  . vertexesLimit = vertexes.length; m_Buffer = new MorphVertexBuffer(vertexesLimit*2); m_Buffer.update(vertexes); m_TextureRegion = new MorphTexture(m_Texture, vertexesLimit*2); m_TextureRegion.ApplyUV(vertexes); }
      
      





なぜなら GroundShape-抽象、子孫ではbuildVertexBuffer関数をオーバーロードする必要があります。この関数では、頂点のリスト(UV座標)を作成して返す必要があります。 ここに彼女は

  protected abstract Vertex[] buildVertexBuffer();
      
      





次のステップでは、いくつかのGroundShapeメソッドをオーバーロードして、AndEngineに表面に何をどのようにペイントするかを伝えます。

  @Override protected void doDraw(final GL10 pGL, final Camera pCamera) { //  m_TextureRegion.onApply(pGL); // super.doDraw(pGL, pCamera); } @Override protected void onInitDraw(final GL10 pGL) { //           UV . //GLHelper - . super.onInitDraw(pGL); GLHelper.enableTextures(pGL); GLHelper.enableTexCoordArray(pGL); } @Override protected void drawVertices(GL10 pGL, Camera arg1) { //   . pGL.glDrawArrays(GL10.GL_TRIANGLES, 0, vertexesLimit); }
      
      





onApplyを呼び出して、UV座標の頂点をdoRawで使用するように指定した場合、三角形自体の頂点を示すために、関数を追加で呼び出す必要はなく、単にgetVertexBufferをオーバーロードして頂点バッファーを返します。

  @Override protected VertexBuffer getVertexBuffer() { return m_Buffer; }
      
      





以下では、デフォルトで単純にオーバーロードされ、意味を持たない関数について説明しますが、これらは継承プロセスの必須部分です。

  @Override public boolean collidesWith(IShape arg0) { return false; } @Override public float getBaseHeight() { return 0; } @Override public float getBaseWidth() { return 0; } @Override public float getHeight() { return 0; } @Override public float getWidth() { return 0; } @Override public boolean contains(float arg0, float arg1) { return false; } @Override protected boolean isCulled(Camera arg0) { return false; } @Override protected void onUpdateVertexBuffer() { } }
      
      





OK、AndEngineが、三角形で構成され、テクスチャを持つ「モデル」を描く方法を指示するクラスをスケッチしました。 これは2Dエンジンですが、OpenGLでも機能します。スプライトは2つの三角形に描かれます。

ちなみに、OGLのAndroidにはGL_POLYGONSはなく、 GL_TRIANGLESのみが存在することに注意してください。 最速のものはGL_TRIANGLE_STRIPです。それらについては、 en.wikipedia.org / wiki / Triangle_stripをご覧ください。 ただし、これらには特定のシーケンスとトラブルが必要であり、これは行いたくなかったので、 GL_TRIANGLESを使用します (後のテストでは、パフォーマンスの増加は最小限でした)。 したがって、表面を三角形で「見る」と、最初と比べて次のようになります。

画像画像

そのため、送信される行のリストに基づいて生成する必要があります。 このためのオブジェクトを作成します。

 private class GroundSelf extends GroundShape { public GroundSelf(List<Section> sec, BitmapTextureAtlas texture) { super(texture); sections = sec;//    ,    . Init();//    GroundShape' }
      
      





そしてGroundShape :: Init()は、覚えているように、すべての相続人がオーバーロードしなければならないbuildVertexBuffer()を呼び出します。 この関数では、各三角形のすべての頂点を構築し、UV座標を設定する必要があります。 テクスチャが正方形であり、地球は一般に非凸多面体であることを考慮する価値があります。すべての三角形のテクスチャを1対1の座標比で愚かに引っ張ると、画像のようにテクスチャを並べ替えることさえできません。 さらに、要因を設定できる必要があります。なぜなら、 長さが高さよりも大きい場合、U座標は係数だけ大きくする必要があります。

テクスチャを操作するときは、コンパスを写真として撮ることを強くお勧めします。これにより、テクスチャ座標の方向を正しく判断できます。

実際、buildVertexBuffer関数では、オブジェクトとそのUV座標のすべての三角形を定義します。

  @Override protected Vertex[] buildVertexBuffer() { int vertexesCount = 0, i, j, k = 0; float hellY = 800.0f;//   "". final float maxU = 4.0f;//     U final float maxV = 2.0f;//  V float stepU; //       // -   ,    . //    V. float startV = sections.get(0).lines.get(0).line.getY1(); float valueV = hellY - sections.get(0).lines.get(0).line.getY1(); for (i = 0; i < sections.size(); ++i) vertexesCount += sections.get(i).lines.size()*6; Vertex[] res = new Vertex[vertexesCount]; Section tmpSection; Line tmpLine; for (i = 0; i < sections.size(); ++i) { tmpSection = sections.get(i); //         //.     . //  6 . for (j = 0; j < tmpSection.lines.size(); ++j) { tmpLine = tmpSection.lines.get(j).line; stepU = maxU/(float)tmpSection.lines.size(); res[k] = new Vertex(); res[k].x = tmpLine.getX1(); res[k].y = tmpLine.getY1(); res[k].u = (float)j*stepU; res[k++].v = maxV + ((startV - tmpLine.getY1())/valueV)*maxV; res[k] = new Vertex(); res[k].x = tmpLine.getX1(); res[k].y = hellY; res[k].u = (float)j*stepU; res[k++].v = 0.0f; res[k] = new Vertex(); res[k].x = tmpLine.getX2(); res[k].y = tmpLine.getY2(); res[k].u = (float)(j + 1)*stepU; res[k++].v = maxV + ((startV - tmpLine.getY2())/valueV)*maxV; res[k] = new Vertex(); res[k].x = tmpLine.getX2(); res[k].y = tmpLine.getY2(); res[k].u = (float)(j + 1)*stepU; res[k++].v = maxV + ((startV - tmpLine.getY2())/valueV)*maxV; res[k] = new Vertex(); res[k].x = tmpLine.getX1(); res[k].y = hellY; res[k].u = (float)j*stepU; res[k++].v = 0.0f; res[k] = new Vertex(); res[k].x = tmpLine.getX2(); res[k].y = hellY; res[k].u = (float)(j + 1)*stepU; res[k++].v = 0.0f; } } //  ,  . return res; } List<Section> sections; }
      
      





作成された表面。 次に、それを世界にアタッチする必要があります。 これは、AndEngineで通常どおり行われます。

 //  grndSelf = new GroundSelf(sections, EvoGlobal.getTextureCache().get(EvoTextureCache.tex_ground).texture); //  AndEngine EvoGlobal.getWorld().getScene().attachChild(grndSelf);
      
      





結果:

画像



解決策を求めてGoogle経由でここに来た人が満足することを願っています。



All Articles