Some context: I am writing a 2D Destructible terrain library for Cocos2D/Box2D which involves a hybrid of C++ and Objective-C. I recently encountered a situation that has stumped me where I can't think of a solution that doesn't involve parallel arrays.
In order for the physics boundaries to be defined around the terrain, an initial trace must be made of the terrain border. I make a function call to trace all isolated bodies of pixels within a texture and cache them. This creates the following data structures
- An NSMutableDictionary "borderPixels" with the key being equal to an CGPoint wrapped in an NSValue which is equal to the pixel's unique location. This holds all traced pixels.
- A circular linked lists with TerPixels pointing to their next neighbor pixel
- An NSMutableArray "traceBodyPoints" which holds a single TerPixel representing a 'start' point of a terrain body. I only store TerPixel *'s here where I need to trace a physics body. So, if a terrain body has been modified, I insert any individual TerPixel * from the modified body into this array. I can then reference each of these and traverse the linked list to trace the physics body.
Here is some code to help paint a better picture of the situation:
-(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
Here is where I can't find a solution. I need to relate each TerPixel * in the traceBodyPoints array to a Box2D b2Body which will be created and added to the physics world. In my library, each isolated body of pixels within a texture corresponds to a Box2D body. So, when an event happens that destroys a chunk of the terrain, I need to destroy the body associated the the destroyed pixels and retrace ONLY the altered bodies. This means I need a way to associate any given TerPixel * to a Box2D body *.
In Objective-C with ARC, to my knowledge, I cannot include C++ objects/pointers in Objective-C containers without bridge casting to void *'s. Problem is these operations need to be incredibly performant and engaging in bridge casting is very costly. Also, I don't want to include a pointer to a Box2D body in every single TerPixel. This would be a nightmare to ensure there are no dangling pointers and require pointless iteration to nil pointers out.
Here is my logic for creating physics boundaries
-(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
Hopefully this makes sense. If you need a visual of what is happening, here is a video. http://www.youtube.com/watch?v=IUsgjYLr6e0&feature=youtu.be where the green boundaries are physics boundaries.