How To Make An App Like Pokemon Go

In this tutorial, you’ll learn how to make an app like Pokemon Go. You’ll learn how to use augmented reality and location services to get gamers outdoors! By Jean-Pierre Distler.

Leave a rating/review
Save for later
Share
You are currently viewing page 3 of 4 of this article. Click here to view the first page.

Polishing

Using primitives like cubes and spheres is an easy way to build your app without spending too much time mucking around with 3D models — but 3D models look _soo_ much nicer. In this section, you’ll add some polish to the game by adding 3D models for enemies and the ability to throw fireballs.

Open the art.scnassets folder to see two .dae files. These files contain the models for the enemies: one for a wolf, and one for a dragon.

The next step is to change setupTarget() inside ViewController.swift to load one of these models and assign it to the target’s itemNode property.

Replace the contents of setupTarget() with the following:

func setupTarget() {
  //1
  let scene = SCNScene(named: "art.scnassets/\(target.itemDescription).dae")
  //2
  let enemy = scene?.rootNode.childNode(withName: target.itemDescription, recursively: true)
  //3  
  if target.itemDescription == "dragon" {
    enemy?.position = SCNVector3(x: 0, y: -15, z: 0)
  } else {
    enemy?.position = SCNVector3(x: 0, y: 0, z: 0)
  }
    
  //4  
  let node = SCNNode()
  node.addChildNode(enemy!)
  node.name = "enemy"
  self.target.itemNode = node
}

Here’s what’s going on above:

  1. First you load the model into a scene. The target’s itemDescription has the same name as the .dae file.
  2. Next you traverse the scene to find a node with the name of itemDescription. There’s only one node with this name, which also happens to be the root node of the model.
  3. Then you adjust the position so that both models appear at the same place. If you get your models from the same designer, you might not need this step. However, I used models from two different designers: the wolf is from I found the wolf from 3dwarehouse.sketchup.com and the dragon from https://clara.io.
  4. Finally, you add the model to an empty node assign it to the itemNode property of the current target. This is small trick to make the touch handling in the next section a little easier.

Build and run your project; you’ll see a 3D model of a wolf that looks far more menacing than your lowly cube!

In fact, the wolf looks scary enough you might be tempted to run away, but as a brave hero retreat is not an option! Next you’ll add some fireballs so you can fight him off before you become lunch for a wolf pack.

The touch ended event is a good time to throw a fireball, so add the following method to ViewController.swift:

override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
  //1
  let touch = touches.first!
  let location = touch.location(in: sceneView)
   
  //2
  let hitResult = sceneView.hitTest(location, options: nil)
  //3
  let fireBall = SCNParticleSystem(named: "Fireball.scnp", inDirectory: nil)
  //4
  let emitterNode = SCNNode()
  emitterNode.position = SCNVector3(x: 0, y: -5, z: 10)
  emitterNode.addParticleSystem(fireBall!)
  scene.rootNode.addChildNode(emitterNode)
  
  //5  
  if hitResult.first != nil {
    //6
    target.itemNode?.runAction(SCNAction.sequence([SCNAction.wait(duration: 0.5), SCNAction.removeFromParentNode(), SCNAction.hide()]))
    let moveAction = SCNAction.move(to: target.itemNode!.position, duration: 0.5)
      emitterNode.runAction(moveAction)
  } else {
    //7
    emitterNode.runAction(SCNAction.move(to: SCNVector3(x: 0, y: 0, z: -30), duration: 0.5))
  }
}

Here’s how the fireball logic works:

  1. You convert the touch to a coordinate inside the scene.
  2. hitTest(_, options:) sends a ray trace to the given position and returns an array of SCNHitTestResult for every node that is on the line of the ray trace.
  3. This loads the particle system for the fireball from a SceneKit particle file.
  4. You then load the particle system to an empty node and place it at the bottom, outside the screen. This makes it look like the fireball is coming from the player’s position.
  5. If you detect a hit...
  6. ...you wait for a short period then remove the itemNode containing the enemy. You also move the emitter node to the enemy’s position at the same time.
  7. If you didn’t score a hit, the fireball simply moves to a fixed position.

Build and run your project, and make that wolf go up in flames!

How to Make an app like Pokemon Go

Finishing Touches

To finish your game, you’ll need to remove the enemy from the list, close the augmented reality view and go back to the map to find the next enemy.

Removing the enemy from the list must be done in MapViewController, since the list of enemies lives there. To do this, you will add a delegate protocol with only one method called when a target is hit.

Add the following protocol inside ViewController.swift, just above the class declaration:

protocol ARControllerDelegate {
  func viewController(controller: ViewController, tappedTarget: ARItem)
}

Also add the following property to ViewController:

var delegate: ARControllerDelegate?

The method in the delegate protocol tells the delegate that there was a hit; the delegate can then decide what to do next.

Still in ViewController.swift, find touchesEnded(_:with:) and change the block of code for the condition of the if statement as follows:

if hitResult.first != nil {
  target.itemNode?.runAction(SCNAction.sequence([SCNAction.wait(duration: 0.5), SCNAction.removeFromParentNode(), SCNAction.hide()]))
  //1
  let sequence = SCNAction.sequence(
    [SCNAction.move(to: target.itemNode!.position, duration: 0.5),
     //2
     SCNAction.wait(duration: 3.5),  
     //3
     SCNAction.run({_ in
        self.delegate?.viewController(controller: self, tappedTarget: self.target)
      })])
   emitterNode.runAction(sequence)
} else {
  ...
}

Here’s what your changes mean:

  1. You change the action of the emitter node to a sequence, the move action stays the same.
  2. After the emitter moves, pause for 3.5 seconds.
  3. Then inform the delegate that a target was hit.

Open MapViewController.swift and add the following property to store the selected annotation:

var selectedAnnotation: MKAnnotation?

You’ll use this in a moment to remove it from the MapView.

Now find mapView(_:, didSelect:) and make the following changes to the conditional binding and block (i.e., the if let) which instantiates the ViewController:

if let viewController = storyboard.instantiateViewController(withIdentifier: "ARViewController") as? ViewController {
  //1
  viewController.delegate = self
          
  if let mapAnnotation = view.annotation as? MapAnnotation {
    viewController.target = mapAnnotation.item
    viewController.userLocation = mapView.userLocation.location!

    //2
    selectedAnnotation = view.annotation
    self.present(viewController, animated: true, completion: nil)
  }
}

Quite briefly:

  1. This sets the delegate of ViewController to MapViewController.
  2. Then you save the selected annotation.

Below the MKMapViewDelegate extension add the following:

extension MapViewController: ARControllerDelegate {
  func viewController(controller: ViewController, tappedTarget: ARItem) {
    //1
    self.dismiss(animated: true, completion: nil)
    //2
    let index = self.targets.index(where: {$0.itemDescription == tappedTarget.itemDescription})
    self.targets.remove(at: index!)
    
    if selectedAnnotation != nil {
      //3
      mapView.removeAnnotation(selectedAnnotation!)
    }
  }
}

Taking each commented section in turn:

  1. First you dismiss the augmented reality view.
  2. Then you remove the target from the target list.
  3. Finally you remove the annotation from the map.

Build and run to see your finished app:

How to Make an app like Pokemon Go