碰撞以及如何检测碰撞详解
本节的学习目标
如何设置两个物理之间碰撞,有如何让两个物体不能进行碰撞
怎么能检测到两个物体进行了接触(注意是接触不是碰撞)
解析
首先确定一个问题: 是要用A去碰撞B 呢? 还是B 去碰撞A?
我用A去碰撞B 来讲解这个问题
能够实现物理碰撞的前提条件是什么?
两个物体都要有物理身体,我们知道游戏中物理身体有三种dynimic,static,kinematic
- 第一个学习目标-怎么设置两个物体是否进行碰撞
节点A 和节点B 都设置了物理身体(SCNPhysicsBody),那么如图所示,SCNPhysicsBody 有三个属性如下
- categoryBitMask (分类掩码)
- collisionBitMask (碰撞掩码)
- contactTestBitMask (接触检测掩码)
解释一下前两个属性的作用
categoryBitMask (分类掩码)表示的物理身体的类别,如果是游戏的话,加入这个节点属于飞机 我们可以给飞机设置一个类别掩码 0b001 (最大值为15位)
collisionBitMask(碰撞掩码) 表示节点的物体身体允许被那些分类的物理身体碰撞 0b101
A 要去碰撞B, 如果要产生碰撞效果应该怎么设置呢?
ANode.physicsBody.categoryBitMask = 0b001;
BNode.physicsBody.collisionBitMask = 0b011; // b允许那些分类与自己碰撞
如果两个掩码进行按位与运算 结果为一个非零的数字 就会产生碰撞
如果是下面的设置将不会产生碰撞
ANode.physicsBody.categoryBitMask = 0b001;
BNode.physicsBody.collisionBitMask = 0b110;
提示:
注意千万不要将两者的顺序搞混了,有点绕
- 第二个学习目标-如何实现接触检测
注意一个词语'接触检测'不是碰撞检测哦!就是说两个物体是否碰撞与能否检测到接触没有关系
先来看一个代理,这个代理是物理世界的一个属性
scnView.scene?.physicsWorld.contactDelegate = self
代理SCNPhysicsContactDelegate 有三个方法
// 开始接触
optional public func physicsWorld(_ world: SCNPhysicsWorld, didBegin contact: SCNPhysicsContact)
// 接触中
optional public func physicsWorld(_ world: SCNPhysicsWorld, didUpdate contact: SCNPhysicsContact)
// 接触结束
optional public func physicsWorld(_ world: SCNPhysicsWorld, didEnd contact: SCNPhysicsContact)
如果两个物体满足接触条件就会触发这个代理事件
怎么才能让其满足这个接触条件呢?
1.首先要设置物理身体(同上面一样)
如果要想A去接触B,并且要触发代理事件
ANode.physicsBody.categoryBitMask = 0b001;
BNode.physicsBody.contactTestBitMask = 0b101;// 允许分类掩码的对象和自己发生接触时触发回调函数
这样A 在受到力的时候,去接触B 就会去触发代理事件
完整的示例演示
第一步 创建工程(略)
-
第二步 创建一个SCNView 的视图
var scnView:SCNView = SCNView(frame: UIScreen.main.bounds)
-
第三步 创建一个游戏场景
var scene = SCNScene() scnView.scene = scene
-
第四步 创建一个地板
var floorNode = SCNNode() floorNode.geometry = SCNFloor() scene.rootNode.addChildNode(floorNode);
-
第五步 创建一个正方体B
let boxNode = SCNNode() boxNode.geometry = SCNBox(width: 10, height: 10, length: 10, chamferRadius: 0) boxNode.geometry?.firstMaterial?.diffuse.contents = UIColor.red boxNode.categoryBitMask = 1; boxNode.position = SCNVector3Make(0, 5, -20) boxNode.physicsBody = SCNPhysicsBody.static() // 设置碰撞掩码和接触掩码 boxNode.physicsBody!.contactTestBitMask = 0b111; boxNode.physicsBody!.collisionBitMask = 0b010; scene.rootNode.addChildNode(boxNode);
-
第六步 创建球体 A
let sphereNode = SCNNode() sphereNode.geometry = SCNSphere(radius: 1) sphereNode.geometry?.firstMaterial?.diffuse.contents = UIColor.green scene.rootNode.addChildNode(sphereNode); sphereNode.position = SCNVector3Make(0, 20, -20) sphereNode.physicsBody = SCNPhysicsBody.dynamic() sphereNode.physicsBody?.categoryBitMask = 1;
-
第七步 实现代理
scnView.scene?.physicsWorld.contactDelegate = self func physicsWorld(_ world: SCNPhysicsWorld, didBegin contact: SCNPhysicsContact) { print(contact); } func physicsWorld(_ world: SCNPhysicsWorld, didUpdate contact: SCNPhysicsContact) { print(contact); } func physicsWorld(_ world: SCNPhysicsWorld, didEnd contact: SCNPhysicsContact) { print(contact); }
运行结果如下
为什么没碰撞呢?
sphereNode.physicsBody?.categoryBitMask = 1;
boxNode.physicsBody!.collisionBitMask = 0b010;
分析
1|0b10 = 0 所以不产生效果
如果改为下面的设置就会产生碰撞效果
sphereNode.physicsBody?.categoryBitMask = 0b10;
- 如何设置接触检测呢?
设置正方体的允许那些物理身体接触自己发生代理事件
boxNode.physicsBody!.collisionBitMask = 0x01;
设置球体的属性掩码
sphereNode.physicsBody?.categoryBitMask=0x01;
命令行输出如下
'<SCNNode: 0x6080003c1a40 pos(0.000000 5.000000 -20.000000) | geometry=<SCNBox: 0x60800018ea00 | width=10.000 height=10.000 length=10.000 chamferRadius=0.000> | no child>' '<SCNNode: 0x6080003c1b30 pos(0.000000 20.000000 -20.000000) | geometry=<SCNSphere: 0x6000001448e0 | radius=1.000> | no child>' point(0.000000 9.949695 -20.000000) normal(0.000000 -1.000000 0.000000) impulse(0.000000) distance:0.050305>
- SCNPhysicsContact 对象
属性如下
// 被碰撞的节点
open var nodeA: SCNNode { get }
// 主动碰撞的物体 这里指的是上面例子的球体
open var nodeB: SCNNode { get }
// 碰撞点的世界坐标
open var contactPoint: SCNVector3 { get }
// 碰撞点的法线
open var contactNormal: SCNVector3 { get }
// 碰撞的力度
open var collisionImpulse: CGFloat { get } // the collision impulse on nodeA
// 离世界坐标点的距离
open var penetrationDistance: CGFloat { get }
物理世界的碰撞检测就这些内容了