Trigonometry for Games – Sprite Kit and Swift Tutorial: Part 2/2

Trigonometry for Games – Sprite Kit and Swift Tutorial: Part 2/2

sup { font-size: 80%; vertical-align:super; } blockquote { padding-left: 30px; }

Learn Trigonometry for Game Programming!

Update Note: This is the third incarnation of one of our very popular tutorials – the first version was written by Tutorial Team member Matthijs Hollemans for Cocos2D, and the second version was update to Sprite Kit by Tony Dahbura. This latest version still uses Sprite Kit, but is updated for iOS 8 and Swift.

Welcome back to the Trigonometry for Game Programming series!

In the first part of the series, you learned the basics of trigonometry and saw for yourself how useful it can be for making games. Math doesn’t have to be boring – as long as you have a fun project to apply it to!

In this second and final part of the series, you will extend your simple space game by adding missiles, an orbiting asteroid shield and an animated “game over” screen. Along the way, you’ll learn more about the sine and cosine functions, and see some other useful ways to put the power of trig to work in your games.

Get ready to ride the sine wave back into space!

Getting Started

This tutorial picks up where you left off in the last part. If you don’t have it already, here is the project with all of the source code up to this point.

As of right now, your game has a spaceship and a rotating cannon, each with health bars. While they may be sworn enemies, neither has the ability to damage the other, unless the spaceship flies right into the cannon (which works out better for the cannon, damage-wise).

It’s time for some fireworks. You will now give the player the ability to fire missiles by swiping the screen. The spaceship will launch a missile in the direction of the swipe. For that, you’ll need to add some new properties for tracking touches.

Open GameScene.swift and add the following properties to the class:

let playerMissileSprite = SKSpriteNode(imageNamed:"PlayerMissile")
var touchLocation = CGPointZero
var touchTime: CFTimeInterval = 0

You’ll move the missile sprite from the player’s ship in the direction it’s facing. You’ll use the touch location and time to track where and when the user taps on the screen to trigger a missile.

Then, add these lines at the bottom of the code inside didMoveToView():

playerMissileSprite.hidden = true

Note that the missile sprite is hidden initially; you’ll only make it visible when the player fires. To increase the challenge, the player will only be able to have one missile in flight at a time.

The first touch handling method to add is touchesBegan(), which will be called whenever the user first puts their finger on the touchscreen:

override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
  if let touch = touches.anyObject() as? UITouch {
    let location = touch.locationInNode(self)
    touchLocation = location
    touchTime = CACurrentMediaTime()

This is pretty simple – whenever a touch is detected, you store the touch location and the time. The actual work happens in touchesEnded(), which you’ll add next:

override func touchesEnded(touches: NSSet, withEvent event: UIEvent) {
  let TouchTimeThreshold: CFTimeInterval = 0.3
  let TouchDistanceThreshold: CGFloat = 4
  if CACurrentMediaTime() - touchTime < TouchTimeThreshold && playerMissileSprite.hidden {
    if let touch = touches.anyObject() as? UITouch {
      let location = touch.locationInNode(self)
      let swipe = CGVector(dx: location.x - touchLocation.x, dy: location.y - touchLocation.y)
      let swipeLength = sqrt(swipe.dx * swipe.dx + swipe.dy * swipe.dy)
      if swipeLength > TouchDistanceThreshold {
        // TODO: more code here

The outer if statement calculates the time between starting and ending the swipe. If the time is greater than the TouchTimeThreshold value of 0.3 seconds, the missile doesn’t fire. Also, since the player can only shoot one missile at a time, the touch is ignored if a missile is already flying.

The next part works out what sort of gesture the user made; was it really a swipe, or just a tap? You should only launch missiles on swipes, not taps. You have done this sort of calculation a couple of times already – subtract two coordinates, then use the Pythagorean Theorem to find the distance between them. If the distance is greater than the TouchDistanceThreshold value of 4 points, treat it as an intentional swipe.

Note:You could have used UIKit’s built in gesture recognizers for this, but the aim here is to understand how trigonometry is used behind the scenes to implement this kind of logic.

There are two ways you could make the missile fly:

The first option would be to create a playerMissileVelocity vector, based on the angle that you’re aiming the missile. Inside update(), you would then add this velocity (multiplied by the time delta) to the missile sprite’s position each frame, and check if the missile has flown outside of the visible screen area, so that it can be reset. This is very similar to how you made the spaceship move in Part 1 of this tutorial.

However, unlike the spaceship, the missile never changes course; it always flies in a straight line. So you can take a simpler approach:

The second option is to calculate the final destination of the missile in advance, when you launch it. Then you can use a moveTo() action on the missile sprite and let Sprite Kit animate it to it’s final position.

This saves you from having to check whether the missile has left the visible screen, and is also an opportunity to do some more interesting math!

To begin, replace the // TODO comment in touchesEnded() with the following code:

var angle = atan2(swipe.dy, swipe.dx)
playerMissileSprite.zRotation = angle - 90 * DegreesToRadians
playerMissileSprite.position = playerSprite.position
playerMissileSprite.hidden = false

Here, you use atan2() to convert the swipe vector to an angle, set the sprite’s rotation and position, and make the missile sprite visible.

Now, however, comes the interesting part. You know the starting position of the missile (which is the current position of the player’s ship) and you know the angle (derived from the player’s swipe motion). What you need to calculate now is the destination point of the missile based on these facts.

You already have the direction vector, and you learned in Part 1 how to use normalization to set the length of a vector to whatever you need. But what length do you want? Well that’s the challenging bit: Because you want the missile to stop when it moves outside the screen border, the length it travels depends on the starting position and direction.

The destination point always lies just outside the screen instead of exactly on the edge of the screen, because it looks better if the missile completely flies out of sight before it vanishes. For this purpose, add another constant at the top of GameScene.swift:

let PlayerMissileRadius: CGFloat = 20

Finding the destination point is a bit complicated. If you know that (for example) the player is shooting downward, you can work out the vertical distance the missile needs to fly – that is simply the starting Y-position of the missile, plus the PlayerMissileRadius, but you then need to calculate the X component by determining where the missile will intersect that invisible line.

For missiles that fly off the bottom or top edges of the screen the X component of the destination can be calculated with the following formula:

destination.x = playerPos.x + ((destination.y – playerPos.y) / swipe.dy * swipe.dx)

This is similar to the normalization technique from Part 1, where you scaled up a vector by first dividing both components by the current length, and then multiplying by the desired length. Here, you work out the ratio of the swipe vector’s Y component to the final distance, then multiply the X component by the same value and add it to the ship’s current X position in order to get the destination X coordinate.

For missiles that go off the left or right edges, you’d use essentially the same function, but just swap all the X and Y values.

This technique of extending a vector until it hits an edge is known as projection, and it’s very helpful for all sorts of game applications, such as detecting if an enemy can see the player by projecting a vector along their line of sight and seeing if it hits a wall first, or the player.

There’s a snag, however. If the intersection point is near a corner, it’s not obvious which edge the missile will intersect first:


That’s OK; you’ll just calculate both intersection points, then see which is the shorter distance from the player!

Add the following code immediately beneath the playerMissileSprite.hidden = false line you added to touchesEnded:

//calculate vertical intersection point
var destination1 = CGPoint.zeroPoint
if swipe.dy > 0 {
  destination1.y = size.height + PlayerMissileRadius // top of screen
} else {
  destination1.y = -PlayerMissileRadius // bottom of screen
destination1.x = playerSprite.position.x +
  ((destination1.y - playerSprite.position.y) / swipe.dy * swipe.dx)
//calculate horizontal intersection point
var destination2 = CGPoint.zeroPoint
if swipe.dx > 0 {
  destination2.x = size.width + PlayerMissileRadius // right of screen
} else {
  destination2.x = -PlayerMissileRadius // left of screen
destination2.y = playerSprite.position.y +
  ((destination2.x - playerSprite.position.x) / swipe.dx * swipe.dy)

Here, you’re calculating the two candidate destination points for the missile; now you need to work out which is nearer to the player. Add the following code next, below the code you just wrote:

// find out which is nearer
var destination = destination2
if abs(destination1.x) < abs(destination2.x) || abs(destination1.y) < abs(destination2.y) {
  destination = destination1

You could have used the Pythagorean theorem here to work out the diagonal distance from the player to each intersection point, and chosen the shortest, but there’s a quicker way: you know that since the two possible intersection points lie along the same vector, if either the X or Y component is shorter then the distance as a whole must be shorter – so there’s no need to calculate the diagonal lengths.

Now to fire the missile!

Add this last piece of code to touchesEnded(), immediately after the code you just wrote:

// run the sequence of actions for the firing
let missileMoveAction = SKAction.moveTo(destination, duration: 2)
playerMissileSprite.runAction(missileMoveAction) {
  self.playerMissileSprite.hidden = true

Build and run the app. You can now swipe to shoot bolts of plasma at the turret. Pretty neat, huh? Note that you can only fire one missile at a time – you have to wait until the previous missile has disappeared from the screen before firing again.

There’s still one problem (isn’t there always?) — The missile appears to travel faster or slower depending on the distance it travels.

That’s because, currently, the duration of the animation is hard-coded to last 2 seconds. If the missile needs to travel further, then it goes faster in order to cover more distance in the same amount of time. It would be more realistic if the missiles always travelled at a consistent speed.

Your good friend Sir Isaac Newton can help out here! As Newton discovered, time = distance / speed. You can use Pythagoras to calculate the distance, so there’s just the matter of specifying the speed.

Add another constant to the top of GameScene.swift:

let PlayerMissileSpeed: CGFloat = 300

This is the distance that you want the missile to travel each second. Now, replace the last block of code you added with this new version:

// calculate distance
let distance = sqrt(pow(destination.x - playerSprite.position.x, 2) +
  pow(destination.y - playerSprite.position.y, 2))
// run the sequence of actions for the firing
let duration = NSTimeInterval(distance / PlayerMissileSpeed)
let missileMoveAction = SKAction.moveTo(destination, duration: duration)
playerMissileSprite.runAction(missileMoveAction) {
  self.playerMissileSprite.hidden = true

This time, instead of hard-coding the duration, you’ve derived it from the distance and speed by using Newton’s formula. Run the app again and you’ll see that the missile now always flies at the same speed, no matter how far or close the destination point is.

And that’s how you use trig to set up a moveTo() action. It’s a bit involved, but then it’s largely fire & forget because Sprite Kit does all the work of animating for you.

Meme - not bad

Hitting Your Targets

Right now, the missile completely ignores the cannon – your spaceship might as well be firing a beam of green light!

That’s about to change. As before, you will use a simple radius-based method of collision detection. You already added a PlayerMissileRadius constant, so you’re all set to detect cannon/missile collisions using the same technique as you used for the cannon/ship collision.

Add a new method:

func checkMissileCannonCollision() {
  if !playerMissileSprite.hidden {
    let deltaX = playerMissileSprite.position.x - turretSprite.position.x
    let deltaY = playerMissileSprite.position.y - turretSprite.position.y
    let distance = sqrt(deltaX * deltaX + deltaY * deltaY)
    if distance <= CannonCollisionRadius + PlayerMissileRadius {
      playerMissileSprite.hidden = true
      cannonHP = max(0, cannonHP - 10)
      updateHealthBar(cannonHealthBar, withHealthPoints: cannonHP)

This works pretty much the same as checkShipCannonCollision(): you calculate the distance between the sprites, and consider it a collision if that distance is less than the sum of the radii.

If the collision is detected, first hide the missile sprite and cancel its animations; then reduce the cannon’s hit points, and redraw its health bar. Simple!

Add a call to checkMissileCannonCollision() inside the update() method, immediately after the other updates:


Build and run, then try it out. Finally you can inflict some damage on the enemy!

Inflicting damage

Before moving on, it would be nice if the missile had some sound effects. As with the ship-turret collision before, you can play sounds with a Sprite Kit action. Add the following two properties to GameScene:

var missileShootSound: SKAction!
var missileHitSound: SKAction!

Set them up in didMoveToView():

missileShootSound = SKAction.playSoundFileNamed("Shoot.wav", waitForCompletion: false)
missileHitSound = SKAction.playSoundFileNamed("Hit.wav", waitForCompletion: false)

Now, change the line playerMissileSprite.runAction(missileMoveAction) in touchesEnded() to:

playerMissileSprite.runAction(SKAction.sequence([missileShootSound, missileMoveAction])) {

Rather than a single action to move the missile, you’re setting up a sequence to play the sound and then move the missile.

And, finally, add the following line to checkMissileCannonCollision(), at the bottom of the inner if statement:


That should do it: The missile now shoots out with a ZZAPP sound, and – if your aim is true – hits the turret with a satisfying BOINK!

Challenges for the 1337

Here’s a challenge for you: can you make the cannon shoot back at the spaceship?

You already know all the required pieces, and this will be some really good practice to make sure you understand what we’ve covered so far. Try it out for yourself before you look at the spoiler!

Solution Inside: Make the cannon shoot back SelectShow

To implement this on your own, all you have to do is create a new missile sprite (using CannonMissile.png), calculate the destination, and send the missile on its way. You know the angle because the turret already points at the player. The destination point for the moveTo() action is obviously the position of the player at that moment.

Collision detection with the player works the same way as before: the missile has hit as soon as the distance between the missile and the player becomes less than the sum of their radii. To make the game extra challenging for the player, allow the cannon to shoot more than one missile at a time (hint: you’ll probably want to set up an array of missile sprites).


Got that done already? Think you’re pretty hot stuff, huh? Here’s another challenge for you!

Currently, your missiles fly to their destination point in a straight line. But what if the missiles were heat-seeking? A heat-seeking missile adjusts its course when it detects that the player has moved.

You’ve got the power of trig on your side, so how would you do it? Hint: instead of calculating the speed and direction of the missile just once, you would do it again and again on each frame, as you do with the ship.

Solution Inside: Make the missile heat seeking SelectShow

Since you’re calculating the speed and direction of the missile on each frame, you can no longer use a moveTo() action. Instead, you have to do the animation by yourself. Continuously adjust the speed of the missile based on the new angle that it makes with the player. To read more about this sort of “seeking” behavior, check out the Game AI tutorial.

Make sure to give the guided missile a limited lifetime, so the player can avoid it if they keep dodging it long enough, or the game might become a bit too hard to play!


How’d you do? Is your spaceship dodging guided missiles like Tom Cruise, or still flying around scot-free?

Adding an Orbiting Shield

To make the game more challenging, you will give the enemy a shield. The shield will be a magical asteroid sprite that orbits the cannon and destroys any missiles that come near it.

Add a couple more constants to the top of GameScene.swift:

let OrbiterSpeed: CGFloat = 120
let OrbiterRadius: CGFloat = 60
let OrbiterCollisionRadius: CGFloat = 20

And some new properties to GameScene:

let orbiterSprite = SKSpriteNode(imageNamed:"Asteroid")
var orbiterAngle: CGFloat = 0

Initialize the new sprite inside didMoveToView(), after the other sprites:


Then, add the following new method to GameScene:

func updateOrbiter(dt: CFTimeInterval) {
  // 1
  orbiterAngle = (orbiterAngle + OrbiterSpeed * CGFloat(dt)) % 360
  // 2
  let x = cos(orbiterAngle * DegreesToRadians) * OrbiterRadius
  let y = sin(orbiterAngle * DegreesToRadians) * OrbiterRadius
  // 3
  orbiterSprite.position = CGPointMake(cannonSprite.position.x + x, cannonSprite.position.y + y)

The asteroid will orbit around the cannon. In other words, it describes a circular path, round and round and round and round. To accomplish this, you need two pieces: the radius that determines how far the asteroid is from the center of the cannon, and the angle that describes how far it has rotated around that center point.

This is what updateOrbiter() does:

  1. It increments the angle by a certain speed (from the OrbiterSpeed constant), adjusted for the delta time. The angle is then wrapped to the 0 – 360 range using % 360. That isn’t strictly necessary, as sin() and cos() work correctly with angles outside of that range, however if the angles get too large then floating point precision may become a problem (also, it’s easier to visualise angles if they are in this range, for debugging purposes).
  2. It calculates the new X- and Y-positions for the orbiter using sin() and cos(). These take the radius (which forms the hypotenuse of the triangle) and the current angle, and return the adjacent and opposite sides, respectively. More about this in a second.
  3. It sets the new position of the orbiter sprite by adding the X- and Y-positions to the center position of the cannon.

Note: You’re using the % operator to constrain the orbiterAngle to the range 0 – 360, even though orbiterAngle is a CGFloat. In C/Objective-C you would have had to use the fmod() function for this, because the % operator only works with integers, but Swift’s % operator supports floating point values too!

You have briefly seen sin() and cos() in action, but it may not have been entirely clear how they worked. You know that both of these functions can be used to calculate the lengths of the other sides of a right triangle, once you have an angle and the hypotenuse.

But aren’t you curious why you can actually do that?

Let’s draw a circle:

The illustration above exactly depicts the situation of the asteroid orbiting around the cannon. The circle describes the path of the asteroid and the origin of the circle is the center of the cannon.

The angle starts at zero degrees but increases all the time until it ends up right back at the beginning. As you can see it, is the radius of the circle that determines how far away from the center the asteroid is placed.

So, given the angle and the radius, you can derive the X- and Y-positions using the cosine and sine, respectively:

Now let’s take a look at a plot of a sine wave and a cosine wave:

On the horizontal axis are the degrees of a circle, from 0 to 360 (or 0 to 2π radians, if you’re a mathematician). The vertical axis usually goes from -1 to +1, but if your circle has a radius that is greater than one (and they tend to) then the vertical axis really goes from –radius to +radius.

As the angle increases from 0 to 360, find the angle on the horizontal axis in the plots for the cosine and sine waves. The vertical axis then tells you what the values for x and y are:

  • If the angle is 0 degrees, then cos(0) is 1 * radius but sin(0) is 0 * radius. That corresponds exactly to the (x, y) coordinate in the circle: x is equal to the radius, but y is 0.
  • If the angle is 45 degrees, then cos(45) is 0.707 * radius and so is sin(45). This means x and y are both the same at this point on the circle. (Note: if you’re trying this out on a calculator, then switch it to DEG mode first. You’ll get radically different answers if it’s in RAD mode (no pun intended :])
  • If the angle is 90 degrees, then cos(90) is 0 * radius and sin(90) is 1 * radius. You’re now at the top of the circle where the (x, y) coordinate is (0, radius).
  • And so on, and so on. To get a more intuitive feel for how the coordinates in the circle relate to the values of the sine, cosine and even tangent functions, try out this cool interactive circle.

Make sense? Awesome. Did you also notice that the curves of the sine and cosine are very similar? In fact, the cosine wave is simply the sine wave shifted by 90 degrees. Go ahead and impress your friends and family with your knowledge of the mathematical origins of sine and cosine. :]

Back to coding. Add a call to updateOrbiter() at the bottom of update():


Build and run the app. You should now have an asteroid that perpetually circles the enemy cannon.

You can also make the asteroid spin around its own axis: Add the following line to the bottom of updateOrbiter():

orbiterSprite.zRotation = orbiterAngle * DegreesToRadians

By setting the rotation to orbiterAngle, the asteroid always stays oriented in the same position relative to the cannon, much like the Moon always shows the same side to the Earth. Even though it looks like it isn’t spinning, it certainly is!

If you insert a minus sign, so that orbiterSprite.zRotation = -orbiterAngle * DegreesToRadians, the Asteroid will appear to spin on its axis relative to the cannon, instead of always facing it. Pick whichever effect you like best. Build and run to play around with it for a bit.

Let’s give the orbiter a purpose. If the missile comes too close, the asteroid will destroy it before it gets a chance to do any damage to the cannon. Add the following method:

func checkMissileOrbiterCollision() {
  if !playerMissileSprite.hidden {
    let deltaX = playerMissileSprite.position.x - orbiterSprite.position.x
    let deltaY = playerMissileSprite.position.y - orbiterSprite.position.y
    let distance = sqrt(deltaX * deltaX + deltaY * deltaY)
    if distance < OrbiterCollisionRadius + PlayerMissileRadius {
      playerMissileSprite.hidden = true
      orbiterSprite.runAction(SKAction.scaleTo(1, duration: 0.5))

And don’t forget to include a call to it at the end of update():


This should look pretty familiar; it’s basically the same thing you wrote for checkMissileCannonCollision(). When the collision is detected, the missile sprite is removed. This time, you don’t play a sound, but as an added visual flourish, you increase the size of the asteroid sprite by 2X, and then immediately animate it scaling back down again. This makes it look like the orbiter “ate” the missile!

Build and run to see your new orbiting shield in action.

Orbiting missile shield

Game Over, With Trig!

There is still more that you can do with sines and cosines. They’re not just useful for calculating things with triangles and creating circular paths – they also come in handy for animations.

A good place to demo such an animation is the game over screen. Add the following constant to the top of GameScene.swift:

let DarkenOpacity: CGFloat = 0.8

And add a few new properties to the top of the GameScene class:

var darkenLayer: SKSpriteNode?
var gameOverLabel: SKLabelNode?
var gameOver = false
var gameOverElapsed: CFTimeInterval = 0

You’ll use these properties to keep track of the game state and the nodes to show the “Game Over” information.

Next, add this method to the class:

func checkGameOver(dt: CFTimeInterval) {
  // 1
  if playerHP > 0 && cannonHP > 0 {
  if !gameOver {
    // 2
    gameOver = true
    gameOverElapsed = 0
    // 3
    let fillColor = UIColor(red: 0, green: 0, blue: 0, alpha: 1)
    darkenLayer = SKSpriteNode(color: fillColor, size: size)
    darkenLayer?.alpha = 0
    darkenLayer/.position = CGPoint(x: size.width/2, y: size.height/2)
    // 4
    let text = (playerHP == 0) ? "GAME OVER" : "Victory!"
    gameOverLabel = SKLabelNode(fontNamed: "Helvetica")
    gameOverLabel?.text = text
    gameOverLabel?.fontSize = 24
    gameOverLabel?.position = CGPoint(x: size.width/2 + 0.5, y: size.height/2 + 50)
  } else {
    // 5
    darkenLayer?.alpha = min(DarkenOpacity, darkenLayer!.alpha + CGFloat(dt))

This method checks whether the game is done, and if so, handles the game over animation:

  1. The game keeps on going until either the player or cannon run out of health points.
  2. When the game is over, you set gameOver to true, and disable the accelerometer.
  3. Create a new, all-black color layer and add it on top of everything else. Its alpha opacity is set to 0 initially, so that it is completely transparent. Later in this method, you’ll animate the opacity value of this layer so that it appears to fade in.
  4. Add a new text label and place it on the screen. The text is either “Victory!” if the player won or “Game Over” if the player lost, determined based on the player’s health points.
  5. The above steps only happen once to set up the game over screen – every time after that, the code enters the else clause. Here, you animate the alpha of the new color layer from 0 to 0.8 (the DarkenOpacity constant) – almost completely opaque, but not quite.

Add a call to checkGameOver() at the bottom of update():


And add a small snippet of logic to the top of touchesEnded():

if gameOver {
  let scene = GameScene(size: size)
  let reveal = SKTransition.flipHorizontalWithDuration(1)
  view?.presentScene(scene, transition: reveal)

This restarts the game when the user taps on the game over screen.

Build and run, then try it out. Shoot at the cannon or collide your ship with it until one of you runs out of health. The screen will fade to black and the game over text will appear. The game no longer responds to the accelerometer, but the animations still keep going:

Game over

This is all fine and dandy, but where are the sine and cosines? As you may have noticed, the fade in animation of the black layer was very linear. It just goes from transparent to opaque at a consistent rate.

We can do better than this – we can use sin() to alter the timing of the fade. This is known as “easing” and the effect you will apply here is known as an “ease out”.

Note: You could just use runAction() to do the alpha fade, as it supports various easing modes. Again, the purpose of this tutorial is not to learn Sprite Kit; it’s to learn the math behind it, including easing!

Add a new constant at the top of GameScene.swift:

let DarkenDuration: CFTimeInterval = 2

Next, change the single line in the else statement in checkGameOver() to:

gameOverElapsed += dt
if gameOverElapsed < DarkenDuration {
  var t = CGFloat(gameOverElapsed / DarkenDuration)
  t = sin(t * Pi / 2) // ease out
  darkenLayer.alpha = DarkenOpacity * t

gameOverElapsed keeps track of how much time has passed since the game ended. It takes two seconds to fade in the black layer (DarkenDuration). The variable t determines how much of that duration has passed by. It always has a value between 0.0 and 1.0, regardless of how long DarkenDuration really is.

Then you perform the magic trick:

t = sin(t * Pi / 2) // ease out

This converts t from a linear interpolation into one that breathes a bit more life into things:

Build and run to see the new “ease out” effect. If you find it hard to see the difference, then try it with the “ease out” line commented out, or change the duration of the animation. The effect is subtle, but it’s there.

Note: If you want to play with the values and test the effect quickly, try setting cannonHP to 10 so you can end the game with a single shot.

Easing is a subtle effect, so let’s wrap up with a much more obvious bounce effect – because things that bounce are always more fun!

Inside that else clause in checkGameOver(), add the following after the previous code:

// label position
let y = abs(cos(CGFloat(gameOverElapsed) * 3)) * 50
gameOverLabel.position = CGPoint(x: gameOverLabel.position.x, y: size.height/2 + y)

OK, what’s happening here? Recall what a cosine looks like:

If you take the absolute value of cos() – using abs() – then the section that would previously go below zero is flipped. The curve already looks like something that bounces, don’t you think?

Because the output of these functions lies between 0.0 and 1.0, you multiply it by 50 to stretch it out to 0-50. The argument to cos() is normally an angle, but you’re giving it the gameOverElapsed time to make the cosine move forward through its curve.

The factor of 3 is just to make it go a bit faster. You can tinker with these values until you have something that you think looks cool.

Build and run to check out the bouncing text:

bouncing text

You’ve used the shape of the cosine to describe the bouncing motion of the text label. These cosines are useful for all sorts of things!

One last thing you can do is let the bouncing motion lose amplitude over time. You do this by adding a damping factor. Create a new property in GameScene:

var gameOverDampen: CGFloat = 0

The idea here is when the game ends, you’ll need to reset this value to 1.0 so the damping takes effect. Over time as the text bounces, the damping will slowly fade off to 0 again.

In checkGameOver(), find the if block where you set gameOver to true and add the following line right after you set that property:

gameOverDampen = 1

In the else block, change the code underneath the “// label position” comment to be the following:

let y = abs(cos(CGFloat(gameOverElapsed) * 3)) * 50 * gameOverDampen
gameOverDampen = max(0, gameOverDampen - 0.3 * CGFloat(dt))
gameOverLabel.position = CGPoint(x: gameOverLabel.position.x, y: size.height/2 + y)

It’s mostly the same as before, but you multiply the y-value by the damping factor and, simultaneously, you reduce this damping factor slowly from 1.0 back to 0.0 (but never less than 0; that’s what the max() prevents). Build and run, then try it out!

Where to Go from Here?

Here is the full TrigBlasterPart2 from this Trigonometry for Game Programming tutorial series.

Congratulations, you have delved into the depths of sine, cosine and tangent, and you have witnessed some examples of how they can be applied in a real game. I hope you’ve seen how handy Trigonometry really is for games!

Note that we didn’t talk much about arcsine and arccosine. They are much less useful in practice than arctangent, although a common use for arccosine is to find the angle between two arbitrary vectors – for example, to model the reflection of a light beam in a mirror, or to calculate how bright an object should be depending on its angle to a light source.

You can find another great example of the usefulness of trigonometric functions in the Tiny Wings tutorial. It uses cosine to give the hills from the game nicely curved shapes.

If you fancy using your new-found skills for more game development, but don’t know where to start, then why not try out our book iOS Games by Tutorials. It will certainly kick start your development!

Drop by the forums to share your successes and agonies with trig. And use your new powers wisely!

Credits: The graphics for this game are based on a free sprite set by Kenney Vleugels. The sound effects are based on samples from

Trigonometry for Games – Sprite Kit and Swift Tutorial: Part 2/2 is a post from: Ray Wenderlich

The post Trigonometry for Games – Sprite Kit and Swift Tutorial: Part 2/2 appeared first on Ray Wenderlich.



Write a comment