使用SpriteKit进行碰撞处理的双重调度

时间:2013-11-09 19:59:56

标签: objective-c sprite-kit dispatch double-dispatch

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

- (void)didBeginContact:(SKPhysicsContact *)contact

联系对象有两个物理实体:

SKPhysicsBody *bodyA;
SKPhysicsBody *bodyB;

我的游戏会有很多对象,当然我可以测试categoryBitMask以找出与什么相撞的内容。但是考虑到我打算有很多种类(当然不超过32种)并且可能会动态地引入新类型,那么动态双重调度的最优雅方法是如何编写碰撞,爆炸,得分等逻辑从所有这些碰撞?当然,我可以建立一个巨大的毛茸茸的if语句,但我希望有更清洁的东西。

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

3 个答案:

答案 0 :(得分:2)

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

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

-(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];
        }
    }
}

答案 1 :(得分: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]];

}

VisitablePhysicsBody ContactVisitor 是执行双重调度所需的中间人,它们非常简单,源代码位于项目仓库中。它们最终允许您拥有仅与处理某些类型节点的联系人相关的类。

例如,在我的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

答案 2 :(得分: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哇,评论建议也是这样。 AW