3

我正在使用 SpriteKit 的碰撞检测。它有一个如下所示的回调:

- (void)didBeginContact:(SKPhysicsContact *)contact

接触对象有两个物理体:

SKPhysicsBody *bodyA;
SKPhysicsBody *bodyB;

我的游戏会有很多对象,当然我可以测试一下categoryBitMask,看看什么和什么发生了碰撞。但是考虑到我打算有很多种类(当然不超过 32 种)并且可能会动态引入新类型,那么进行动态双重调度以编码碰撞、爆炸、得分等逻辑的最优雅的方法是什么?从所有这些碰撞中?当然,我可以构建一个巨大的毛茸茸的 if 语句,但我希望有更干净的东西。

也许一个查找表存储适当的处理程序的选择器?然后我通过某种组合来索引查找表categoryBitMasks?我很想听听一些建议。

4

3 回答 3

2

以下是Kobold Kit中联系人调度的工作方式。

它的要点:您向每个联系节点发送一条消息didBeginContact:withOtherBody:,以便每个节点自己知道它与哪个其他主体建立或失去联系。如果您需要其他主体的节点,可以从 SKPhysicsBodynode属性中获取。

-(void) didBeginContact:(SKPhysicsContact *)contact
{
    SKPhysicsBody* bodyA = contact.bodyA;
    SKPhysicsBody* bodyB = contact.bodyB;
    SKNode* nodeA = bodyA.node;
    SKNode* nodeB = bodyB.node;
    for (id<KKPhysicsContactEventDelegate> observer in _physicsContactObservers)
    {
        SKNode* observerNode = observer.node;
        if (observerNode == nodeA)
        {
            [observer didBeginContact:contact otherBody:bodyB];
        }
        else if (observerNode == nodeB)
        {
            [observer didBeginContact:contact otherBody:bodyA];
        }
    }
}

-(void) didEndContact:(SKPhysicsContact *)contact
{
    SKPhysicsBody* bodyA = contact.bodyA;
    SKPhysicsBody* bodyB = contact.bodyB;
    SKNode* nodeA = bodyA.node;
    SKNode* nodeB = bodyB.node;
    for (id<KKPhysicsContactEventDelegate> observer in _physicsContactObservers)
    {
        SKNode* observerNode = observer.node;
        if (observerNode == nodeA)
        {
            [observer didEndContact:contact otherBody:bodyB];
        }
        else if (observerNode == nodeB)
        {
            [observer didEndContact:contact otherBody:bodyA];
        }
    }
}
于 2013-11-09T21:39:14.037 回答
2

我已经使用 Pong 作为我选择的游戏为 SkPhysicsBodyContact 创建了一个双重调度的工作示例。工作代码可在我的 github 上找到。

https://github.com/kouky/iOS-SpriteKit-Pong

您实际上需要使用访问者模式在您的联系人委托中执行双重调度,因为在 Objective-C 中我们不能重载类方法参数。

- (void)didBeginContact:(SKPhysicsContact *)contact
{

    SKPhysicsBody *firstBody, *secondBody;
    firstBody = contact.bodyA;
    secondBody = contact.bodyB;

    VisitablePhysicsBody *firstVisitableBody = [[VisitablePhysicsBody alloc] initWithBody:firstBody];
    VisitablePhysicsBody *secondVisitableBody = [[VisitablePhysicsBody alloc] initWithBody:secondBody];

    [firstVisitableBody acceptVisitor:[ContactVisitor contactVisitorWithBody:secondBody forContact:contact]];
    [secondVisitableBody acceptVisitor:[ContactVisitor contactVisitorWithBody:firstBody forContact:contact]];

}

VisitablePhysicsBodyContactVisitor是执行双重调度所需的中间人,它们非常简单,源代码在项目 repo 中。它们最终允许您拥有仅关注处理某些类型节点的联系人的类。

例如,在我的 Pong 示例中,有一个名为BallNodeContactVisitor的类,它仅在出现涉及BallNode的联系人时接收消息。类中有一些遵循命名约定的方法,允许我们确定BallNode与其他节点类型(如PaddleNode )接触的结果。

@implementation BallNodeContactVisitor

// Handles contacts with PlayfieldScene edges
- (void)visitPlayfieldScene:(SKPhysicsBody *)playfieldBody
{

    BallNode *ball = (BallNode *) self.body.node;
    PlayfieldScene *playfield = (PlayfieldScene *) playfieldBody.node;
    // Perform something
}

// Handles contacts with PaddleNodes
- (void)visitPaddleNode:(SKPhysicsBody *)paddleBody
{
    BallNode *ball = (BallNode *) self.body.node;
    PaddleNode *paddle= (PaddleNode *) paddleBody.node;
    // Perform something else
}

@end
于 2013-11-21T13:11:37.670 回答
0

为什么不检查 SKNode 子类

然后,您可以轻松检查联系的body.node课程。然后创建一个配置字典,为您提供调用什么方法。

一些节点子类...

Player : SKSpriteNode
Bullet : SKSpriteNode
Monster : SKSpriteNode

...然后创建方法...

-(void)player:(Player*) player didBeginContactWithMonster:(Monster*) monster;
-(void)player:(Player*) player didBeginContactWithBullet:(Bullet*) bullet;

...然后创建一个配置,如...

NSDictionary *contactDispatch = @{
@"player:didBeginContactWithMonster:" : @[ [Player class], [Mosnter class] ],
@"player:didBeginContactWithBullet:" : @[ [Player class], [Bullet class] ]
};

因此,您可以检查接触体的包含情况,然后使用 实例化选择器NSSelectorFromString,然后使用 调用它performSelector:withObject:withObject:

它可能会以多种方式进行优化、规范化,但我觉得它是一种干净的解决方案。还没打样,刚遇到。

只是注意到您在问题本身的末尾写了几乎相同的内容。无论如何都会发布它。:D 哇,评论建议也一样。哦。

于 2014-02-19T02:46:24.847 回答