c++ - 使用 Objective-C 和 C++ 避免并行数组

标签 c++ objective-c data-structures cocos2d-iphone box2d

一些背景:我正在为 Cocos2D/Box2D 编写一个 2D 可破坏地形库,它涉及 C++ 和 Objective-C 的混合体。我最近遇到了一个让我很困惑的情况,我想不出一个不涉及并行数组的解决方案。

为了在地形周围定义物理边界,必须对地形边界进行初始追踪。我进行了一个函数调用来跟踪纹理中所有孤立的像素体并缓存它们。这将创建以下数据结构

  • 一个 NSMutableDictionary“borderPixels”,其键等于 CGPoint 包裹在 NSValue 中,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 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其中绿色边界是物理边界。

最佳答案

engaging in bridge casting is very costly

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

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

你只需要一个这个数组的包装类,我们称之为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
}

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

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

关于c++ - 使用 Objective-C 和 C++ 避免并行数组,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17599343/

相关文章:

c# - .NET DLL 中的 System.DllNotFoundException

c++ - 如何在 Visual C++ 2010 中从 C++0x 启用 nullptr?

objective-c - 需要 SpriteKit 碰撞仅在第一次接触时发生

ios - 无法接收变更通知、键值观察、KVO模型-IOS

algorithm - 一种仅使用两个临时队列来反转队列的方法,仅此而已?

c++ - 将元素插入左倾的黑红树c++

c++ - 选择合适的 STL 容器来记录数据

c++ - 为什么要在方法的定义中使用 throw?

objective-c - UIImage 的 UICollectionView 滚动性能不佳

php - PHP的数组类型作为数据结构有什么特点?