Now Reading
AR Madness — our open source ARKit game tutorial! Part Two — Building the Game

AR Madness — our open source ARKit game tutorial! Part Two — Building the Game

This is part two of an epic tutorial, showing how to build an ARKit game!

  • Part One — Game Design and First ARKit app
  • Part Two — Building the Game
  • Part Three — Collision Detection
  • Part Four — Sounds, Music and Explosions!
  • Part Five — Game Management

TL:DR

If you want to skip the tutorial and just get the code to run on your phone — get it here on Github.

PART TWO: Building our game

Ok, about 1600 words into the tutorial and we’re really just warming up! I told you it was a long one!

First, we’ll add our UI elements to our main screen, then we’ll connect them to code.

In Xcode, click on the Main.storyboard and this will open:

In the centre is the visual editor for the main ViewController file. Expand out the “View Controller Scene” in the second panel and select “Scene View”:

This is the main object, upon which all other content will be added. It is of class ARSCNView — part of the ARKit SDK. ‘AR’ refers to ARKIT and ‘SCNView’ to the SCNView class in SceneKit, Apple’s 3D SDK. A lot of ARKit is inherited from SceneKit so will be familiar if you’ve used that (or SpriteKit) before.

First, I want to place that ARSCNView inside a standard UIView — so to do this, we need to replace it with an UIView, then add back in a ARSCNView.

So, from the Object Library, drag out a View and it will automatically replace it:

Now drag a ARSCNView onto that View and make it the same width and height:

Why did we do this? So we can now add additional UI elements on top of the ARSCNView.

Before we do that though, we need to hook up the new ARSCNView to the “sceneView” outlet in the ViewController.swift file:

While we’re in the ViewController file delete these lines from viewDidLoad:

// Create a new scene
let scene = SCNScene(named: "art.scnassets/ship.scn")!
// Set the scene to the view
sceneView.scene = scene

as we won’t be using that spaceship.

Adding UI elements

Ok, now drag out an UIImageView and position it in the centre to use as the aiming target:

We need to add our icons to the project to use them, so drag them from where ever you have them on your Mac into Assets.xcassets:

Now, back in the Storyboard, select the UIImageView we added, and select its image as “target”, setting the width and height to 100:

If you run the app now, you’ll see the camera view with the target superimposed on top:

Nice.

Ok, let’s add our two fire buttons now. Drag out two UIButtons, and replace their text with the axe and banana images, like this:

Run the app again and you’ll see them:

What are those letters and numbers at the bottom?

They are statistics provided by ARKit on performance. The “60fps” is frames per second, and ideally should stay at 60. You can remove this section at any time by commenting out this line of code in viewDidLoad:

// Show statistics such as fps and timing information
sceneView.showsStatistics = true

(or changing true to false of course).

Ok, create two Action Outlets (functions) in ViewController.swift from the two buttons i.e.

Control-Drag to the ViewController.swift file to add an Action Outlet

After both buttons are hooked up, the start of the Class should look like this, the new code highlighted in bold:

class ViewController: UIViewController, ARSCNViewDelegate {

//MARK: - variables
@IBOutlet var sceneView: ARSCNView!
//MARK: - buttons
@IBAction func onAxeButton(_ sender: Any) {
}
@IBAction func onBananaButton(_ sender: Any) {
}

Note I’ve also put in some “//MARK: -”s for readability.

Ok — we now have functions that will be called when their respective buttons are tapped by the player. When they are called we want their corresponding virtual objects to be fired towards wherever the player is pointing the camera target.

Let’s add in the 3D models first. From the downloaded files, drag everything in the 3DModels folder into the art.scnassets folder, resulting in something like this:

Each model, apart from the bath, has a texture file to go with it. The shark has two. Make sure that the texture files are applied to their models by selecting a model, then in the Scene graph section, click on an object, then look in the Attributes Inspector tab to see are the textures applied in any Materialspresent. For the axe, there is simply one Material, which uses the “axe_texture.jpg”:

going widescreen for this one!

For the shark, there are two: the “Parent_Sharkbody” and “Sharkjaw”:

Note — working with the 3D models is probably the area you have least experience in, so is most likely the place to make mistakes, especially if you source your own files. Take your time — explore the models and the various values for their properties and settings; trial and error can go a long way.

If you get stuck at all here (or anywhere else), leave a comment below and we’ll get back to you.

Ok, let’s try firing some bananas and some axes!

Time for some maths

When the player hits the banana button, we want to fire the model towards the target. Enter this code into the ViewController.swift:

I got this code from here, and it returns a direction and a position for the camera. Note both are defined by a three dimensional vector, using the SCNVector3 class.

Now we create two new functions — “createMissile” and “fireMissile”. createMissile first:

Let’s take a closer look at this function.

func createMissile(type : String)->SCNNode{

This tells us that the function takes a String parameter called ‘type’ — we’ll use this for the “type” of the missile e.g. banana or axe — and that the function will return a SCNNode object.

SCNNode is basically an “object” that appears in the scene for the player to see, and is one of the most important classes you’ll use in making AR games.

The switch statement checks which missile it is, then runs this:

let scene = SCNScene(named: "art.scnassets/banana.dae")

This retrieves our “.dae” (or Collada) 3D model — in this instance “banana.dae”

node = (scene?.rootNode.childNode(withName: "Cube_001", recursively: true)!)!

This takes the main node from the .dae file and assigns it to our SCNNode instance called node. The “Cube_001” is the name of this node, as seen here:

This name will likely be different for each model i.e. the axe has a node name of “axe” so make sure you update that line of code accordingly in any other models you use.

node.scale = SCNVector3(0.2,0.2,0.2)

This line changes the size of the model — as a lot of models are huge when you get them. We change the size using a SCNVector3 and provide values for x,y and z, here with a value of 0.2. This basically makes the model size change to 20% of its original size.

node.name = "banana"

We then give a name of “banana” to the SCNNode. This will be used later to help us identify which objects are which in the scene, programatically.

Ok, now let’s add the fireMissile function, which will use the createMissile function to create some nodes and fire them in the scene!

Ok, what’s going on here? Again, this line shows that we’re expecting a String parameter called type, which is once again used to specify “axe” or “banana”:

func fireMissile(type : String){

This creates our missile node:

node = createMissile(type: type)

This gets the user’s position and direction, using the function we wrote earlier:

let (direction, position) = self.getUserVector()

This uses that position as the initial position of the missile i.e. it will come out from the user’s viewpoint in the app when fired:

node.position = position

The switch statement again is used to set different values, based on whether it’s a banana or an axe missile. This line sets the direction that the missile will take:

nodeDirection  = SCNVector3(direction.x*4,direction.y*4,direction.z*4)

The ‘*4’ is used to increase the speed of the missile — you can change this value as needed.

This line applies a force at the x axis of the node — with the affect of spinning the node, just for affect.

node.physicsBody?.applyForce(nodeDirection, at: SCNVector3(0.1,0,0), asImpulse: true)

After the switch statement, a similar line actually “fires” the missile node, applying a force in the direction we have specified:

node.physicsBody?.applyForce(nodeDirection , asImpulse: true)

Note that both those lines uses the node’s “physicsBody” — this is used for a variety of purposes, like moving the node as we are doing now. It can also be used to interact with other objects in the scene; shortly we will use it to detect “collisions” i.e. when objects hit other objects.

Then finally the node is added to the scene for the user to see:

sceneView.scene.rootNode.addChildNode(node)

Ok, let’s run the app and hit some buttons! You should hopefully see bananas and axes flying from you into the scene!

If you can’t see the models appear, have a look around to see are they above / behind you etc. They may be too small or too big — if so adjust the scale in the createMissile function.

Adding objects to fire at

We can now fire objects at will all around us, in augmented reality. Amazing! What’ll be even better though is having other things to fire at! Add this longer function to ViewController.swift:

There’s a lot going on there, but we can go through it slowly.

for index in 1…100 {

is the start of the for loop, that’ll be run 100 times.

var node = SCNNode()

creates a node instance that will be used for each bathtub or shark object that we’ll place around the player.

if (index > 9) && (index % 10 == 0) {

checks if we’re at the 10th, 20th, 30th, 40th …. 100th item in the for loop. If so, we then:

let scene = SCNScene(named: "art.scnassets/mouthshark.dae")
node = (scene?.rootNode.childNode(withName: "shark", recursively: true)!)!
node.scale = SCNVector3(0.3,0.3,0.3)                
node.name = "shark"

which stores the shark model in our node and then scales it, and gives it a name of “shark”. There should be only 10 sharks — all the rest should be bathtubs, as shown in the next few lines of code:

else{
let scene = SCNScene(named: "art.scnassets/bath.dae")
node = (scene?.rootNode.childNode(withName: "Cube_001", recursively: true)!)!
node.scale = SCNVector3(0.02,0.02,0.02)
node.name = "bath"
}

We then set the node’s physics body to dynamic and to be unaffected by gravity in the scene:

node.physicsBody = SCNPhysicsBody(type: .dynamic, shape: nil)            node.physicsBody?.isAffectedByGravity = false

The next line is important to understand — it’s where we place the node into the scene:

//place randomly, within thresholds            
node.position = SCNVector3(randomFloat(min: -10, max: 10),randomFloat(min: -4, max: 5),randomFloat(min: -10, max: 10))

This is basically setting the node’s x,y and z positions to be random numbers between certain thresholds. The “randomFloat” is a function that we haven’t actually written yet — we will in a minute!

Experiment with the values in that line of code — as they will change the area into which your 100 nodes could be placed.

Note aswell that we randomly position the nodes in this way to make the game harder, and more fun, for the player. We’re going to have more points in the game if you shoot a shark, so don’t want them in the same place every time!

These lines:

//rotate            
let action : SCNAction = SCNAction.rotate(by: .pi, around: SCNVector3(0, 1, 0), duration: 1.0)
let forever = SCNAction.repeatForever(action)
node.runAction(forever)

rotate the node around it’s y-axis, forever, by using an SCNAction. SCNActions are really useful ways to carry out “actions” on nodes — like moving them, rotating them etc.

Finally, this line:

//add to scene            sceneView.scene.rootNode.addChildNode(node)

adds our node to the scene, then the loop will run again.

Now we can add that function to generate a random number:

func randomFloat(min: Float, max: Float) -> Float {
return (Float(arc4random()) / 0xFFFFFFFF) * (max - min) + min
}

and finally, add the text highlighted in bold to viewDidLoad, to add these nodes when the screen is loaded:

addTargetNodes()
}

Ok, you should be ready to run the app, and hopefully will see something like this:

bathtubs everywhere!!

Ok — so now we have buttons to fire bananas and axes, and bathtubs and sharks to shoot at them! If you go ahead and do that, you’ll see that the objects will bump into each other and move — but there won’t be any cool explosions, as we haven’t written any code yet to do that!


So ends Part Two of the Neverending Tutorial!

Author Bio —  Andy O’Sullivan

Innovation in Liberty IT | Creator of http://appsandbiscuits.com | Gaeilge | all content my own opinion

What's Your Reaction?
Excited
0
Happy
0
In Love
0
Not Sure
0
Silly
0
View Comments (0)

Leave a Reply

Your email address will not be published.