いくつかのコンテキスト: C++ と Objective-C のハイブリッドを含む Cocos2D/Box2D 用の 2D Destructible 地形ライブラリを作成しています。私は最近、並列配列を含まない解決策が思いつかない状況に遭遇しました。
地形の周囲に物理境界を定義するには、地形の境界から最初のトレースを作成する必要があります。関数呼び出しを行って、テクスチャ内の分離されたすべてのピクセル ボディをトレースし、それらをキャッシュします。これにより、次のデータ構造が作成されます
- ピクセルの一意の位置に等しい NSValue にラップされた CGPoint に等しいキーを持つ NSMutableDictionary "borderPixels"。これは、トレースされたすべてのピクセルを保持します。
- TerPixels が次の隣接ピクセルを指す循環リンク リスト
- 地形本体の「開始」点を表す単一の TerPixel を保持する NSMutableArray "traceBodyPoints"。ここには、物理体をトレースする必要がある TerPixel * のみを格納します。したがって、地形本体が変更されている場合は、変更された本体からの個々の TerPixel * をこの配列に挿入します。次に、これらのそれぞれを参照し、リンクされたリストを走査して物理体をトレースします。
状況をよりよく把握するためのコードを次に示します。
-(void)traverseBoundaryPoints:(CGPoint)startPt {
if (!borderPixels) borderPixels = [[NSMutableDictionary alloc] init]; // Temp
if (!traceBodyPoints) traceBodyPoints = [[NSMutableArray alloc] init]; // Temp
TerPixel * start = [[TerPixel alloc] initWithCoords:startPt.x ypt:startPt.y prevx:-1 prevy:0];
TerPixel * next = start;
//CCLOG(@"Start of traverseBoundary next.x and next.y %d, %d", next.x, next.y);
TerPixel * old;
while (true) {
old = next;
next = [self findNextBoundaryPixel:next];
[next setNSP:[old subTerPixel:next]];
old.nextBorderPixel = next;
if (next.x == start.x && next.y == start.y) {
CCLOG(@"SUCCESS :: start = next");
next.nextBorderPixel = start; // Make the linked list circular
NSValue * pixLocVal = [next getAsValueWithPoint];
[borderPixels setObject:next forKey:pixLocVal];
// Add the pixel to the tracePoints array to be traversed/traced later
[traceBodyPoints addObject:start];
break;
} // end if
// Connect the linked list components
NSValue * pixLocVal = [next getAsValueWithPoint];
[borderPixels setObject:next forKey:pixLocVal];
} // end while
} // end traverse function
ここで、解決策を見つけることができません。traceBodyPoints 配列内の各 TerPixel * を、作成されて物理ワールドに追加される Box2D b2Body に関連付ける必要があります。私のライブラリでは、テクスチャ内の分離されたピクセルの各ボディが Box2D ボディに対応しています。そのため、地形のチャンクを破壊するイベントが発生した場合、破壊されたピクセルに関連付けられたボディを破壊し、変更されたボディのみを再トレースする必要があります。これは、任意の TerPixel * を Box2D body * に関連付ける方法が必要であることを意味します。
ARC を使用した Objective-C では、私の知る限り、void * へのブリッジ キャストなしでは、Objective-C コンテナに C++ オブジェクト/ポインタを含めることはできません。問題は、これらの操作が信じられないほど高性能である必要があり、ブリッジのキャスティングに非常にコストがかかることです。また、すべての TerPixel に Box2D 本体へのポインターを含めたくありません。これは、ダングリング ポインターが存在しないことを確認し、ポインターを nil にするための無意味な反復を必要とする悪夢です。
これが物理境界を作成するための私のロジックです
-(void)createPhysicsBoundaries {
if ([self->traceBodyPoints count] == 0) {
CCLOG(@"createPhysicsBoundaries-> No bodies to trace");
return;
}
// NEED LOGIC HERE TO DELETE ALTERED BODIES
// NEED TO DELETE EXISTING BOX2D BODY AND RE-TRACE A NEW ONE
// For each body that has been altered, traverse linked list to trace the body
for (TerPixel * startPixel in self->traceBodyPoints) {
TerPixel * tracePixel = startPixel.nextBorderPixel;
b2BodyDef tDef;
tDef.position.Set(0, 0);
b2Body * b = self->world->CreateBody(&tDef);
self->groundBodies->push_back(b);
b->SetUserData((__bridge void*) self);
b2EdgeShape edgeShape;
CCLOG(@"StartPixel %d, %d", startPixel.x, startPixel.y);
while (tracePixel != startPixel) {
b2Vec2 start = b2Vec2(tracePixel.x/PTM_RATIO, tracePixel.y/PTM_RATIO);
//CCLOG(@"TracePixel BEFORE %d, %d", tracePixel.x, tracePixel.y);
tracePixel = tracePixel.nextBorderPixel;
//CCLOG(@"TracePixel AFTER %d, %d", tracePixel.x, tracePixel.y);
b2Vec2 end = b2Vec2(tracePixel.x/PTM_RATIO, tracePixel.y/PTM_RATIO);
edgeShape.Set(start,end);
b->CreateFixture(&edgeShape, 0);
} // end while
} // end for
} // end createPhysicsBoundaries
うまくいけば、これは理にかなっています。何が起こっているかを視覚的に知りたい場合は、こちらのビデオをご覧ください。http://www.youtube.com/watch?v=IUsgjYLr6e0&feature=youtu.be緑の境界は物理境界です。