如何设置 SceneKit 碰撞检测

Posted

技术标签:

【中文标题】如何设置 SceneKit 碰撞检测【英文标题】:How to set up SceneKit collision detection 【发布时间】:2015-02-06 22:38:32 【问题描述】:

您好,我仔细阅读了文档,无法弄清楚如何在场景套件中设置碰撞检测。有人可以举个例子吗。 请帮助我非常渴望解决这个问题。 谢谢!

编辑: 你好非常感谢你,对不起,我忘了提到我的项目是在swift中。没什么大不了的,我大部分时间都可以翻译自己。

当对象相互碰撞和反弹时,我的 BitMask 工作正常。但是我似乎无法使该功能正常工作

func physicsWorld(world: SCNPhysicsWorld, didBeginContact contact: SCNPhysicsContact)
    let contactMask = contact.nodeA.physicsBody!.categoryBitMask | contact.nodeB.physicsBody!.categoryBitMask
    if (contactMask == (CollisionBallCategory | CollisionTerminatorCategory)) 
        println("Collided")
    

看着the documentation,我似乎需要以某种方式将场景物理世界委托分配给这个方法。我不知道该怎么做。

【问题讨论】:

在您编辑后,我为您的问题添加了一个语言标签 - 现在 Cocoa (Touch) 有两种主要语言,最好澄清您要问的是哪种语言。说到这一点,您也从未提及您的目标是哪个平台——SceneKit 在 ios 和 OS X 上都运行,尽管通常任何特定于 SceneKit 的东西对这两个平台都是通用的。 一旦您检查了文档和/或我更新的答案以确保您的碰撞委托设置正确,请在 physicsWorld:didBeginContact: 内粘贴 println。这样你就会知道你的问题是方法没有被调用还是你的 contactMask 逻辑不能正常工作。 【参考方案1】:

SceneKit 中碰撞检测的主要内容:

它基于位掩码,它们共同构成一个表格。 联系人代表是您应对冲突的方式。

使物体发生碰撞

例如,您可以用简单的英语陈述一些游戏设计,如下所示:

小行星相互撞击(并形成更小的小行星)。导弹应该相互穿过,但要摧毁火箭和小行星。火箭不应该对导弹做任何事情(反之亦然),但如果一个导弹离另一个或小行星太近,你就有一个严重的问题,你今天不会去太空。

通过碰撞检测实现这一点的第一步是根据哪些对交互来编写该设计。您可以使用表格执行此操作:

         | Missile | Rocket | Asteroid
--------------------------------------
Missile  | No      | Yes    | Yes
Rocket   | No      | Yes    | Yes
Asteroid | No      | No     | Yes

然后您可以将表的标题转换为一组类别常量,以供您的代码使用。

typedef NS_OPTIONS(NSUInteger, CollisionCategory) 
    CollisionCategoryMissile    = 1 << 0,
    CollisionCategoryRocket     = 1 << 1,
    CollisionCategoryAsteroid   = 1 << 2,
;

missile.physicsBody.categoryBitMask = CollisionCategoryMissile;
rocket.physicsBody.categoryBitMask = CollisionCategoryRocket;
asteroid.physicsBody.categoryBitMask = CollisionCategoryAsteroid;

对这些常量使用按位或来创建填充表格的collisionBitMask 值。

missile.physicsBody.collisionBitMask =
    CollisionCategoryRocket | CollisionCategoryAsteroid;
rocket.physicsBody.collisionBitMask =
    CollisionCategoryRocket | CollisionCategoryAsteroid;
asteroid.physicsBody.collisionBitMask = CollisionCategoryAsteroid;

这就是让 SceneKit 为您解决碰撞(即,将对象相互反弹)所需的全部内容。

响应碰撞

如果您还想收到碰撞通知(这样您就可以让导弹炸毁东西,然后让您的飞船撞上小行星以结束游戏),您需要在场景的物理世界中设置 contact delegate 并实现一个或多个在发生联系时调用的contact delegate methods。

在您的联系委托方法(例如,physicsWorld:didBeginContact:)中,您需要找出联系中涉及哪些类别的实体,以及哪些类别,这样您就可以访问您的代码,无论您的游戏如何为碰撞做:

- (void)physicsWorld:(SCNPhysicsWorld *)world didBeginContact:(SCNPhysicsContact *)contact

    CollisionCategory contactMask =
        contact.nodeA.physicsBody.categoryBitMask | contact.nodeB.physicsBody.categoryBitMask;

    // first, sort out what kind of collision
    if (contactMask == (CollisionCategoryMissile | CollisionCategoryRocket)) 
        // next, sort out which body is the missile and which is the rocket
        // and do something about it
        if (contact.nodeA.physicsBody.categoryBitMask == CollisionCategoryMissile) 
            [self hitRocket:contact.nodeB withMissile:contact.nodeA];
         else 
            [self hitRocket:contact.nodeA withMissile:contact.nodeB];
        
     else if (contactMask == (CollisionCategoryMissile | CollisionCategoryAsteroid)) 
        // ... and so on ...
    

将此代码放在您的一个类中(可能是一个视图控制器——无论您的游戏逻辑是否良好),并让该类声明符合SCNPhysicsContactDelegate 协议。

@interface ViewController: UIViewController <SCNPhysicsContactDelegate>

然后将该对象作为联系人代理分配给场景的物理世界:

// in initial setup, where presumably you already have a reference to your scene
scene.physicsWorld.contactDelegate = self

了解详情

SCNPhysicsBody 参考文档中有一点关于冲突解决的内容。 Apple 有一些使用碰撞检测的示例代码——它是 WWDC slides 和 demo 示例应用程序以及 vehicle physics 演示中的演示大杂烩的一部分。

除此之外,SceneKit 的碰撞处理模型与 SpriteKit 的几乎完全相同,因此 SpriteKit programming guide 中的几乎所有内容对于理解 SceneKit 中的相同内容也很有用。

【讨论】:

在“响应冲突”部分添加了一些内容,以更直接地解决您编辑的问题中的问题。将代码留在 ObjC 中(至少现在是这样)——如果您愿意阅读,我不必重写和重新测试它。 :) 实际上,考虑到 Swift 的当前状态,可以说更好使用 NS_OPTIONS 在 C 中定义您的碰撞类别枚举,然后将其导入到 Swift — 这会自动让您RawOptionSetType,您可以将按位运算符应用于其中,如果您在 Swift 中声明了枚举,则必须自己添加它们。 (然后在 Swift 中完成剩下的代码。) 非常感谢最后两行代码是我无法弄清楚的,谢谢! 嗨!我似乎再也找不到 Swift 4 中的 CollisionCategory 了。我们还有其他方法可以做到这一点吗? 仔细阅读答案。 CollisionCategory 不是 API,它是你自己定义的类型。

以上是关于如何设置 SceneKit 碰撞检测的主要内容,如果未能解决你的问题,请参考以下文章

在 ARkit 中未检测到 SceneKit 碰撞

如何检测精灵套件中的碰撞?

设置碰撞检测,然后在 2D 游戏中处理碰撞

如何使用Bullet物理引擎 碰撞检测

android 游戏 碰撞检测

如何在游戏循环中只检测一次碰撞而不是连续检测碰撞?