游戏中的各种力
- 力对物体产生作用有个前期条件那就是物体必须有物理身体(SCNPhysicsBody)
- 一个物体可能受到很多力的作用
- 如果力加到静态身体和运动身体上会产生什么影响?你应该自己去尝试。
- 力都能对那些类型的物体产生影响呢?怎么模拟龙卷风呢?
- 如何让所有物体失重呢?
你在学习一个东西的时候,应该想出很多问题,这样我们才能带着疑问探索,你会觉得很好玩。我们现在就去玩玩.
SCNPhysicsField
提示:这个类几乎包含了物理世界存在的各种力,我们要掌握它的属性的含义
控制力的强度(默认为1.0)
@property(nonatomic) CGFloat strength;
决定力的衰减指数(默认为0)
// 如果值不为0,力的计算公式是 (1 / distance ^ falloffExponent)
@property(nonatomic) CGFloat falloffExponent;
设置距离力中心点的最小不衰减距离,在这个范围内力不衰减(默认值为1e-6)
@property(nonatomic) CGFloat minimumDistance;
设置力的激活状态(默认为YES)
@property(nonatomic, getter=isActive) BOOL active;
阻止任何在它作用范围内的力(默认为NO)
@property(nonatomic, getter=isExclusive) BOOL exclusive;
决定力作用的范围
@property(nonatomic) SCNVector3 halfExtent;
决定作用的范围是个四方体还是一个球体(默认NO)
@property(nonatomic) BOOL usesEllipsoidalExtent;
决定力作用的范围是在指定的范围内,还是范围外
@property(nonatomic) SCNPhysicsFieldScope scope;
力的中心到影响范围的偏移
@property(nonatomic) SCNVector3 offset;
力的方向(默认为(0,-1,0))
// 注意它只对线性力有影响,比如重力
@property(nonatomic) SCNVector3 direction;
决定哪些节点可以被影响(高级用法,暂时不讲,当学习了碰撞检测之后,在悄悄告诉你)
@property(nonatomic) NSUInteger categoryBitMask NS_AVAILABLE(10_10, 8_0);
先提几个问题: 静态身体,动态身体,运动什么?那些可以添加速度?怎么添加速度? 拖拽力,能不能让静态的物体运动呢?
只有动态身体可以添加速度,静态身体和运动身体添加速度没有效果
// 创建拖拽力
+ (SCNPhysicsField *)dragField;
我们给动态身体添加一个(0,0,-1000)的速度,然后给它添加一个30的拖拽力。
SCNNode *drayFieldNode = [SCNNode node];
drayFieldNode.physicsField = [SCNPhysicsField dragField];
drayFieldNode.physicsField.strength = 30;
drayFieldNode.physicsField.direction = SCNVector3Make(0, -1, 0);
[boxNode addChildNode:drayFieldNode];
拖拽力没有方向,只有大小,主要是阻碍物体的运动,如果真要说方向朝向哪里,就是和物体运动方向相反,如果物体没有速度,拖拽力不会对物体产生影响。
创建一个围绕轴旋转的力
+ (SCNPhysicsField *)vortexField;
- (void)addVortexField{
SCNNode *vortexFieldNode = [SCNNode node];
vortexFieldNode.physicsField = [SCNPhysicsField vortexField];
vortexFieldNode.physicsField.strength = 1;
vortexFieldNode.physicsField.direction = SCNVector3Make(-1, 0, 0);
[self.scnView.scene.rootNode addChildNode:vortexFieldNode];
}
旋转力类似右手螺旋定则,设置的轴线方向为大拇指的指向的方向,手指环绕的方向才是力的方向。
创建朝向一个点的力
+ (SCNPhysicsField *)radialGravityField;
-(void)addRadialGravity{
SCNNode *radialGravityNode = [SCNNode node];
radialGravityNode.physicsField = [SCNPhysicsField radialGravityField];
radialGravityNode.physicsField.strength = -1000;
radialGravityNode.position = SCNVector3Make(0, 0, 0);
[self.scnView.scene.rootNode addChildNode:radialGravityNode];
}
可以设置位置,力朝向设置的位置。如果设置负值,力是朝向外边的。
线性力
+ (SCNPhysicsField *)linearGravityField;
-(void)addLineGravity{
SCNNode *lineGravityNode = [SCNNode node];
lineGravityNode.physicsField = [SCNPhysicsField linearGravityField];
lineGravityNode.physicsField.strength = 1;
lineGravityNode.physicsField.direction = SCNVector3Make(0, 0, -1);
[self.scnView.scene.rootNode addChildNode:lineGravityNode];
}
创建随机的力
// smoothness 噪点的平滑性 animationSpeed运动的速度
+ (SCNPhysicsField *)noiseFieldWithSmoothness:(CGFloat)smoothness animationSpeed:(CGFloat)speed;
-(void)addNoiseField{
SCNNode *noiseFieldNode = [SCNNode node];
noiseFieldNode.physicsField = [SCNPhysicsField noiseFieldWithSmoothness:0 animationSpeed:1];
noiseFieldNode.physicsField.strength = 5;
[self.scnView.scene.rootNode addChildNode:noiseFieldNode];
}
你知道用在什么地方吗?比如你想营造下雪的效果 或者 萤火虫效果,可以使用这个力
一种和速度成正比的随机力
+ (SCNPhysicsField *)turbulenceFieldWithSmoothness:(CGFloat)smoothness animationSpeed:(CGFloat)speed;
-(void)addTurbulenceField{
SCNNode *turbulenceFieldNode = [SCNNode node];
turbulenceFieldNode.physicsField = [SCNPhysicsField turbulenceFieldWithSmoothness:0 animationSpeed:1];
turbulenceFieldNode.physicsField.strength = 5;
[self.scnView.scene.rootNode addChildNode:turbulenceFieldNode];
}
弹性力(胡克定律)
+ (SCNPhysicsField *)springField;
SCNNode *springField = [SCNNode node];
springField.physicsField = [SCNPhysicsField springField];
springField.physicsField.strength = 0.01;
springField.position = SCNVector3Make(0, 30, 0);
[self.scnView.scene.rootNode addChildNode:springField];
创建这个力,需要设置力的位置和力的大小
创建电场
+ (SCNPhysicsField *)electricField;
提示:这种力的大小,取决于物体带的电荷的多少和距离磁场的距离 放向取决于电荷的正负。
-(void)addElectricField{
SCNNode *electricFieldNode = [SCNNode node];
electricFieldNode.physicsField = [SCNPhysicsField electricField];
electricFieldNode.physicsField.strength = 10;
[self.scnView.scene.rootNode addChildNode:electricFieldNode];
}
提示:
电场默认的属性是正的
如何创建带电荷的节点对象呢?给段代码自己看看,后面会专门讲解
SCNNode *boxNode = [SCNNode node];
boxNode.geometry = [SCNBox boxWithWidth:4 height:4 length:4 chamferRadius:0];
boxNode.geometry.firstMaterial.diffuse.contents = [UIColor redColor];
boxNode.physicsBody = [SCNPhysicsBody dynamicBody];
boxNode.position = SCNVector3Make(0, 30, 0);
boxNode.physicsBody.velocity = SCNVector3Make(0, 0, 0);
[self.scnView.scene.rootNode addChildNode:boxNode];
boxNode.physicsBody.charge = -10; // 创建电荷正负和大小
创建磁场
+ (SCNPhysicsField *)magneticField;
- (void)addMagneticField{
SCNNode *magneticFielddNode = [SCNNode node];
magneticFielddNode.physicsField = [SCNPhysicsField magneticField];
magneticFielddNode.physicsField.strength = -0.5;
[self.scnView.scene.rootNode addChildNode:magneticFielddNode];
}
吸引或者排斥物体,取决于电荷的大小,正负,速度,距离等因素
自定义力
typedef SCNVector3 (^SCNFieldForceEvaluator)(SCNVector3 position, SCNVector3 velocity, float mass, float charge, NSTimeInterval time);
import UIKit
import SceneKit
class Test17VC: UIViewController {
var scnView:SCNView!
var floorNode:SCNNode!
override func viewDidLoad() {
super.viewDidLoad()
scnView = SCNView(frame: self.view.bounds)
scnView.backgroundColor = UIColor.black
scnView.scene = SCNScene()
self.view.addSubview(scnView)
let cameraNode = SCNNode()
cameraNode.name = "cameraNode"
cameraNode.camera = SCNCamera()
cameraNode.position = SCNVector3Make(0, 30, 30)
cameraNode.rotation = SCNVector4Make(1, 0, 0, Float(-Double.pi/6))
scnView.scene?.rootNode.addChildNode(cameraNode)
floorNode = SCNNode()
floorNode.name = "floorNode"
floorNode.geometry = SCNFloor()
floorNode.geometry?.firstMaterial?.diffuse.contents = "art.scnassets/1.jpg"
floorNode.physicsBody = SCNPhysicsBody(type: .static, shape: SCNPhysicsShape(geometry: SCNFloor(), options: nil))
scnView.scene?.rootNode.addChildNode(floorNode)
let segment = UISegmentedControl(items: ["拖拽","围绕轴旋转","朝一个点的力","线性力","随机力","漂落","弹力","电场"])
segment.frame = CGRect(x: 10, y: view.bounds.height - 44, width: view.bounds.width - 20, height: 34)
segment.addTarget(self, action: #selector(displayChange), for: .valueChanged)
segment.selectedSegmentIndex = 0
view.addSubview(segment)
}
@objc func displayChange(sender:UISegmentedControl) {
for node in (scnView.scene?.rootNode.childNodes)! {
if node.name != "cameraNode" && node.name != "floorNode" {
node.removeFromParentNode()
}
}
navigationController?.title = sender.titleForSegment(at: sender.selectedSegmentIndex)
switch sender.selectedSegmentIndex {
case 0:
addDragField()
case 1:
addVortexField()
case 2:
addRadialGravity()
case 3:
addLineBox()
case 4:
addNoiseField()
case 5:
addTurbulenceField()
case 6:
addSpringField()
case 7:
addElectricField()
default:
break
}
}
//拖拽
func addDragField() {
let boxNode = createBox()
scnView.scene?.rootNode.addChildNode(boxNode)
let drayFieldNode = SCNNode()
drayFieldNode.physicsField = SCNPhysicsField.drag()
drayFieldNode.physicsField?.strength = 30
drayFieldNode.physicsField?.direction = SCNVector3Make(0, -1000, 0)
boxNode.addChildNode(drayFieldNode)
}
//围绕轴旋转
func addVortexField() {
let boxNode = createBox()
scnView.scene?.rootNode.addChildNode(boxNode)
let vortexFieldNode = SCNNode()
vortexFieldNode.physicsField = SCNPhysicsField.vortex()
vortexFieldNode.physicsField?.strength = 1
vortexFieldNode.physicsField?.direction = SCNVector3Make(-1, 0, 0)
scnView.scene?.rootNode.addChildNode(vortexFieldNode)
}
//朝一个点的力
func addRadialGravity() {
let boxNode = createBox()
scnView.scene?.rootNode.addChildNode(boxNode)
let radialGravityNode = SCNNode()
radialGravityNode.physicsField = SCNPhysicsField.radialGravity()
radialGravityNode.physicsField?.strength = 1000
radialGravityNode.position = SCNVector3Make(0, 0, 0)
scnView.scene?.rootNode.addChildNode(radialGravityNode)
}
//线性力
func addLineBox() {
let boxNode = createBox()
scnView.scene?.rootNode.addChildNode(boxNode)
let lineGravityNode = SCNNode()
lineGravityNode.physicsField = SCNPhysicsField.linearGravity()
lineGravityNode.physicsField?.strength = 100
lineGravityNode.physicsField?.direction = SCNVector3Make(0, 0, -1)
scnView.scene?.rootNode.addChildNode(lineGravityNode)
}
//随机力
func addNoiseField() {
let boxNode = createBox()
scnView.scene?.rootNode.addChildNode(boxNode)
let noiseFieldNode = SCNNode()
noiseFieldNode.physicsField = SCNPhysicsField.noiseField(smoothness: 0, animationSpeed: 100)
noiseFieldNode.physicsField?.strength = 100
scnView.scene?.rootNode.addChildNode(noiseFieldNode)
}
//漂落
func addTurbulenceField() {
for _ in 0..<10 {
let boxNode = SCNNode(geometry: SCNSphere(radius: 1))
boxNode.geometry?.firstMaterial?.diffuse.contents = "art.scnassets/earth/sun.jpg"
boxNode.physicsBody = SCNPhysicsBody(type: .dynamic, shape: SCNPhysicsShape(geometry: SCNBox(width: 5, height: 5, length: 5, chamferRadius: 0), options: nil))
boxNode.position = SCNVector3Make(Float(arc4random_uniform(10)), Float(arc4random_uniform(20) + 20), 1)
scnView.scene?.rootNode.addChildNode(boxNode)
}
let turbulenceFieldNode = SCNNode()
turbulenceFieldNode.physicsField = SCNPhysicsField.turbulenceField(smoothness: 0, animationSpeed: 1)
turbulenceFieldNode.physicsField?.strength = 5
scnView.scene?.rootNode.addChildNode(turbulenceFieldNode)
}
//弹力
func addSpringField() {
let boxNode = createBox()
scnView.scene?.rootNode.addChildNode(boxNode)
let springField = SCNNode()
springField.physicsField = SCNPhysicsField.spring()
springField.physicsField?.strength = 5
springField.position = SCNVector3Make(0, 30, 0)
scnView.scene?.rootNode.addChildNode(springField)
}
//电场
func addElectricField() {
let tube = SCNTube(innerRadius: 1, outerRadius: 1.2, height: 4)
tube.firstMaterial?.diffuse.contents = "art.scnassets/earth/moon.jpg"
let tubeNode = SCNNode(geometry: tube)
tubeNode.physicsBody = SCNPhysicsBody(type: .kinematic, shape: SCNPhysicsShape(geometry: SCNTube(innerRadius: 1, outerRadius: 1.2, height: 4), options: nil))
tubeNode.position = SCNVector3Make(-5, 2, 0)
scnView.scene?.rootNode.addChildNode(tubeNode)
let tubeNode1 = SCNNode(geometry: SCNTube(innerRadius: 4.5, outerRadius: 5, height: 2))
tubeNode1.geometry?.firstMaterial?.diffuse.contents = "art.scnassets/earth/moon.jpg"
tubeNode1.position = SCNVector3Make(6, 1, 0)
scnView.scene?.rootNode.addChildNode(tubeNode1)
let particleSystem = SCNParticleSystem(named: "art.scnassets/particle/fireParticle.scnp", inDirectory: nil)
particleSystem?.colliderNodes = [tubeNode1,floorNode]
particleSystem?.isAffectedByPhysicsFields = true
particleSystem?.particleCharge = 10
let particleNode = SCNNode()
particleNode.position = SCNVector3Make(0, 0, 0)
particleNode.addParticleSystem(particleSystem!)
particleNode.physicsBody = SCNPhysicsBody()
particleNode.physicsBody?.type = .dynamic
tubeNode.addChildNode(particleNode)
let electricFieldNode = SCNNode()
electricFieldNode.physicsField = SCNPhysicsField.electric()
electricFieldNode.physicsField?.strength = -10
tubeNode1.addChildNode(electricFieldNode)
}
func createBox() -> SCNNode {
let boxNode = SCNNode(geometry: SCNBox(width: 5, height: 5, length: 5, chamferRadius: 0))
boxNode.geometry?.firstMaterial?.diffuse.contents = "art.scnassets/2.jpg"
boxNode.position = SCNVector3Make(0, 2.5, 8)
boxNode.physicsBody = SCNPhysicsBody(type: .dynamic, shape: SCNPhysicsShape(geometry: SCNBox(width: 5, height: 5, length: 5, chamferRadius: 0), options: nil))
return boxNode
}
}