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()
}
}