我正在使用SpriteKit的碰撞检测。它有一个回调如下:
- (void)didBeginContact:(SKPhysicsContact *)contact
联系对象有两个物理实体:
SKPhysicsBody *bodyA;
SKPhysicsBody *bodyB;
我的游戏会有很多对象,当然我可以测试categoryBitMask
以找出与什么相撞的内容。但是考虑到我打算有很多种类(当然不超过32种)并且可能会动态地引入新类型,那么动态双重调度的最优雅方法是如何编写碰撞,爆炸,得分等逻辑从所有这些碰撞?当然,我可以建立一个巨大的毛茸茸的if语句,但我希望有更清洁的东西。
也许一个查找表存储适当处理程序的选择器?然后我通过categoryBitMasks
的某种组合索引查找表?我很乐意听到一些建议。
答案 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