一些上下文:我正在为Cocos2D / Box2D写一个2D可破坏地形库,其中涉及C ++和Objective-C的混合体。我最近遇到了一种让我感到困惑的情况,我无法想到不涉及并行数组的解决方案。
为了在地形周围定义物理边界,必须对地形边界进行初始跟踪。我进行了一个函数调用,以跟踪纹理中所有孤立的像素体并将其缓存。这将创建以下数据结构
一个NSMutableDictionary“ borderPixels”,其密钥等于包装在NSValue中的CGPoint,该NSValue等于像素的唯一位置。这将保留所有跟踪的像素。
圆形链接列表,其中TerPixels指向其下一个相邻像素
NSMutableArray“ traceBodyPoints”包含一个表示地形物体“起点”的TerPixel。我只在这里存储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 b2Body将被创建并添加到物理世界中。在我的库中,纹理内每个孤立的像素主体都对应一个Box2D主体。因此,当发生破坏大块地形的事件时,我需要破坏与被破坏像素相关的物体,并且仅回溯已改变的物体。这意味着我需要一种将任何给定的TerPixel *与Box2D主体*关联的方法。
据我所知,在带有ARC的Objective-C中,如果没有将桥强制转换为void *的情况,就无法在Objective-C容器中包含C ++对象/指针。问题在于这些操作需要非常出色地执行,并且进行桥梁浇铸的成本非常高。另外,我不想在每个TerPixel中都包含一个Box2D主体的指针。确保没有悬空的指针并且需要无意义的迭代以使指针变为零将是一场噩梦。
这是我创建物理边界的逻辑
-(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其中绿色边界是物理边界。
最佳答案
从事桥梁铸造非常昂贵
谁说的?它仍然只是演员,基本上是免费的/微不足道的。桥转移或保留强制转换会添加相应的引用计数方法调用,但是您在这里不需要。
解决您的问题的方法非常简单。您具有包含traceBodyPoints
类实例的TerPixel
数组。
您只需要此数组的包装器类,就称它为TerrainBlob
。 TerrainBlob类包含NSMutableArray traceBodyPoints
属性和b2Body的另一个属性:
@interface TerrainBlob : NSObject
@property NSMutableArray* traceBodyPoints;
@property b2Body* body;
@end
您可以改进TerPixel以包含对TerrainBlob的反向引用,该引用必须很弱以避免保留循环。这样,每个像素都可以访问b2Body。您还可以在TerrainBlob中添加一个方法,该方法可以添加TerPixel并正确设置terrainBlob属性,以方便使用。
@interface TerPixel : NSObject
...
@property (weak) TerrainBlob* terrainBlob;
@end
然后,您可以从TerPixel中访问b2Body:
b2Body* body = _terrainBlob.body;
if (body)
{
// do something with body
}
现在,您只需要在一个位置更新主体,并且每个TerPixel都需要在使用主体之前检查主体是否为
nil
。最后,值得一提的是,以像素为基础的可破坏地形是过大的,尤其是在Retina设备上。除非您已经这样做,否则请考虑创建跨越多个像素的近似线形,因为对于物理模拟,您实际上并不需要完美的像素精度。