0

一些背景:我正在为 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 体。因此,当发生破坏一大块地形的事件时,我需要破坏与被破坏像素相关的主体,并仅回溯改变的主体。这意味着我需要一种将任何给定的 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其中绿色边界是物理边界。

4

1 回答 1

2

从事桥梁铸造是非常昂贵的

谁说的?它仍然只是一个演员表,基本上免费/可以忽略不计。桥接转移或保留转换会添加相应的引用计数方法调用,但您在这里不需要。

您的问题的解决方案非常简单。您有traceBodyPoints包含TerPixel该类实例的数组。

您只需要这个数组的包装类,我们称之为TerrainBlob。TerrainBlob 类包含作为属性的 NSMutableArraytraceBodyPoints和 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
}

现在只需要在一个位置更新body,每个TerPixel在使用前都需要检查body是否存在nil

最后,值得一提的是,基于像素的可破坏地形太过分了,尤其是在 Retina 设备上。除非您已经这样做了,否则请考虑创建跨越多个像素的近似线条形状,因为您实际上并不需要像素完美的物理模拟精度。

于 2013-07-11T18:07:20.007 回答