In the interest of full disclosure, this is Daily Coding Tip 040, but it’s been 43 days since this series started.
Most of those missing tips have occurred in the last week, as I’ve struggled to get more complicated AR examples done in time to post them every day.
I will catch up and make the numbers right again!
Before we get started with the remote control, we need to add the car we’re going to control. Create an Xcode project with the Augmented Reality template, and open the Experience.rcproject file in Reality Composer. This is where the default steel box is, but we’re going to want to remove it. Click the box, press delete, and click the + Add button in the top right. In the Transport category there’s a plane, taxi, fire engine and police car.
Choose whichever of these you want, and place it at the origin where the steel box was.
The properties panel on the right side has a field at the top for the name. Give the model a descriptive name. I chose the taxi, so I gave mine the name Taxi. This name will be accessible from code, so it’s important that you add it and don’t forget. If you choose a vehicle other than the taxi, you’ll need to change the part of my code that refers to it by this name.
Before we start writing SwiftUI, we need to extend some of the mathematical types. These extensions are based on an answer by Stack Overflow user Nativ, with the addition of a speed parameter in the
movedForwardPosition function. Since the forward vector is a unit vector, it shows you how to move exactly 1 unit in the forward direction. In
RealityKit, 1 unit is a 1 metre, which is way too far for us to be moving. Instead we’ll be multiplying it by a decimal speed that reduces the distance travelled substantially.
Now we can create the UI that will allow us to control the vehicle. Although the UI has no access to the
ARView or the models it displays, we are passing closures for two situations. We need to take action when the rotation changes, and when a timer goes off. I’ve been pretty lazy and avoided setting an
ARSessionDelegate, which would give us to the
func session(ARSession, didUpdate: ARFrame) function.
This is a callback that occurs every time a new frame is about to be displayed, otherwise known as a frame update.
Instead of doing that, I’ve set a timer to perform an action 60 times a second. When an AR app is working well, it should run at 60 FPS, so this is close enough for our purposes. When the
moving toggle is on, the vehicle will move at the speed specified by the speed slider.
Steering can be done by the rotation slider.
Now we need to create the
rotationChanged functions, which will be passed as closures to
DriveView. I’m using
AnyViewRepresentable again, but this time it takes a closure that will make the
ARView automatically. The
newOrientation computed property calculates a new angle in radians from the rotation property. The rotation property will be measured in degrees instead. The
newOrientation property also converts this float value to a quaternion, which is what we need to set the rotation of the vehicle.
ContentView structure has the
boxAnchor as a constant, which is important because we referred to it in the
rotationChanged methods above. Because I didn’t rename the scene in Reality Composer from Box, this is still the automatically generated name of the function that loads it. It doesn’t matter much, and I thought it would be less complicated to do it this way.
All that’s left to do now is pass the
@State bindings to the
DriveView, and display the AR content in the
If your app ends up being confined to a small square, instead of stretching to the size of the entire screen, you need to add a launch screen.
Create a new file in your project and give it the default name of Launch Screen.
Make sure to go to your project settings and specify the name of your launch screen file.