ARKit系列文章目錄node
本文是Ray Wenderlich上《ARKit by Tutorials》的讀書筆記,主要講內容概要和讀後感 swift
本文中,咱們將經過一個Monster Truck小遊戲的例子,學習SceneKit中的一些特殊的物理效果.你必定不知道:SceneKit中內置了正宗的車輛物理效果! app
共三部分:車身(Body),車軸(Axle),車輪(Wheel) 框架
在SceneKit的編輯器中進行組裝: async
注意,車輪是經過一個軸來鏈接到真正的車軸(Axle)上的.編輯器
在代碼中加載這些部件:ide
// 1
let truckScene = SCNScene(
named: "MonsterTruck.scnassets/Models/MonsterTruck.scn")!
truckNode = truckScene.rootNode.childNode(
withName: "Truck", recursively: true)
wheelFLNode = truckScene.rootNode.childNode(
withName: "Wheel_FL", recursively: true)
wheelFRNode = truckScene.rootNode.childNode(
withName: "Wheel_FR", recursively: true)
wheelRLNode = truckScene.rootNode.childNode(
withName: "Wheel_RL", recursively: true)
wheelRRNode = truckScene.rootNode.childNode(
withName: "Wheel_RR", recursively: true)
// 2
truckNode.addChildNode(wheelFLNode!)
truckNode.addChildNode(wheelFRNode!)
truckNode.addChildNode(wheelRLNode!)
truckNode.addChildNode(wheelRRNode!)
// 3
truckNode.isHidden = true
sceneView.scene.rootNode.addChildNode(truckNode)
複製代碼
SceneKit中有專用的物理效果:post
SCNPhysicsVehicle
類型的.SCNPhysicsVehicle
類型的.選中Truch節點,作以下圖設置: 學習
定義一些常量,交賦值給SCNPhysicsVehicleWheel
節點.spa
let wheelRadius: CGFloat = 0.04
let wheelFrictionSlip: CGFloat = 0.9
let suspensionMaxTravel: CGFloat = 4.0
let suspensionMaxForce: CGFloat = 100
let suspensionRestLength: CGFloat = 0.08
let suspensionDamping: CGFloat = 2.0
let suspensionStiffness: CGFloat = 2.0
let suspensionCompression: CGFloat = 4.0
func createPhysicsVehicleWheel(wheelNode: SCNNode, position: SCNVector3) -> SCNPhysicsVehicleWheel {
let wheel = SCNPhysicsVehicleWheel(node: wheelNode)
wheel.connectionPosition = position
wheel.axle = SCNVector3(x: -1.0, y: 0, z: 0)
wheel.maximumSuspensionTravel = suspensionMaxTravel
wheel.maximumSuspensionForce = suspensionMaxForce
wheel.suspensionRestLength = suspensionRestLength
wheel.suspensionDamping = suspensionDamping
wheel.suspensionStiffness = suspensionStiffness
wheel.suspensionCompression = suspensionCompression
wheel.radius = wheelRadius
wheel.frictionSlip = wheelFrictionSlip
return wheel
}
複製代碼
各個常量的含義:
將車輪綁定在車身上,並使用物理效果:
func createVehiclePhysics() {
// 1
if physicsVehicle != nil {
sceneView.scene.physicsWorld.removeBehavior(physicsVehicle)
}
//2
let wheelFL = createPhysicsVehicleWheel(
wheelNode: wheelFLNode!,
position: SCNVector3(x: -0.07, y: 0.04, z: 0.06))
let wheelFR = createPhysicsVehicleWheel(
wheelNode: wheelFRNode!,
position: SCNVector3(x: 0.07, y: 0.04, z: 0.06))
let wheelRL = createPhysicsVehicleWheel(
wheelNode: wheelRLNode!,
position: SCNVector3(x: -0.07, y: 0.04, z: -0.06))
let wheelRR = createPhysicsVehicleWheel(
wheelNode: wheelRRNode!,
position: SCNVector3(x: 0.07, y: 0.04, z: -0.06))
// 3
physicsVehicle = SCNPhysicsVehicle(
chassisBody: truckNode.physicsBody!,
wheels: [wheelFL, wheelFR, wheelRL, wheelRR])
// 4
sceneView.scene.physicsWorld.addBehavior(physicsVehicle)
}
複製代碼
初始化時放置的位置,將車輛放在聚焦框focusNode的上面:
func updatePositions() {
// 1
self.truckNode.position = self.focusNode.position
self.truckNode.position.y += 0.20
// 2
self.truckNode.physicsBody?.velocity = SCNVector3Zero
self.truckNode.physicsBody?.angularVelocity = SCNVector4Zero
// 3
self.truckNode.physicsBody?.resetTransform()
}
複製代碼
此外,還有添加地面,設置遊戲狀態等.
咱們想讓用戶點擊屏幕時,加速前進,鬆開後緩慢減速
var maximumSpeed: CGFloat = 2.0
var isThrottling = false
var engineForce: CGFloat = 0
let defaultEngineForce: CGFloat = 10.0
var brakingForce: CGFloat = 0
let defaultBrakingForce: CGFloat = 0.01
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
isThrottling = true
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
isThrottling = false
}
func renderer(_ renderer: SCNSceneRenderer, updateAtTime time: TimeInterval) {
DispatchQueue.main.async {
self.updateStatus()
self.updateFocusNode()
self.updateVehiclePhysics()
}
}
func updateVehiclePhysics() {
// 1
guard self.gameState == .playGame else { return }
// 2
if isThrottling {
engineForce = defaultEngineForce
brakingForce = 0
} else {
engineForce = 0
brakingForce = defaultBrakingForce
}
// 3 apply方法是SCNPhysicsVehicle自帶的方法
physicsVehicle.applyEngineForce(engineForce, forWheelAt: 0)
physicsVehicle.applyEngineForce(engineForce, forWheelAt: 1)
physicsVehicle.applyEngineForce(engineForce, forWheelAt: 2)
physicsVehicle.applyEngineForce(engineForce, forWheelAt: 3)
physicsVehicle.applyBrakingForce(brakingForce, forWheelAt: 0)
physicsVehicle.applyBrakingForce(brakingForce, forWheelAt: 1)
physicsVehicle.applyBrakingForce(brakingForce, forWheelAt: 2)
physicsVehicle.applyBrakingForce(brakingForce, forWheelAt: 3)
// Limit Speed
if self.physicsVehicle.speedInKilometersPerHour >
CGFloat(maximumSpeed) {
engineForce = CGFloat(0.0)
}
}
複製代碼
能夠用CoreMotion框架來控制車輛方向.
let motionManager = CMMotionManager()
let steeringClamp: CGFloat = 0.6
var steeringAngle: CGFloat = 0
func updateSteeringAngle(acceleration: CMAcceleration) {
steeringAngle = (CGFloat)(acceleration.y)
if steeringAngle < -steeringClamp {
steeringAngle = -steeringClamp;
} else if steeringAngle > steeringClamp {
steeringAngle = steeringClamp;
}
}
func startAccelerometer() {
// 1
guard motionManager.isAccelerometerAvailable else { return }
// 2
motionManager.accelerometerUpdateInterval = 1/60.0
// 3
motionManager.startAccelerometerUpdates(
to: OperationQueue.main,
withHandler: { (accelerometerData: CMAccelerometerData?,
error: Error?) in
self.updateSteeringAngle(acceleration:
accelerometerData!.acceleration)
})
}
func stopAccelerometer() {
motionManager.stopAccelerometerUpdates()
}
複製代碼
此外,還要在updateVehiclePhysics()
方法中添加,才能使用,這也是系統自帶的處理車輛轉向的方法:
physicsVehicle.setSteeringAngle(steeringAngle, forWheelAt: 0)
physicsVehicle.setSteeringAngle(steeringAngle, forWheelAt: 1)
複製代碼
第五部分讀書筆記結束!請期待第二版的讀書筆記。
ARKit是在WWDC2017上推出的,2018春季更新了ARKit 1.5版本.《ARKit by Tutorials》初版寫做完成時,WWDC2018還未召開,所以初版書中內容較爲簡單,也沒有涉及到ARKit 2.0的新特性:世界地圖,圖片追蹤,3D物體檢測等. 後續第二版更新已在2018年秋季發佈,新增了兩章ARKit 2.0的Demo及講解,我會持續更新該讀書筆記系列.