碰撞以及如何检测碰撞详解

本节的学习目标

  • 如何设置两个物理之间碰撞,有如何让两个物体不能进行碰撞

  • 怎么能检测到两个物体进行了接触(注意是接触不是碰撞)


解析

碰撞以及如何检测碰撞详解_第1张图片
让学习成为一种习惯

首先确定一个问题: 是要用A去碰撞B 呢? 还是B 去碰撞A?

我用A去碰撞B 来讲解这个问题

能够实现物理碰撞的前提条件是什么?

两个物体都要有物理身体,我们知道游戏中物理身体有三种dynimic,static,kinematic

  • 第一个学习目标-怎么设置两个物体是否进行碰撞

节点A 和节点B 都设置了物理身体(SCNPhysicsBody),那么如图所示,SCNPhysicsBody 有三个属性如下

  1. categoryBitMask (分类掩码)
  2. collisionBitMask (碰撞掩码)
  3. 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);
     }
    

运行结果如下

碰撞以及如何检测碰撞详解_第2张图片
Scenekit_15.gif

为什么没碰撞呢?

sphereNode.physicsBody?.categoryBitMask = 1;
boxNode.physicsBody!.collisionBitMask = 0b010;

分析

1|0b10 = 0 所以不产生效果

如果改为下面的设置就会产生碰撞效果

sphereNode.physicsBody?.categoryBitMask = 0b10;
碰撞以及如何检测碰撞详解_第3张图片
Scenekit_15.gif
  • 如何设置接触检测呢?

设置正方体的允许那些物理身体接触自己发生代理事件

boxNode.physicsBody!.collisionBitMask = 0x01;

设置球体的属性掩码

sphereNode.physicsBody?.categoryBitMask=0x01;

命令行输出如下

' | no child>' ' | 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 }

物理世界的碰撞检测就这些内容了

你可能感兴趣的:(碰撞以及如何检测碰撞详解)