Chapters

Hide chapters

Apple Augmented Reality by Tutorials

First Edition - Early Access 3 · iOS 14 · Swift 5.1 · Xcode 12

Before You Begin

Section 0: 3 chapters
Show chapters Hide chapters

Section I: Reality Composer

Section 1: 5 chapters
Show chapters Hide chapters

Section VI: ARKit & SceneKit

Section 6: 2 chapters
Show chapters Hide chapters

Section VII: ECS & Collaborative Experiences (Bonus Section)

Section 7: 2 chapters
Show chapters Hide chapters

14. Raycasting & Physics
Written by Chris Language

In this chapter, you’ll pick up from where you left off in the previous one. Your AR-based SpriteKit game is coming along well, and you’ve laid a lot of the groundwork already. Your goal now is to add all the missing pieces and finishing touches.

Take a moment to take stock of what you’ve done and what’s up next.

What’s done?

  • Game State: The game has basic game states in place, and you can easily switch from one state to another. This lets you control your code based on the current state of the game.

  • Spawn Point: When the player taps the screen, the game adds an AR anchor in the camera’s view. A tiny box that acts as the spawning point for the emojis also appears.

  • Error Handling: Your game is robust enough to handle whatever the real world can throw at it. It informs the player of any tracking issues and, most importantly, it can recover from an interruption.

What’s next?

  • Spawning Emojis: With the spawn point in place, you’ll spawn multiple emojis at this location.

  • Running Actions: To add some polish, you’ll run some custom actions on the emojis to play sound effects, scale and run additional code.

  • Enabling Physics: You’ll enable physics so the emojis participate in the physics simulation. This gives each emoji a physical shape and applies forces like gravity to it.

  • Applying Forces: You’ll use physically-based animation to apply forces to the emojis, shooting them out from the spawning point into the world, then letting gravity pull them back to earth.

  • 2D Raycasting: You’ll use 2D raycasting to check if the player touches any of the spawned emojis to save them from certain death.

Now that you know what’s next, it’s time to get cracking!

Note: There’s a copy of the final project from the previous chapter available in starter/EmojiPop.

Spawning emojis

Your first step is to get the emojis to spawn. You’ll use the spawn point as the parent node to spawn the new emojis. This ensures the emojis spawn in the player’s view.

Start by creating a helper function that spawns a single emoji. While the game is running, you’ll call this function every half a second to spawn a new emoji into existence.

Open Scene.swift, then add the following function to Scene:

func spawnEmoji() {  
  // 1
  let emojiNode = SKLabelNode(
    text:String(emojis.randomElement()!))
  emojiNode.name = "Emoji"
  emojiNode.horizontalAlignmentMode = .center
  emojiNode.verticalAlignmentMode = .center
  // 2
  guard let sceneView = self.view as? ARSKView else { return }
  let spawnNode = sceneView.scene?.childNode(
    withName: "SpawnPoint")
  spawnNode?.addChild(emojiNode)
}

This defines a function named spawnEmoji() whose main responsibility is spawning a single emoji.

Take a closer look at what it’s doing:

  1. Creates a new SKLabelNode using a random emoji character from the string of emojis available in emojis. The node is named Emoji and it’s centered vertically and horizontally.

  2. Interrogates the available node in scene, looking for the node named SpawnPoint. It then adds the newly-created emoji as a child of spawnNode. This places the emoji into the scene.

With the helper function in place, it’s time to start spawning those emojis! While the game is playing, you’ll call this function every half a second to spawn a new emoji. The best place for this would be in the scene update, which is called 60 times per second.

Add the following to update(_:):

// 1
if gameState != .Playing { return }
// 2
if spawnTime == 0 { spawnTime = currentTime + 3 }
// 3   
if spawnTime < currentTime {
  spawnEmoji()
  spawnTime = currentTime + 0.5;
}
//4     
updateHUD("SCORE: " + String(score) + 
  " | LIVES: " + String(lives))

Here’s how this breaks down:

  1. You only want to update the game while it’s in the Playing state.
  2. If spawnTime is 0, the game just started so you give the player a few seconds to prepare for the onslaught of emojis that are about to spawn. This creates a slight delay of 3 seconds before the first emoji spawns.
  3. Once spawnTime is less than currentTime, it’s time to spawn a new emoji. Once spawned, you reset spawnTime to wait for another half a second before spawning the next emoji.
  4. Finally, you update the HUD with the current score and available lives.

Great, you’re finally spawning emojis! You’re welcome to do a quick build and run to test things out, but prepare to be underwhelmed.

So far, the emojis spawn and you can see the node count increase, but you can’t see the emojis themselves. That’s because they’re hiding behind the spawn point.

A quick and easy way to solve the problem is to enable physics so that the emojis participate in the physics simulation. Once spawned, gravity will pull the emojis toward the ground.

Enabling physics

SpriteKit comes with a very powerful 2D physics engine. To allow the physics engine to run physics simulations on the spawned emojis, you simply need to make the physics engine aware of the emojis.

To enable physics, you need to attach a physics body to the SpriteKit node. The physics body describes all the physical properties of the node, including their shape, mass, friction, damping and restitution.

The physics engine will take all this information into account when it simulates 2D physics interactions on the nodes. Those interactions include things like gravity, friction and collisions with other nodes in the physical world.

Physics body types

One of the key properties you must specify when creating a physics body is its type. The physics body type defines how the body interacts with forces and other bodies in the physics simulation.

SpriteKit uses three types of physics bodies:

  • Dynamic: The physics engine automatically moves this type of body in response to forces and collisions.

  • Static: This type of body is similar to a dynamic body, except that the physics engine ignores its velocity and forces and collisions don’t affect it. You can still move and rotate these types of bodies, and other dynamic bodies will interact with it.

  • Edge: This type of body is very similar to a static body, but it has no volume. Use edges to represent negative space within a scene, such as an invisible boundary.

Physics shapes

In addition to the type, shape is another important property you must specify when creating a physics body. This defines the 2D shape the physics engine uses to detect collisions.

When choosing a shape to use, there’s usually a tradeoff between performance and the accuracy of the collisions.

Take careful note of the following spaceship, with various examples of physics body shapes indicated in gray.

When creating a physics body, SpriteKit allows you to use the following shapes:

  • None: This allows you to demonstrate a node with no physics body attached.

  • Circular: This is the most performant physics shape to use. It defines a circular volume around the spaceship, which the physics engine uses for collisions. Although you’ll get the best possible performance, the collisions won’t be very accurate.

  • Rectangular: This is the second most performant physics shape to use. It defines a rectangular volume around the spaceship. Although this shape produces better results during collisions, it’s still not very accurate when considering the shape of the spaceship.

  • Polygonal: There’s a significant performance hit when using this type of physics shape. It does, however, allow you to define a polygonal volume around the spaceship that matches the shape more accurately. You’ll get more accurate collisions, but at a cost.

  • Alpha Channel: This is the most expensive shape type to use. It uses the image alpha channel to calculate a volume around the spaceship. You’ll get pixel-perfect collisions, producing highly accurate results, but at a very high performance cost.

Now that you’ve covered the basics, it’s time to enable physics on the spawned emojis.

Enabling physics

In SpriteKit, all physics bodies are SKPhysicsBody objects. Once you create a physics body, you assign it to the physicsBody property of the SKNode.

Add the following to the bottom of spawnEmoji():

// Enable Physics
emojiNode.physicsBody = SKPhysicsBody(circleOfRadius: 15)
emojiNode.physicsBody?.mass = 0.01

This creates a new, circular-shaped physics body that you’ll attach to the emoji node’s physicsBody. The physical mass of the body is set to 10 grams.

Build and run. Now, you’ll notice something different from before.

When the emojis spawn, they start to fall towards the ground. You’ve applied a gravitational force to them by having them participate in the physics simulation.

This is a good step forward, but you still want to make the game more exciting.

Force

In real life, when you want to make a ball move, you have to apply a force to it — by kicking it, for example. Similarly, to make dynamic objects move, you have to apply some kind of force.

A force has both a magnitude and direction. You define these as a 2D vector containing an X- and Y-axis.

These examples show various forces applied to the ball and the resulting reaction:

  • Horizontal: Applying a 2D force vector of (x: 10, y: 0) will push the ball to the right horizontally.

  • Vertical: Applying a 2D force vector of (x: 0, y: 10) will push the ball upwards vertically.

  • Diagonal: Applying a 2D force vector of (x: 10, y: 10) will push the ball diagonally, moving upwards and right at the same time.

Adding some randomness

Adding some randomness to the gameplay will make the game more challenging and increase the replay value. Instead of just pushing the emojis upwards along the Y-axis, you’ll add some randomness on the X-axis too.

Add the following helper function to Scene:

func randomCGFloat() -> CGFloat {
  return CGFloat(Float(arc4random()) / Float(UINT32_MAX))
}

This function will generate a random value between 0.0 and 1.0. Now, you can easily bring in some randomness when applying force to the emojis.

Applying an impulse

Instead of applying a constant force, you’ll apply the force as an impulse. Gravity, for example, is a constant force, whereas a kick is an impulse.

To apply an impulse, use applyImpulse(), which is available on the physicsBody of the SKNode.

Add the following to the bottom of spawnEmoji():

// Add Impulse
emojiNode.physicsBody?.applyImpulse(CGVector(
  dx: -5 + 10 * randomCGFloat(), dy: 10))

This applies an impulse on the emoji’s physics body, kicking it upwards with a random sidewards direction.

Torque

Torque is another type of force that you can apply to physics bodies — a rotational force. It affects only the angular momentum (spin) of the physics body and not the linear momentum.

Applying torque will make the node spin around its center of mass.

To make the node spin to the right, apply a positive torque to the physics body. When you apply a negative torque, the node will spin to the left.

Applying torque

You can use applyTorque(), which is available on the physicsBody, to apply torque to a SKNode.

Add the following to the bottom of spawnEmoji():

// Add Torque
emojiNode.physicsBody?.applyTorque(-0.2 + 0.4 * randomCGFloat())

This applies a torque to the emoji’s physics body, making it in a random direction.

Another build and run will show you the current state of affairs.

The emojis no longer just fall. They’re shot up into the air at random trajectories, then they fall to their doom. Awesome! But hang on, there’s more you can do to spice up the game, but you need to know a little bit about actions and how to run them first.

Actions

Actions allow you to perform basic animations to manipulate a node’s position, scale, rotation and opacity within a scene. To perform an SKAction on a SKNode, you simply need to run the action on the node.

Here are a few transformative SKActions available:

  • Scale: If you want to grow tiny Coolio into big Coolio when he collects a power-up, use the scale action.

  • Move: If you want to jump over an enemy when the player performs a certain action, there’s a move action to do it.

  • Fade: You just drank a magic potion that turns you into a translucent ghost, thanks to the fade action.

  • Rotate: If you want to roll forward and squish a bug when the player performs an action, use the rotate action.

Note: Be aware that if the node has a dynamic physics body attached, you should not run transformative actions on it. If the node is declared as a static physics body, then you’re good to go. Fading a node in and out should be fine, too.

Here are a few special SKActions:

  • Wait: If you want to pause for a moment before performing another action, use the wait action.

  • Remove from Parent: If you want to destroy a squished bug and make it disappear from the scene, there’s a handy remove from parent action you can use.

  • Play Sound: If you want the squished bug to scream while it’s being crushed, use play sound to give it its last words.

  • Run Code Block: If you want to execute custom code after running some actions, there’s a run code block action you can use. This is super useful for injecting conditional code into action sequences.

Sequence & group actions

You can run only run a single SKAction on a SKNode at a time, but there are two special types of actions you can use to run multiple actions in a sequence or in a group.

In the illustration above, consider the following:

  • Actions 1 - 5: There are five basic actions. For demonstration purposes, say each action will take one second to complete, except action 4, which will take two seconds.

  • Sequences 1 - 2: A sequence action contains multiple actions that run one by one in sequence. Sequence 1 contains actions 1 to 3, which will take three seconds to complete. Sequence 2 contains actions 4 and 5, which will also take three seconds to complete.

  • Group 1: A group action allows you to group actions together so they can run in parallel. Because group 1 consists of two sequence actions, the resulting group action will run as follows: Sequences 1 and 2 trigger at the same time. Actions 1 and 2 run in sequence, and action 4 runs at the same time. Finally, after actions 1, 2 and 4 complete, actions 3 and 5 run at the same time.

Adding sound files

In the next section, you’re going to use a play sound action to add some fun noises to your game. To do this, you’ll need to add a few sound files to your project.

Drag and drop the entire SoundEffects folder, located under starter/resources, into your project.

Be sure to enable Copy Items If Needed with the target set to EmojiPop, then select Finish to complete the process.

This adds the audio resources to your project. You’ll reference them when you play sound effects.

Running actions

Now that you know all about actions, you’ll bring the game to life by adding some actions to it.

Add the following to the bottom of spawnEmoji():

// 1
let spawnSoundAction = SKAction.playSoundFileNamed(
      "SoundEffects/Spawn.wav", waitForCompletion: false)
let dieSoundAction = SKAction.playSoundFileNamed(
      "SoundEffects/Die.wav", waitForCompletion: false)
let waitAction = SKAction.wait(forDuration: 3)
let removeAction = SKAction.removeFromParent()
// 2
let runAction = SKAction.run({
  self.lives -= 1
  if self.lives <= 0 {
    self.stopGame()
  }
})
// 3
let sequenceAction = SKAction.sequence(
  [spawnSoundAction, waitAction, dieSoundAction, runAction,
    removeAction])    
emojiNode.run(sequenceAction)

Here’s how this breaks down:

  1. Creates a few basic actions that you’ll use in just a moment. They’re fairly self-explanatory based on their names and action types.

  2. Creates a custom code block action that decreases the lives by one. When all the lives are depleted, the game stops.

  3. Creates a single action sequence that consists of all the previously-created actions. You then run the sequence action against the freshly-spawned emojis. The resulting action sequence will play out as follows, as soon as an emoji is spawned: Play spawn sound ▸ Wait for three seconds ▸ Play die sound ▸ Decrease lives / stop game ▸ Remove emojis from scene.

This will automatically manage the spawned emojis. If the player fails to save them in time, the game automatically removes them and considers them dead. So sad. :[

Build and run to see how things look now.

The emojis spawn with a nice squeaky sound, then fall to their deaths. You can even hear them hit the ground with a thud, causing you to lose a life and, eventually, lose the game.

Understanding 2D raycasting

The poor emojis, there’s no way to save them right now. That’s just so sadistically… satisfying! :]

Bring out the hero in yourself and let 2D raycasting come to the rescue, saving the day and, of course, millions of emojis.

So how does 2D raycasting work?

When the player touches the screen, which is a two-dimensional surface, the game has to convert that touch point into three-dimensional space to determine if the player touched a node.

To do that, a ray is cast from the phone’s physical position into augmented space to that touched location on the screen. When the ray hits a node, that node is reported as a touched node. If the ray doesn’t hit anything, the player missed.

Handling touches

Next, you’ll add touch functionality to the game so the player can save those poor emojis.

Add the following helper function to Scene:

func checkTouches(_ touches: Set<UITouch>) {
  // 1
  guard let touch = touches.first else { return }
  let touchLocation = touch.location(in: self)
  let touchedNode = self.atPoint(touchLocation)
  // 2
  if touchedNode.name != "Emoji" { return }
  score += 1
  // 3     
  let collectSoundAction = SKAction.playSoundFileNamed(
      "SoundEffects/Collect.wav", waitForCompletion: false)
  let removeAction = SKAction.removeFromParent()
  let sequenceAction = SKAction.sequence(
    [collectSoundAction, removeAction]) 
  touchedNode.run(sequenceAction)
}

This defines a function called checkTouches(_:) that checks if the player touched an emoji.

Take a look at what’s happening here:

  1. This takes the first available touch from a provided list of touches. It then uses the touched screen location to do a quick raycast into the scene, determining whether the player hit any of the available SKNodes.

  2. If the player touched a node, and it’s indeed an emoji node, the score increases by 1.

  3. Finally, you create and run an action sequence consisting of a sound effect and an action that will remove the emoji node from its parent node — ultimately destroying the touched emoji by removing it from the scene.

With this function in place, find and uncomment the call to it in touchesBegan_:with:):

checkTouches(touches)

While the game is running, you’ll check if the player touched any emojis, then remove them from the scene.

Fantastic, you can now save those emojis! You’re almost done, there’s just one tiny thing left to do.

Adding finishing touches

When the game starts, the spawn point just pops into view. This feels a bit abrupt and unpolished. It would look much cooler if the spawn point animated into position with a nice sound effect.

Open ViewController.swift, then find view(_:nodeFor:) and change the initial ’boxNode’ scale from 1.5 to 0, as follows:

boxNode.setScale(0)

Now, when the game creates the box, it’s so small that it’s invisible.

Add the following to the bottom of view(_:nodeFor:), right before return:

let startSoundAction = SKAction.playSoundFileNamed(
  "SoundEffects/GameStart.wav", waitForCompletion: false)
let scaleInAction = SKAction.scale(to: 1.5, duration: 0.8)
boxNode.run(SKAction.sequence(
  [startSoundAction, scaleInAction]))

This creates and runs an action sequence consisting of a sound effect and a scale effect on the created box node. It slowly scales the box to 1.5 while playing a nice sound effect.

Great! See how easy it is to add polish using just basic actions?

You’re all done! Build and run your game and reap the rewards of all your effort.

You’ve just finished your awesome SpriteKit-based ARKit game!

Key points

Congratulations, you’ve reached the end of the chapter and section.

While you soak in this proud moment, here’s a quick recap of all the things you learned in this chapter:

  • Physics: You now know how to enable physics on nodes. This is an easy way to breathe life into your AR content. You can make objects interact with one another using collisions, manipulate them with linear-based forces and even make them spin by applying torque.

  • Actions: Actions are incredibly powerful, enabling you to do all sorts of things like move, scale and rotate nodes. You can play sounds, remove nodes from scenes and run custom code blocks. You can run them in a sequence or in a group.

  • 2D Raycasting: Finding touched nodes is as easy as taking the screen touch point and doing a raycast into the scene to see if it hits any nodes.

In the next chapter, you’ll learn about using SceneKit with ARKit. Now, go challenge your friends and family to see who can save the most emojis. See you soon!

Have a technical question? Want to report a bug? You can ask questions and report bugs to the book authors in our official book forum here.
© 2026 Kodeco Inc.