0

CCSpriteBatchNode のソース コードを含むいくつかの場所で、そこから子を追加/削除するのは「高価」であることを見てきました。私の理解では、バッチ ノードを使用することの全体的なポイントは、同じスプライト シートからの多くのスプライトが同じコンテナに追加されているときに、高価な OpenGL 呼び出しが何度も発生するのを防ぐことです。

私が疑問に思っているのは、1) 子をスプライト バッチ ノードに追加/削除するのに「費用がかかる」こと、および 2) いつ使用するのが適切であると考えられるかということです。

たとえば、10 個のスプライトを作成するレーザー オブジェクトがあります...画面上を移動すると、その特定の画面位置の現在のスプライトを表示/非表示にします。画面の右端に到達すると、レーザー オブジェクトが破棄され、10 個のスプライトも破棄されます。それで、私が思っていたのは、これはスプライト バッチ ノードを使用するのに適切ではないということでしょうか。それは 10 個のスプライトしかなく、非常に高速に発生するためです。移動アニメーションは 0.2 秒なので、プレーヤーがすばやく発砲した場合、それはバッチ ノードに 10 個のスプライトを何度も追加/削除することを意味します...

別のケースでは、SpriteBatchNode をさまざまなオブジェクトに対して既にセットアップしており、追加する必要がある 1 回限りのスプライトに出くわすことがあります。それはたまたま同じスプライト シートの一部であるため、追加したくなります。そこにあるのでそのバッチノードに、そしてそれはすでにその特定のスプライトシートに指定されています......とにかく、このトピックについて明確にしたいと思います。

4

3 に答える 3

0

CCSpriteBatchNodeaと法線の主な違いは、 aはスプライトごとにではなく、すべてのスプライトのすべてのデータを一度に GPU に送信するCCSpriteという事実です。CCSpriteBatchNode

CCSpriteドローコールは次のように機能します。

glVertexAttribPointer(kCCVertexAttrib_Position, 3, GL_FLOAT, GL_FALSE, kQuadSize, (void*) (offset + diff));
glVertexAttribPointer(kCCVertexAttrib_TexCoords, 2, GL_FLOAT, GL_FALSE, kQuadSize, (void*)(offset + diff));
glVertexAttribPointer(kCCVertexAttrib_Color, 4, GL_UNSIGNED_BYTE, GL_TRUE, kQuadSize, (void*)(offset + diff));
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

基本的に、スプライトのデータを設定するために 3 つの呼び出しが行われ、次に への呼び出しglDrawArraysが行われます。100 個のスプライトがある場合、このコードは 100 回実行されます。

次に見てみましょうCCSpriteBatchNode(別の可能な最適化である VAO なしの実装を選択しました):

glVertexAttribPointer(kCCVertexAttrib_Position, 3, GL_FLOAT, GL_FALSE, kQuadSize, (GLvoid*) offsetof( ccV3F_C4B_T2F, vertices));
glVertexAttribPointer(kCCVertexAttrib_Color, 4, GL_UNSIGNED_BYTE, GL_TRUE, kQuadSize, (GLvoid*) offsetof( ccV3F_C4B_T2F, colors));
glVertexAttribPointer(kCCVertexAttrib_TexCoords, 2, GL_FLOAT, GL_FALSE, kQuadSize, (GLvoid*) offsetof( ccV3F_C4B_T2F, texCoords));
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffersVBO_[1]);
glDrawElements(GL_TRIANGLE_STRIP, (GLsizei) n*6, GL_UNSIGNED_SHORT, (GLvoid*) (start*6*sizeof(indices_[0])) );

このコードは、連続したメモリに格納されているため、すべてのスプライトのすべてのデータを一度に設定します。この呼び出しは、スプライトの数に関係なく、1、10、100 で同じです。

そのため、より効率的ですが、同時に、データがメモリに連続して格納されるため、子が削除、追加、または変更されると、それに応じて配列を変更し、GPU で更新する必要があります。これが、追加と削除のコストが発生する場所です (または、非表示の CCSprite はレンダリング フェーズをスキップするだけで、バッチ ノードの非表示の CCSprite はスキップしないという事実からも)。

個人的な経験から言えば、通常、コストはごくわずかであり、可能な場合は常に を使用する必要がありますCCSpriteBatchNode(スプライトごとではなく、ノード全体をブレンドするなどの制限があるため)。同じ種類/理由の複数のスプライトを描画する。

ただし、自分でベンチマークするのは簡単なはずです。

于 2014-05-11T23:19:15.293 に答える
0

前述のように、スプライト バッチ ノードは、すべての子に対して GPU への呼び出しをバッチ処理します (それらは同じテクスチャを使用するため)。ただし、パフォーマンスに影響を与えるには、かなりの量のスプライトが必要です。10スプライトの場合、違いはないと思います...

とはいえ、Cocos2d の新しいバージョン (3.0 など) を使用している場合、現在ベータ版の 3.1 は自動バッチ処理を提供するため、CCSpriteBatchNode をいじって時間を無駄にする必要はありません。Cocos2d は、GPU に送信されたデータを自動的にバッチ処理します。

于 2014-05-12T13:32:13.640 に答える
0

1) スプライト バッチ ノードに子を追加/削除するのが「高価」である方法

「高価」になる可能性があることを私が認識している唯一のシナリオは、アトラスの容量を増やす必要がある場合です。ご覧のとおり、バッチ ノードには容量があり、それを超える子を追加すると、ノードはその容量を増やし、すべてのスプライトのテクスチャ座標を再計算する必要があります。

これを修正するには、最初からバッチ ノードに適切な容量を与えます (小さすぎず、多すぎず)。必要に応じて、そのような番号を特定するのはあなた次第です。

2) いつ使用するのが適切であると考えられますか?

同じテクスチャ ソースを使用できる複数のスプライトがある場合。マリオ ゲームの場合、画面上に複数のコインが必要になることは明らかです。これは、バッチ ノードの適切な使用例です。コイン イメージ用のバッチ ノードがあれば、すべてのコイン スプライトがこのバッチ ノードを使用します。

いくつかの要素を同じテクスチャにパックできる場合があります。たとえば、コインの画像、モンスターの画像、キノコの画像をすべて同じテクスチャに収めることができます。このようにして、すべてのコイン、モンスター、キノコが同じバッチ ノードを使用できます。

バックグラウンド テクスチャのようなものにバッチ ノードは必要ありません。おそらくバックグラウンド スプライトは 1 つしか必要ないからです。

それで、私が思っていたのは、これはスプライト バッチ ノードを使用するのに適切ではないということでしょうか。それは 10 個のスプライトしかなく、非常に高速に発生するためです。移動アニメーションは 0.2 秒なので、プレーヤーがすばやく発砲した場合、それはバッチ ノードに 10 個のスプライトを何度も追加/削除することを意味します...

これは、バッチ ノードの有効な使用例です。結局、10個のスプライトが同時に描画されます。また、レーザー オブジェクトをもう使用しないことがわかっている場合は、対応するバッチ ノードをいつでもアンロードできます。ゲーム内に複数のレーザー オブジェクトが存在する可能性があるため、バッチ ノードを使用することをお勧めします。

率直に言って、パフォーマンスについてあまり心配する必要はありません。私は自分のゲームであらゆる種類のもの (キャラクター、天気のパーティクル、マップ オブジェクト、コレクション、インターフェイスなど) に常に数十を使用していますが、それらのおかげで、55 fps を下回ることはめったにありません。

実際、バッチ ノードの使用に反対するのは難しいと思います。それらが害を及ぼすことはめったにありません。

于 2014-05-11T23:25:08.813 に答える