ARKit series of articles directory

Ray Wenderlich’s ARKit by Tutorials is a summary of his reading notes

In this article, we’ll learn about some of the special physics in SceneKit using an example of a Monster Truck mini-game. What you didn’t know :SceneKit has real vehicle physics built in!

The basic structure

There are three parts: Body, Axle and Wheel.

Assemble it in the SceneKit editor:

Note that the wheels are attached to the real Axle by a Axle.

Load these parts in the code:

/ / 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)
Copy the code

Added vehicle physics

SceneKit has dedicated physics effects:

  • SCNPhysicsVehicle: Makes a standard physical vehicle behave like a car.
  • SCNPhysicsBody: The standard physical form under normal circumstances. It’s used on things like car bodies. This type of shape will be during the creation processSCNPhysicsVehicleType.
  • SCNPhysicsVehicleWheel: a dedicated physical body that simulates not only the behavior of the wheel, but also the appearance and other physical properties. This type of shape will be during the creation processSCNPhysicsVehicleType.

Select Truch node and make the Settings as shown below:

Create wheel physics

Define some constants and assign them to the SCNPhysicsVehicleWheel node.

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
}
Copy the code

The meaning of each constant:

  • Wheel Radius: indicates the actual Radius of the physical appearance of a Wheel.
  • Wheel Friction Slip: Specifies the Friction force between the Wheel itself and the contact surface.
  • Suspension Maximum Travel Defines the Maximum Travel allowed by the wheel along the connection point. The units are centimeters.
  • Suspension Maximum Force: defines the Maximum Force of the Suspension wheels and body. It’s in Newtons.
  • Suspension Rest Length: Defines the Length of the Suspension at Rest. It’s in meters.
  • Suspension Damping: Defines the Damping coefficient of the Suspension during oscillation.
  • Define the spring coefficient between the wheel itself and the chassis of the vehicle.
  • Suspension Compression: defines the speed at which the Suspension returns to the resting state after Compression.

Full vehicle physics

Attach the wheels to the body of the car and use physics:

func createVehiclePhysics(a) {
  / / 1
  ifphysicsVehicle ! =nil {
    sceneView.scene.physicsWorld.removeBehavior(physicsVehicle)
  }
  / / 2
  letwheelFL = createPhysicsVehicleWheel( wheelNode: wheelFLNode! , position:SCNVector3(x: -0.07, y: 0.04, z: 0.06))
  letwheelFR = createPhysicsVehicleWheel( wheelNode: wheelFRNode! , position:SCNVector3(x: 0.07, y: 0.04, z: 0.06))
  letwheelRL = createPhysicsVehicleWheel( wheelNode: wheelRLNode! , position:SCNVector3(x: -0.07, y: 0.04, z: -0.06))
  letwheelRR = 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)
}
Copy the code

Place the vehicle on top of the focusNode in the focus frame:

func updatePositions(a) {
  / / 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() }Copy the code

In addition, there are adding ground, setting up the game state and so on.

Add the engine force

We want the user to accelerate when they click on the screen and slow down when they release it

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(a) {
  / / 1
  guard self.gameState == .playGame else { return }
  / / 2
  if isThrottling {
   engineForce = defaultEngineForce
   brakingForce = 0
 } else {
   engineForce = 0
   brakingForce = defaultBrakingForce
 }
  // 3 The apply method is provided by 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)}}Copy the code

Control the direction

You can use the CoreMotion frame to control the direction of the vehicle.

let motionManager = CMMotionManager(a)let steeringClamp: CGFloat = 0.6
var steeringAngle: CGFloat = 0


func updateSteeringAngle(acceleration: CMAcceleration) {
  steeringAngle = (CGFloat)(acceleration.y)
  
  if steeringAngle < -steeringClamp {
    steeringAngle = -steeringClamp;
  } else ifsteeringAngle > steeringClamp { steeringAngle = steeringClamp; }}func startAccelerometer(a) {
  / / 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(a) {
  motionManager.stopAccelerometerUpdates()
}
Copy the code

In addition, you need to add it to the updateVehiclePhysics() method, which is also the system’s own way of handling vehicle steering:

physicsVehicle.setSteeringAngle(steeringAngle, forWheelAt: 0)
physicsVehicle.setSteeringAngle(steeringAngle, forWheelAt: 1)
Copy the code

Part five Reading notes end! Look forward to the second edition of the reading notes.

ARKit was launched at WWDC2017, and spring 2018 saw version 1.5 of ARKit. The first edition of ARKit by Tutorials was written before WWDC2018, so the first edition of ARKit by Tutorials was relatively simple and did not mention ARKit New 2.0 features: world maps, image tracking,3D object detection, etc. The second update was released in fall 2018, adding two chapters of ARKit 2.0 Demo and explanation, and I will continue to update the book notes series.