Prior to the start

This course builds on previous ARKit lessons. If you’re not ready, you can refer to our previous tutorial. Also, if you can, it’s best to find a flat place for your App.

What are we going to learn?

In this teaching, we focus on the horizontal level of ARKit. We will first make an ocean surface (horizontal plane) and then put a ship on it (3D object).

Or build a fleet of lights!

Next, you’ll learn about horizontal planes in ARKit. Finally, I hope that after this tutorial, you will be able to use horizontal planes in your ARKit projects.

As a developer, it is particularly important to have a learning atmosphere and a communication circle. This is my iOS communication group: 1012951431. No matter you are a big bull or a small white, you are welcome to enter.

What is horizontal plane

So when we talk about Horizontal planes in ARKit, what is this Horizontal Plane? When we detect a horizontal plane in ARKit, technically we detect an ARPlaneAnchor. So what is ARPlaneAnchor? Basically, an ARPlaneAnchor is an object that contains detected horizontal information.

The following is Apple’s official description of The Arplanet Anchor:

Information about the position and orientation of a real-world flat surface detected in a world-tracking AR session.

  • Apple ‘s Documentation

Let’s start making apps

We’ll start with this project, so we can focus on the implementation of ARKit. Take a brief look at the code content after you open your project in Xcode. I’ve set up an ARSCNView in my Storyboard.

Build and Run projects for a quick test. You should see the following screen on the iOS emulator:

Click OK on Camera Authorization to get camera permissions. This way you should be able to see your camera picture.

Horizontal detection

Detecting a horizontal plane is simple, thanks to Apple engineers.

Just put the following code into setUpSceneView() in the ViewController:

configuration.planeDetection = .horizontal
Copy the code

Tell ARKit to find any horizontal plane by setting planeDetection to.horizontal. Once ARKit detects a horizontal plane, the horizontal plane is added to the sceneView Session.

To detect the horizontal plane, we must call ARSCNViewDelegate Protocol. In the ViewController category below, create a ViewController category Extension to implement this Protocol.

extension ViewController: ARSCNViewDelegate {

}
Copy the code

Now implement the renderer(_:didAdd:for:) method in this class Extension:

func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {

}
Copy the code

This Protocol method is called every time an ARAnchor is added to the sceneView Session. An ARAnchor object represents a physical location and direction in 3D space. We will use ARAnchor later to detect the horizontal level.

Next, go back to setUpSceneView(). Assign sceneView’s Delegate to the ViewController in setUpSceneView().

You can also set the Debug Options of sceneView to display Feature points if you want. This will help you find enough feature points to detect the horizontal plane. And a horizontal plane is made up of a lot of feature points. Renderer (_:didAdd:for:) is called once enough feature points have been detected to identify the horizontal plane.

Now, your setUpSceneView() should look like this:

func setUpSceneView() {
    let configuration = ARWorldTrackingConfiguration()
    configuration.planeDetection = .horizontal
    
    sceneView.session.run(configuration)
    
    sceneView.delegate = self
    sceneView.debugOptions = [ARSCNDebugOptions.showFeaturePoints]
}
Copy the code

Horizontal plane visualization

Now the App will be notified every time a new ARAnchor is added to sceneView, and we might be interested in what the new ARAnchor added looks like.

So update the renderer(_:didAdd:for:) method:

func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) { // 1 guard let planeAnchor = anchor as? ARPlaneAnchor else { return } // 2 let width = CGFloat(planeAnchor.extent.x) let height = CGFloat(planeAnchor.extent.z) let plane = SCNPlane(width: width, height: height) // 3 plane.materials.first? .diffuse.contents = UIColor.transparentLightBlue // 4 let planeNode = SCNNode(geometry: plane) // 5 let x = CGFloat(planeAnchor.center.x) let y = CGFloat(planeAnchor.center.y) let z = CGFloat(planeAnchor.center.z) planeNode.position = SCNVector3(x,y,z) planeNode.eulerAngles.x = -.pi / 2 // 6 node.addChildNode(planeNode) }Copy the code

Let me walk you through the code line by line.

  1. We will consider the Anchor parameter unwrap of the ARPlaneAnchor to confirm that we have information about the real world plane
  2. On this side, we create a SCNPlane to visualize the ARPlaneAnchor. SCNPlane is a single-sided planar geometric rectangle. We create an SCNPlane by unpacking the X and Y attributes of Extent in the package’s ARPlaneAnchor. An ARPlaneAnchor Extent is the estimated size of the detected plane. We take Extent X and Y to make the height and width of the SCNPlane. We then painted the plane a bright blue to simulate the appearance of water.
  3. We initialize the SCNNode with the SCNPlane geometry we just created
  4. We initializex,yAs well aszIn terms of constantsplaneAnchorThe X, Y, and Z coordinates in the center. This is toplaneNodeCoordinates position of. We rotate counterclockwiseplaneNodeX euler Angle 90 degrees, otherwiseplaneNodeWill stand vertically on the table. If rotated clockwise, it would be a magic illusion, because SceneKit presupposes to render SCNPlane using a material on one side.
  5. Finally, we add planeNode as a child node to the newly added SceneKit node

Build and Run projects. You should now be able to detect and visualize horizontal planes

Extended level

As ARKit receives additional information about the environment, we may wish to expand the horizontal plane we previously detected to make a larger plane or more accurately present the new information.

Renderer (_:didUpdate:for:)

func renderer(_ renderer: SCNSceneRenderer, didUpdate node: SCNNode, for anchor: ARAnchor) {

}
Copy the code

This method is called when the properties of the SceneKit node are updated to match the corresponding anchor points. This allows ARKit to improve its estimation of the location and extent of the horizontal plane.

The node parameter is the updated coordinate position of the anchor point. The Anchor parameter provides the updated width and height of the anchor point. Using these two parameters, we can update the previously implemented SCNPlane to correspond to the updated coordinates.

Next, place the following code in the renderer(_:didUpdate:for:) :

// 1
guard let planeAnchor = anchor as?  ARPlaneAnchor,
    let planeNode = node.childNodes.first,
    let plane = planeNode.geometry as? SCNPlane
    else { return }

// 2
let width = CGFloat(planeAnchor.extent.x)
let height = CGFloat(planeAnchor.extent.z)
plane.width = width
plane.height = height

// 3
let x = CGFloat(planeAnchor.center.x)
let y = CGFloat(planeAnchor.center.y)
let z = CGFloat(planeAnchor.center.z)
planeNode.position = SCNVector3(x, y, z)
Copy the code

Again, let me explain the code line by line:

  1. We will consider ARPlaneAnchoranchorParameter security unpacking. Next, I’m going tonodeThe first child node is also safely unpacked. Finally, we will also regard asSCNPlaneplaneNodeGeometry safety unpacking. We’re just going to take out what we implemented beforeARPlaneAnchor,SCNNode,SCNplaneAnd update properties with corresponding parameters.
  2. Here we useplaneAnchorTo update the x and Z properties of the rangeplaneThe width of high.
  3. And finally, we’re going toplaneNodeThe coordinate position of is updated toplaneAnchorThe x, y, and z coordinates of the center point

Build and Run projects to validate the implementation of horizontal extensions.

Adds objects to the horizontal plane

Now let’s put the boat on the level. In the initial project, I have wrapped a 3D object shaped like a boat for you to use.

Insert the following method in the ViewController category to place the ship on a horizontal plane:

@objc func addShipToSceneView(withGestureRecognizer recognizer: UIGestureRecognizer) {
    let tapLocation = recognizer.location(in: sceneView)
    let hitTestResults = sceneView.hitTest(tapLocation, types: .existingPlaneUsingExtent)
    
    guard let hitTestResult = hitTestResults.first else { return }
    let translation = hitTestResult.worldTransform.translation
    let x = translation.x
    let y = translation.y
    let z = translation.z
    
    guard let shipScene = SCNScene(named: "ship.scn"),
        let shipNode = shipScene.rootNode.childNode(withName: "ship", recursively: false)
        else { return }
    
    
    shipNode.position = SCNVector3(x,y,z)
    sceneView.scene.rootNode.addChildNode(shipNode)
}
Copy the code

The code here is similar to what we taught before, so I won’t explain it line by line. If you want to learn more, check out the previous lectures. The only difference is that we pass different parameters in Types to detect existing flat anchors in sceneView.

To finish, we also need to add the following code:

func addTapGestureToSceneView() {
    let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(ViewController.addShipToSceneView(withGestureRecognizer:)))
    sceneView.addGestureRecognizer(tapGestureRecognizer)
}
Copy the code

This method will put the click gesture into the sceneView.

Call the following method in viewDidLoad() to put the click gesture into sceneView.

addTapGestureToSceneView()
Copy the code

Now if you Build and Run, you should be able to detect a horizontal plane and visualize it and put a cool boat on top of it.

Or a fleet (using lights)

You can use lights by unlighting configureLighting() within viewDidLoad(). This function is very simple and can use lights with just two strokes of code:

sceneView.autoenablesDefaultLighting = true
sceneView.automaticallyUpdatesLighting = true
Copy the code

conclusion

I hope you enjoyed the lesson and learned something valuable. If you do, please let me know by sharing this tutorial. Finally, if you have any comments, questions or suggestions, feel free to leave a comment below.

I’m not sure if it’s a good idea to do this alone, but you can post a picture of where you set up the boat in the comments, and I wonder what interesting place you would do it.

As an example, you can download the finished project file on GitHub.

Recommended at the end of the article: iOS popular anthology

  • 1.BAT and other major manufacturers iOS interview questions + answers

  • 2.Must-read books for Advanced Development in iOS (Classics must Read)

  • 3.IOS Development Advanced Interview “resume creation” guide

  • (4)IOS Interview Process to the basics