SpriteKit Tutorial for Beginners

In this SpriteKit tutorial, you will learn how to create a simple 2D game using SpriteKit, Apple’s 2D game framework, while writing in Swift 4! By Brody Eller.

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

Shooting Projectiles

At this point, the ninja is just begging for some action — so it’s time to add shooting! There are many ways you could implement shooting, but for this game you’re going to make it so that when the user taps the screen, a projectile is shot from the player in the direction of the tap.

You’ll want to use a “move to” action to implement this, but in order to use this you have to do a little math. The “move to” action requires you to give a destination for the projectile, but you can’t just use the touch point because the touch point represents the direction to shoot relative to the player. You actually want to keep the projectile moving through the touch point until it goes off-screen.

Here’s a picture that illustrates the matter:

Projectile Triangle

As you can see, you have a small triangle created by the x and y offset from the origin point to the touch point. You just need to make a big triangle with the same ratio — and you know you want one of the endpoints to be off the screen.

To run these calculations, it really helps if you have some basic vector math routines you can call (like methods to add and subtract vectors). However, SpriteKit doesn’t have any by default so you’ll have to write your own.

Luckily they are very easy to write thanks to the power of Swift operator overloading. Add these functions to the top of your file, right before the GameScene class:

func +(left: CGPoint, right: CGPoint) -> CGPoint {
  return CGPoint(x: left.x + right.x, y: left.y + right.y)
}

func -(left: CGPoint, right: CGPoint) -> CGPoint {
  return CGPoint(x: left.x - right.x, y: left.y - right.y)
}

func *(point: CGPoint, scalar: CGFloat) -> CGPoint {
  return CGPoint(x: point.x * scalar, y: point.y * scalar)
}

func /(point: CGPoint, scalar: CGFloat) -> CGPoint {
  return CGPoint(x: point.x / scalar, y: point.y / scalar)
}

#if !(arch(x86_64) || arch(arm64))
  func sqrt(a: CGFloat) -> CGFloat {
    return CGFloat(sqrtf(Float(a)))
  }
#endif

extension CGPoint {
  func length() -> CGFloat {
    return sqrt(x*x + y*y)
  }
  
  func normalized() -> CGPoint {
    return self / length()
  }
}

These are standard implementations of some vector math functions. If you’re confused about what’s going on here or are new to vector math, check out this quick vector math explanation.

Next, add a new method to the GameScene class — again, before the closing curly brace:

override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
  // 1 - Choose one of the touches to work with
  guard let touch = touches.first else {
    return
  }
  let touchLocation = touch.location(in: self)
  
  // 2 - Set up initial location of projectile
  let projectile = SKSpriteNode(imageNamed: "projectile")
  projectile.position = player.position
  
  // 3 - Determine offset of location to projectile
  let offset = touchLocation - projectile.position
  
  // 4 - Bail out if you are shooting down or backwards
  if offset.x < 0 { return }
  
  // 5 - OK to add now - you've double checked position
  addChild(projectile)
  
  // 6 - Get the direction of where to shoot
  let direction = offset.normalized()
  
  // 7 - Make it shoot far enough to be guaranteed off screen
  let shootAmount = direction * 1000
  
  // 8 - Add the shoot amount to the current position
  let realDest = shootAmount + projectile.position
  
  // 9 - Create the actions
  let actionMove = SKAction.move(to: realDest, duration: 2.0)
  let actionMoveDone = SKAction.removeFromParent()
  projectile.run(SKAction.sequence([actionMove, actionMoveDone]))
}

There's a lot going on here, so here's what it does, step by step.

  1. One of the cool things about SpriteKit is that it includes a category on UITouch with location(in:) and previousLocation(in:) methods. These let you find the coordinate of a touch within an SKNode's coordinate system. In this case, you use it to find out where the touch is within the scene's coordinate system.
  2. You then create a projectile and place it where the player is to start. Note you don't add it to the scene yet, because you have to do some sanity checking first - this game does not allow the ninja to shoot backwards.
  3. You then subtract the projectile's current position from the touch location to get a vector from the current position to the touch location.
  4. If the X value is less than 0, this means the player is trying to shoot backwards. This is not allowed in this game (real ninjas don't look back!), so just return.
  5. Otherwise, it's OK to add the projectile to the scene.
  6. Convert the offset into a unit vector (of length 1) by calling normalized(). This will make it easy to make a vector with a fixed length in the same direction, because 1 * length = length.
  7. Multiply the unit vector in the direction you want to shoot in by 1000. Why 1000? It will definitely be long enough to go past the edge of the screen. :]
  8. Add the shoot amount to the current position to get where it should end up on the screen.
  9. Finally, create move(to:,duration:) and removeFromParent() actions like you did earlier for the monster.

Build and run. Now your ninja should be able to fire away at the oncoming hordes!

SpriteKit Tutorial

Collision Detection and Physics: Overview

So now you have shurikens flying everywhere, but what your ninja really wants to do is to lay some smack down. So, it's time to add some code to detect when your projectiles intersect your targets.

One of the nice things about SpriteKit is it comes with a physics engine built right in! Not only are physics engines great for simulating realistic movement, but they're also great for collision detection purposes.

You'll set up the game to use SpriteKit's physics engine to determine when monsters and projectiles collide. At a high level, here's what you're going to do:

  • Set up the physics world. A physics world is the simulation space for running physics calculations. One is set up on the scene by default, and you might want to configure a few properties on it, like gravity.
  • Create physics bodies for each sprite. In SpriteKit, you can associate a shape with each sprite for collision detection purposes, and set certain properties on it. This is called a physics body. Note that the physics body does not have to be the exact same shape as the sprite. Usually it's a simpler, approximate shape rather than pixel-perfect, since that's good enough for most games and performance.
  • Set a category for each type of sprite. One of the properties you can set on a physics body is a category, which is a bitmask indicating the group or groups it belongs to. In this game, you're going to have two categories: one for projectiles and one for monsters. Then later when two physics bodies collide, you can easily tell what kind of sprite you're dealing with by looking at its category.
  • Set a contact delegate. Remember that physics world from earlier? Well, you can set a contact delegate on it to be notified when two physics bodies collide. There you'll write some code to examine the categories of the objects, and if they're the monster and projectile, you'll make them go boom!

Now that you understand the battle plan, it's time to put it into action!

Brody Eller

Contributors

Brody Eller

Author

Felicity Johnson

Tech Editor

Essan Parto

Final Pass Editor

Richard Critz

Team Lead

Over 300 content creators. Join our team.