SCNConstraint

我们在做应用开发的时候,也会用到约束,应用中的约束,就是当一个视图变化的时候,让和他之间有约束关系的其他视图,按照一定的约束规则变化,那在游戏中,我们的约束是用来干什么的?

官方的解释:约束能够根据你定义的规则,自动调整这些变化(位置 旋转 和 比例)

  • SCNConstraint

这个是游戏中的约束类,是一个抽象的类,我们不能直接使用,但是它有3个子类可以供我们使用。

我们看这个类有哪些属性

 /* 
  * 作用: 影响因子,决定约束的强度
  * 工作原理: 如果设置为1 那么在游戏每一帧渲染的时候,系统都会调整这个约束,如果你设置为0.5 在游戏的某些帧,系统不会进行约束调整 0 完全忽略约束
  * 注意 SCNTransformConstraint  对这类约束不起作用
  */
 var influenceFactor: CGFloat//默认值为 1

接下来我们分析三个子类

  • SCNLookAtConstraint

1.作用:让一个节点的方向,总是指向另外一个一个节点

2.怎么用?

我举个简单的例子,帮助大家理解它的用法 如果你想要玩第一视角的游戏,这是我们需要让摄像机捕捉到人物移动时的位置,这是需要给照相机节点添加一个SCNLookAtConstraint 类型的约束,就能实现这个效果。

3.原理

其实这个约束的原理是更改节点的transform的属性

4.怎么创建

 // target 就是指向的那个目标节点 
+ (instancetype)lookAtConstraintWithTarget:(SCNNode *)target;

5.我们如果想要照相机的视野保持在水平面上,也就是说只沿在Y轴转动跟随目标节点,我们应该怎么做呢?

// 设置下面的属性为YES,就能实现上面的效果,默认为NO
var gimbalLockEnabled: Bool
  • SCNTransformConstraint

1.作用:创建一个转换约束(提供给节点一个新的转换的计算),当系统进行下一次渲染的时候,会重新计算这个块中的约束,然后调整节点的状态

2.创建方法

 /*
  * world 设置为YES 使用世界坐标系,设置为NO 使用自身坐标系 
+ (instancetype)transformConstraintInWorldSpace:(BOOL)world  withBlock:(SCNMatrix4 (^)(SCNNode *node,  SCNMatrix4 transform))block
  • SCNIKConstraint(反向运动约束)

1.作用:将一个节点链移动到一个目标位置

给张图理解一下:

2.使用步骤

  • 创建一个节点链
  • 给根节点添加 SCNIKConstraint 约束对象(胳膊)
  • 添加约束給执行器(手)
  • 限定链式节点移动的范围
  • 设置目标位置,这个值可以动态的改变
  • 举个例子理解一下

比如机器人的组成身体 上臂 胳膊 和 手,身体是上臂的根节点,上臂是胳膊的根节点,胳膊是手的根节点,手是身体的根节点,如果我们要实现上面的约束的话,需要将约束的根节点设置为上臂,那我们把这个约束应该添加到手(执行)这个节点上去

  • 创建反向运动约束
- (instancetype)initWithChainRootNode:(SCNNode *)chainRoot  
+ inverseKinematicsConstraintWithChainRootNode:
  • 设置约束的最大旋转角度
- (void)setMaxAllowedRotationAngle:(CGFloat)angle forJoint:(SCNNode *)node

6.设置目标位置 var targetPosition: SCNVector3

import UIKit
import SceneKit

class Test16VC: UIViewController {

    var scnView:SCNView!
    var ikContrait:SCNIKConstraint!

    override func viewDidLoad() {
        super.viewDidLoad()

        scnView = SCNView(frame: view.bounds)
        scnView.backgroundColor = UIColor.black
        scnView.allowsCameraControl = true
        scnView.scene = SCNScene()
        scnView.scene?.physicsWorld.gravity = SCNVector3Make(0, 90, 0)
        view.addSubview(scnView)

        let cameraNode = SCNNode()
        cameraNode.camera = SCNCamera()
        cameraNode.camera?.automaticallyAdjustsZRange = true
        cameraNode.position = SCNVector3Make(0, 0, 500)
        scnView.scene?.rootNode.addChildNode(cameraNode)

        addArmToScene()
    }

    //添加机器手臂并设置约束
    func addArmToScene() {
        // 创建手掌
        let handNode = SCNNode()
        handNode.geometry = SCNBox(width: 20, height: 20, length: 20, chamferRadius: 0)
        handNode.geometry?.firstMaterial?.diffuse.contents = UIColor.purple
        handNode.position = SCNVector3Make(0, -50, 0)

        // 创建小手臂
        let lowerArm = SCNNode()
        lowerArm.geometry = SCNCylinder(radius: 1, height: 100)
        lowerArm.geometry?.firstMaterial?.diffuse.contents = UIColor.red
        lowerArm.position = SCNVector3Make(0, -50, 0)
        lowerArm.pivot = SCNMatrix4MakeTranslation(0, 50, 0)
        lowerArm.addChildNode(handNode)

        // 创建上臂
        let upperArm = SCNNode()
        upperArm.geometry = SCNCylinder(radius: 1, height: 100)
        upperArm.geometry?.firstMaterial?.diffuse.contents = UIColor.green
        upperArm.pivot = SCNMatrix4MakeTranslation(0, 50, 0)
        upperArm.addChildNode(lowerArm)

        // 创建控制点
        let controlNode = SCNNode()
        controlNode.geometry = SCNSphere(radius: 10)
        controlNode.geometry?.firstMaterial?.diffuse.contents = UIColor.blue
        controlNode.addChildNode(upperArm)
        controlNode.position = SCNVector3Make(0, 100, 0)

        // 添加到场景中去
        scnView.scene?.rootNode.addChildNode(controlNode)

        // 创建约束
        ikContrait = SCNIKConstraint.inverseKinematicsConstraint(chainRootNode: controlNode)
        handNode.constraints = [ikContrait]

        //添加一个手,每次点击屏幕,随机增加一个球
        let tap = UITapGestureRecognizer(target: self, action: #selector(tapHandler))
        scnView.addGestureRecognizer(tap)
    }

    @objc func tapHandler() {
        createNodeScene(scene: scnView.scene!, ikConstrait: ikContrait)
    }

    func createNodeScene(scene:SCNScene,ikConstrait:SCNIKConstraint) {
        let node = SCNNode()
        node.position = SCNVector3Make(Float(arc4random_uniform(100)), Float(arc4random_uniform(100)), Float(arc4random_uniform(100)))
        scene.rootNode.addChildNode(node)
        node.geometry = SCNSphere.init(radius: 10)
        node.geometry?.firstMaterial?.diffuse.contents = UIColor.init(red: CGFloat(arc4random_uniform(UInt32(255.0))) / 255.0, green: CGFloat(arc4random_uniform(UInt32(255.0))) / 255.0, blue: CGFloat(arc4random_uniform(UInt32(255.0))) / 255.0, alpha: 1)
        SCNTransaction.begin()
        SCNTransaction.animationDuration = 0.5
        ikContrait.targetPosition = node.position
        SCNTransaction.commit()
    }

}

results matching ""

    No results matching ""