如何设置 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 碰撞检测的主要内容,如果未能解决你的问题,请参考以下文章