## Trigonometry for Game Programming: Part 2/2

Welcome back to the Trigonometry for Game Programming series!

In the first part of the series, you learned the basics of trigonometry and experienced for yourself how useful it is for making games. You saw that math doesn’t have to be boring – as long as you have a fun project to apply it to, such as making your own games.

Oh yes, and you built the foundation for a solid little space shooter game!

In this second and final part of the series, you will add missiles, an orbiting asteroid shield and an animated “game over” screen to your game. Along the way, you’ll learn more about sine and cosine 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.

It’s time for some fireworks. You will now give the player the ability to fire missiles at the cannon by swiping the screen. The spaceship will then launch a missile in the direction of that swipe. For that, you first need to enable touch handling on the layer.

Open the Xcode project and add these instance variables to *HelloWorldLayer.m*:

@implemenation HelloWorldLayer { ... CCSprite *_playerMissileSprite; CGPoint _touchLocation; CFTimeInterval _touchTime; } |

Also add these lines at the bottom of the code inside `init`

:

self.touchEnabled = YES; _playerMissileSprite = [CCSprite spriteWithFile:@"PlayerMissile.png"]; _playerMissileSprite.visible = NO; [self addChild:_playerMissileSprite]; |

You initially set the missile sprite to be hidden and only make it visible when the player fires.

Now add the methods to handle the touches, starting with the one that is called whenever the user first puts her finger on the touchscreen:

- (void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { UITouch *touch = [touches anyObject]; CGPoint location = [[CCDirector sharedDirector] convertToGL:[touch locationInView:[touch view]]]; _touchLocation = location; _touchTime = CACurrentMediaTime(); } |

This is pretty simple – you store the touch location and the time of the touch in the new instance variables.

The actual work happens in `ccTouchesEnded`

, which you add next:

- (void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { if (CACurrentMediaTime() - _touchTime < 0.3 && !_playerMissileSprite.visible) { UITouch *touch = [touches anyObject]; CGPoint location = [[CCDirector sharedDirector] convertToGL:[touch locationInView:[touch view]]]; CGPoint diff = ccpSub(location, _touchLocation); if (ccpLength(diff) > 4.0f) { // TODO: more code here } } } |

This determines whether there hasn’t been too much time between starting and ending the swipe, 0.3 seconds at most (otherwise, the missle doesn’t fire). Also, the player can only shoot one missile at a time, so the touch is ignored if a missile is already flying.

The last bit figures out what sort of gesture the user made: was it really a swipe, or just a tap? You only launch missiles on swipes, not taps.

*Note:* 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 – but this code looks quite different.

That’s because you’re using one of the handy built-in Cocos2D functions to do the calculation that you used to do by hand for you. I mentioned that these exist in the previous tutorial but you wouldn’t be using them so you could see how things work the long way.

Well, now that you know how things work, why not use these to save time? The built-in Cocos2D functions work on `CGPoint`

structures. If you look at the code, you’ll see `ccpLength()`

is nothing more than the Pythagorean formula you’ve seen before:

CGFloat ccpLength(const CGPoint v) { return sqrtf(v.x*v.x + v.y*v.y); } |

It is common in games to combine the x and y values into one type such as `CGPoint`

. Such a value is referred to as a *vector*, a topic large enough that it deserves its own tutorial.

There are two ways you could make the missile fly.

The first would be to create new `_playerMissileSpeedX`

and `Y`

instance variables and fill them in with the correct speed values, based on the angle that you’re aiming the missile. Inside `update`

, you would then add this speed to the missile sprite’s position, and check whether the missile flew outside of the visible screen so the player can fire again. This is very similar to how you made the spaceship move in Part 1 of this tutorial.

However, the missile doesn’t need to change course on its way: it always flies in a straight line. So you can take an alternative approach: calculating the destination point when you launch the missile. Then you can set a `CCMoveTo`

action on the missile sprite and let Cocos2D handle the rest. This also saves you from having to check whether the missile has left the visible screen.

I bet you saw this coming: the second approach is exactly what you are going to implement right now, and you will use trig to do it.

To begin, add the following lines to the TODO section in `ccTouchesEnded`

:

float angle = atan2f(diff.y, diff.x); _playerMissileSprite.rotation = 90.0f - CC_RADIANS_TO_DEGREES(angle); _playerMissileSprite.position = _playerSprite.position; _playerMissileSprite.visible = YES; |

This is nothing fancy. You calculate the angle, set up the sprite’s rotation and position, and make the missile sprite visible.

Now, however, comes the math. You know the starting position of the missile (which is the 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.

There are actually four different situations that you need to handle, so let’s first consider the one where the user swiped in a downward motion:

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. For this purpose, add a constant at the top *HellowWorldLayer.m* and call it Margin:

const float Margin = 20.0f; |

To find the destination point, you need more than just the angle. Fortunately, if you know that the player is shooting downward, you also know the vertical distance the missile needs to fly. That is simply the starting y-position of the missile, plus the margin. (Remember that the bottom of the screen is y = 0 in Cocos2D.)

Now you have the angle and the adjacent side. To get the destination point, you simply need to calculate the opposite side of the triangle and add it to the starting x-position of the missile.

That sounds like a job for the tan() function – not the arc tangent that you’ve been using up to now, but the normal one:

tan(angle) = opposite / adjacent

Or for your purposes:

opposite = tan(angle) * adjacent

There’s only one snag. The angle that is pictured in the above diagram isn’t really the angle you measured, because the angle you get back from `atan2f()`

is always relative to the 0 degree line of the circle:

Note that in this particular diagram, the adjacent and opposite sides are swapped – it is now the adjacent you’re looking for. So the formula becomes:

adjacent = opposite / tan(angle)

You could probably make this work, but that division makes me cringe. What if tan(angle) happens to be 0? Then you’ll have a division-by-zero on your hands. I’d much rather stick to the first formula, the one that calculates the opposite.

It turns out that this is actually no problem if you remember that beta = (90 – alpha). In other words, if you position yourself in the opposite corner of the triangle and use an angle of (90 – alpha), then you can use that first formula without problems.

Be careful: now that the opposite side points the other way around, you need to subtract it rather than add it to the starting x-position.

Let’s put all this in code. Modify the `ccTouchesEnded`

method to look like the following:

if (ccpLength(diff) > 4.0f) { float angle = atan2f(diff.y, diff.x); _playerMissileSprite.rotation = 90.0f - CC_RADIANS_TO_DEGREES(angle); _playerMissileSprite.position = _playerSprite.position; _playerMissileSprite.visible = YES; float adjacent, opposite; CGPoint destination; // 1 angle = M_PI_2 - angle; adjacent = _playerMissileSprite.position.y + Margin; opposite = tanf(angle) * adjacent; destination = ccp(_playerMissileSprite.position.x - opposite, -Margin); // 2 id action = [CCSequence actions: [CCMoveTo actionWithDuration:2.0f position:destination], [CCCallBlock actionWithBlock:^ { _playerMissileSprite.visible = NO; }], nil]; [_playerMissileSprite runAction:action]; [[SimpleAudioEngine sharedEngine] playEffect:@"Shoot.wav"]; } |

This does two things:

- It adjusts the angle so you’re looking at it from the other corner. It then calculates the length of the adjacent side, and uses the tangent function to find the length of the opposite side. Finally, it calculates the destination coordinate.
- It creates a sequence of actions, starting with a
`CCMoveTo`

. Once the sprite has reached its destination, the`CCCallBlock`

action will make the sprite invisible again.

Build and run, and try it out. Move the ship so that it is about halfway centered on the screen and then swipe down. You should see a missile fly along the path that you swiped. Note that you can only fire one missile at a time – the player should have to wait until the previous missile has disappeared from the screen before firing again.

If you try swiping in any other direction, you’ll notice that the missile doesn’t quite fly where you want. That’s because currently you have only handled the situation where the destination point lies below the bottom of the screen. Each of the other screen edges requires slightly different calculations.

Replace the code that calculates the destination point (the “// 1″ section) with this:

if (angle <= -M_PI_4 && angle > -3.0f * M_PI_4) { // Shoot down angle = M_PI_2 - angle; adjacent = _playerMissileSprite.position.y + Margin; opposite = tanf(angle) * adjacent; destination = ccp(_playerMissileSprite.position.x - opposite, -Margin); } else if (angle > M_PI_4 && angle <= 3.0f * M_PI_4) { // Shoot up angle = M_PI_2 - angle; adjacent = _winSize.height - _playerMissileSprite.position.y + Margin; opposite = tanf(angle) * adjacent; destination = ccp(_playerMissileSprite.position.x + opposite, _winSize.height + Margin); } else if (angle <= M_PI_4 && angle > -M_PI_4) { // Shoot right adjacent = _winSize.width - _playerMissileSprite.position.x + Margin; opposite = tanf(angle) * adjacent; destination = ccp(_winSize.width + Margin, _playerMissileSprite.position.y + opposite); } else // angle > 3.0f * M_PI_4 || angle <= -3.0f * M_PI_4 { // Shoot left adjacent = _playerMissileSprite.position.x + Margin; opposite = tanf(angle) * adjacent; destination = ccp(-Margin, _playerMissileSprite.position.y - opposite); } |

You have actively divided the screen into four sections, like slices of a pie:

Each of these sections calculates a slightly different destination point. Notice that the code for shooting left comes last, because here you have the issue of switching from +180 to -180 degrees again (recall that this is the range of values that `atan2()`

returns).

Now you should be able to fire in any direction! Build and run and give it a try.

There is still one problem (how could there not be?). Sometimes the missile appears to be going faster than at other times.

That’s because currently, the duration of the animation is hard-coded to last 2 seconds. If the missile needs to travel far, then it goes faster in order to cover more distance in the same amount of time.

Instead what you want is for each missile to always travel at the same speed. The hypotenuse comes to the rescue once more!

First, add a new constant at the top of *HelloWorldLayer.m*:

const float PlayerMissileSpeed = 300.0f; |

This is the distance that you want the missile to travel per second. By calculating the length of the hypotenuse, you know the actual distance that the missile travels. To get the needed duration of the animation, you divide those two distances.

Sounds complicated? Nah…

In `ccTouchesEnded`

, change the code that creates the action (the // 2 section) to the following:

float hypotenuse = sqrtf(adjacent*adjacent + opposite*opposite); ccTime duration = hypotenuse / PlayerMissileSpeed; id action = [CCSequence actions: [CCMoveTo actionWithDuration:duration position:destination], [CCCallBlock actionWithBlock:^ { _playerMissileSprite.visible = NO; }], nil]; |

That’s all there is to it. Build and run the app again. Now the missile 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 `CCMoveTo`

action. It’s a bit involved, but then it’s largely fire & forget because Cocos2D does all the work of animating for you.

## 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.

Add a new constant to the source at the top of *HelloWorldLayer.m*:

const float CannonHitRadius = 25.0f; |

For simplicity’s sake, you will assume that the missile itself doesn’t have a radius, so whenever its center point goes within the cannon hit radius, that will be sufficient to register as a collision. That’s precise enough for this game.

Add a new method:

- (void)updatePlayerMissile:(ccTime)dt { if (_playerMissileSprite.visible) { float deltaX = _playerMissileSprite.position.x - _turretSprite.position.x; float deltaY = _playerMissileSprite.position.y - _turretSprite.position.y; float distance = sqrtf(deltaX*deltaX + deltaY*deltaY); if (distance < CannonHitRadius) { [[SimpleAudioEngine sharedEngine] playEffect:@"Hit.wav"]; _cannonHP = MAX(0, _cannonHP - 10); _playerMissileSprite.visible = NO; [_playerMissileSprite stopAllActions]; } } } |

This should be old hat by now: you calculate the distance and then play a sound effect when the distance has become smaller than the hit radius.

Note that you need to call `stopAllActions`

on the missile sprite, because you want it to stop moving. This tells the `CCMoveTo`

action that it is no longer needed.

Call this new method from within `update`

. Place it directly below the call to `updatePlayer`

.

[self updatePlayerMissile:dt]; |

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

## Challenges for the 1337

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

Try to see if you can figure it out – 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 solution!

Got that, and think you’re some hot stuff? 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. Give it a try.

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 *HelloWorldLayer.m*:

const float OrbiterSpeed = 120.0f; // degrees per second const float OrbiterRadius = 60.0f; // degrees const float OrbiterCollisionRadius = 20.0f; |

And some new instance variables as well:

@implementation HelloWorldLayer { ... CCSprite *_orbiterSprite; float _orbiterAngle; // in degrees } |

I added the “in degrees” comments above because unlike the other angles, which were in radians, here you’ll work with degrees. They are just easier to wrap your head around.

Initialize the new sprite inside `init`

after all the previous code:

_orbiterSprite = [CCSprite spriteWithFile:@"Asteroid.png"]; [self addChild:_orbiterSprite]; |

And add a new method:

- (void)updateOrbiter:(ccTime)dt { // 1 _orbiterAngle += OrbiterSpeed * dt; _orbiterAngle = fmodf(_orbiterAngle, 360.0f); // 2 float x = cosf(CC_DEGREES_TO_RADIANS(_orbiterAngle)) * OrbiterRadius; float y = sinf(CC_DEGREES_TO_RADIANS(_orbiterAngle)) * OrbiterRadius; // 3 _orbiterSprite.position = ccp(_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:

- It increments the angle by a certain speed (from the
`OrbiterSpeed`

constant), adjusted for the delta time. Because we as humans like to think of angles as anything between 0 and 360 degrees, you use`fmodf()`

to wrap the angle around back to 0 once it becomes greater than 360. You cannot use the % operator for this, because it only works on integers, but`fmodf()`

does the same thing for floats (and`fmod()`

does the same thing for doubles). Wrapping the angles isn’t strictly necessary but it helps you stay sane when you’re trying to debug something like this. - It calculates the new x- and y-positions for the orbiter using the sine and cosine functions. 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.
- It sets the new position of the sprite by adding the x- and y-positions to the center position of the cannon.

You have briefly seen `sinf()`

and `cosf()`

in action, but it may not have been entirely clear how they worked. Sure, you have the formulas memorized (really? :]) and you know that both of these functions can be used to calculate the lengths of the other sides, once you have an angle and the hypotenuse.

But aren’t you curious *why* you can actually do that? I thought you were!

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… 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`

:

- (void)update:(ccTime)dt { ... [self updateOrbiter:dt]; } |

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.rotation = -_orbiterAngle; |

By setting the rotation to negative `_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!

Remove the minus sign to give the asteroid extra spin that you can see. 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 at the bottom of `updateOrbiter`

:

if (_playerMissileSprite.visible) { float deltaX = _playerMissileSprite.position.x - _orbiterSprite.position.x; float deltaY = _playerMissileSprite.position.y - _orbiterSprite.position.y; float distance = sqrtf(deltaX*deltaX + deltaY*deltaY); if (distance < OrbiterCollisionRadius) { _playerMissileSprite.visible = NO; [_playerMissileSprite stopAllActions]; _orbiterSprite.scale = 2.0f; [_orbiterSprite runAction:[CCScaleTo actionWithDuration:0.5f scale:1.0f]]; } } |

No surprises for you here. It’s the same code you’ve seen several times now. Just remember to stop the `CCMoveTo`

action that is on the missile sprite. For added visual effect, you also scale the asteroid sprite momentarily. This makes it look like the orbiter “ate” the missile.

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

## 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 – they also come in handy for animations.

A good place to show an example of such an animation is the game over screen. Add a few new instance variables in the implementation block at the top of *HelloWorldLayer.m*:

@implementation HelloWorldLayer { ... CCLabelTTF *_gameOverLabel; CCLayerColor *_darkenLayer; BOOL _gameOver; CFTimeInterval _gameOverElapsed; } |

And a new method:

- (void)checkGameOver:(ccTime)dt { // 1 if (_playerHP > 0 && _cannonHP > 0) // not game over yet { return; } if (!_gameOver) { // 2 _gameOver = YES; _gameOverElapsed = 0.0; self.accelerometerEnabled = NO; // 3 _darkenLayer = [[CCLayerColor alloc] initWithColor:ccc4(0, 0, 0, 255)]; _darkenLayer.opacity = 0; [self addChild:_darkenLayer]; // 4 NSString *text = (_playerHP == 0) ? @"GAME OVER" : @"Victory!"; _gameOverLabel = [[CCLabelTTF alloc] initWithString:text fontName:@"Helvetica" fontSize:24.0f]; _gameOverLabel.position = ccp(_winSize.width/2.0f + 0.5f, _winSize.height/2.0f + 50.0f); [self addChild:_gameOverLabel]; } else { // 5 if (_darkenLayer.opacity < 200) { float newOpacity = fminf(200.0f, _darkenLayer.opacity + 255.0f * dt); _darkenLayer.opacity = newOpacity; } } } |

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

- The game keeps on going until either the player or the cannon runs out of hit points.
- When the game is over, set
`_gameOver`

to YES and disable the accelerometer. - Create a new, all-black color layer and add it on top of everything else. Set its opacity to 0 so that it is completely see-through. Elsewhere in this method you will animate the opacity value of this layer so that it appears to fade in.
- 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.
- 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 opacity of the new color layer from 0 to 200 – almost completely opaque, but not quite.

Call `checkGameOver`

at the bottom of `update`

:

- (void)update:(ccTime)dt { ... [self checkGameOver:dt]; } |

And add a small snippet of logic to the top of `ccTouchesEnded`

:

- (void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { if (_gameOver) { CCScene *scene = [HelloWorldLayer scene]; [[CCDirector sharedDirector] replaceScene:[CCTransitionZoomFlipX transitionWithDuration:0.5f scene:scene]]; return; } ... } |

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 hit points. 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. That gives it a nice twist!

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 in a conventional fashion.

You can be better than conventional – you can use `sinf()`

to subtly change the timing. This is often known as “easing” and the effect you will apply in particular is known as an “ease out”.

Add the new constant at the top of *HelloWorldLayer.m*:

const CFTimeInterval DarkenDuration = 2.0; |

Next, change the code in the `else`

statement in `checkGameOver`

to:

- (void)checkGameOver:(ccTime)dt { ... } else { _gameOverElapsed += dt; if (_gameOverElapsed < DarkenDuration) { float t = _gameOverElapsed / DarkenDuration; t = sinf(t * M_PI_2); // ease out _darkenLayer.opacity = 200.0f * 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 = sinf(t * M_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 that “magic” line commented out, or change the duration of the animation. The effect is subtle, but it’s there.

*Note:* If you’re a big fan of Cocos2D actions, then you may have used the `CCEaseSineIn`

action (or any of the other ease in variations) to accomplish a similar effect. Now you know how it works. :]

There is one more thing I’d like to show you. Let’s make the game over text bounce, because things that bounce are always more fun.

Inside that `else`

clause in `checkGameOver`

, add the following so that the method looks like so:

- (void)checkGameOver:(ccTime)dt { ... } else { _gameOverElapsed += dt; if (_gameOverElapsed < DarkenDuration) { float t = _gameOverElapsed / DarkenDuration; t = sinf(t * M_PI_2); // ease out _darkenLayer.opacity = 200.0f * t; } // Game Over Label Position float y = fabsf(cosf(_gameOverElapsed * 3.0f)) * 50.0f; _gameOverLabel.position = ccp(_gameOverLabel.position.x, _winSize.height/2.0f + y); } } |

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

If you take the absolute value of `cosf()`

– using `fabsf()`

– 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 a little. The argument to `cosf()`

is normally an angle, but you’re giving it the `_gameOverElapsed`

time to make the cosine move forward through its curve.

The factor 3.0 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:

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 height over time. You do this by adding a damping factor. Create a new instance variable in the `HelloWorldLayer`

implementation block:

@implementation HelloWorldLayer { ... float _gameOverDampen; } |

In `checkGameOver`

, set `_gameOverDampen`

. Do this at the beginning of the `if`

block, like so:

- (void)checkGameOver:(ccTime)dt { ... if (!_gameOver) { // 2 _gameOver = YES; _gameOverDampen = 1.0f; ... } ... } |

In the `else`

block, change the code underneath the “// Game Over Label Position” comment to be the following:

float y = fabsf(cosf(_gameOverElapsed * 3.0f)) * 50.0f * _gameOverDampen; _gameOverDampen = fmaxf(0.0f, _gameOverDampen - 0.3f * dt); _gameOverLabel.position = ccp(_gameOverLabel.position.x, _winSize.height/2.0f + y); |

It’s mostly the same as before, but you multiply the y-value with 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 `fmaxf()`

prevents). Build and run, then try it out!

## Where to Go from Here?

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

Congratulations, you have peered even deeper into the true natures of sine, cosine and tangent, and you have tried them out to see some examples of how they’re useful in a real game. I hope you’ve seen how handy Trigonometry really is for games!

Note that this series didn’t talk so much about arcsin and arccos. They are much less useful in practice than arctan. One common use for arccos 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 one of the starter kits. These 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 freesound.org.

## User Comments

## 5 Comments

I have been trying to add the capability to fire more than 1 shot at a time. To that effect I have created an array of 5 player missiles, and I check that array every time for collisions, etc. Everything compiles but keeps operating 1 shot at a time. I must have a flaw in the logic but I fail to detect where it is.

How do you normally implement a feature of keeping track of several shots at a time ? This is what I am doing:

Declaration:

`CCSprite *_playerMissileSprite[4];`

in init:

`for (int i =0; i<5; i++) {`

_playerMissileSprite[i] = [CCSprite spriteWithFile:@"PlayerMissile.png"];

_playerMissileSprite[i].visible = NO;

[self addChild:_playerMissileSprite[i]];

}

Checking for a hit:

`for (int i=0; i<5; i++) {`

if (_playerMissileSprite[i].visible)

{

float deltaX = _playerMissileSprite[i].position.x - _turretSprite.position.x;

float deltaY = _playerMissileSprite[i].position.y - _turretSprite.position.y;

float distance = sqrtf(deltaX*deltaX + deltaY*deltaY);

if (distance < CannonHitRadius)

{

[[SimpleAudioEngine sharedEngine] playEffect:@"Hit.wav"];

_cannonHP = MAX(0, _cannonHP - 10);

_playerMissileSprite[i].visible = NO;

[_playerMissileSprite[i] stopAllActions];

}

}

}

Firing using sneaky button:

`if (attackButton.active == YES) {`

for (int i=0; i<5; i++) {

if (!_playerMissileSprite[i].visible) {

float angle = _playerAngle;

_playerMissileSprite[i].rotation = 90.0f - CC_RADIANS_TO_DEGREES(angle);

_playerMissileSprite[i].position = _playerSprite.position;

_playerMissileSprite[i].visible = YES;

float adjacent, opposite;

CGPoint destination;

if (angle <= -M_PI_4 && angle > -3.0f * M_PI_4) {

// Shoot down

angle = M_PI_2 - angle;

adjacent = _playerMissileSprite[i].position.y + Margin;

opposite = tanf(angle) * adjacent;

destination = ccp(_playerMissileSprite[i].position.x - opposite, -Margin);

}

else if (angle > M_PI_4 && angle <= 3.0f * M_PI_4) {

// Shoot up

angle = M_PI_2 - angle;

adjacent = _winSize.height - _playerMissileSprite[i].position.y + Margin;

opposite = tanf(angle) * adjacent;

destination = ccp(_playerMissileSprite[i].position.x + opposite, _winSize.height + Margin);

}

else if (angle <= M_PI_4 && angle > -M_PI_4){

// Shoot right

adjacent = _winSize.width - _playerMissileSprite[i].position.x + Margin;

opposite = tanf(angle) * adjacent;

destination = ccp(_winSize.width + Margin, _playerMissileSprite[i].position.y + opposite);

}

else // angle > 3.0f * M_PI_4 || angle <= -3.0f * M_PI_4

{

// Shoot left

adjacent = _playerMissileSprite[i].position.x + Margin;

opposite = tanf(angle) * adjacent;

destination = ccp(-Margin, _playerMissileSprite[i].position.y - opposite);

}

float hypotenuse = sqrtf(adjacent*adjacent + opposite*opposite);

ccTime duration = hypotenuse / PlayerMissileSpeed;

id action = [CCSequence actions: [CCMoveTo actionWithDuration:duration position:destination],

[CCCallBlock actionWithBlock:^

{

_playerMissileSprite[i].visible = NO;

}],

nil];

[_playerMissileSprite[i] runAction:action];

[[SimpleAudioEngine sharedEngine] playEffect:@"Shoot.wav"];

}

} // for

}

At first glance your code looks OK. What I would do it place some NSLog() statements here and there. For example, in the code that is supposed to first a new missile, print out the value of "i" for the missile that it fires.

Do the same thing in your update loop. This will tell you whether the sprites are really being used or not.

That helped. I had a flaw in the logic.

I was firing all 5 missiles simultaneously one on top of the other. That is why it seemed just 1. It went through the loop so fast it seemed only 1.

When I fired a missile I went through the loop checking missile visibility, so 1st time through all 5 were fired, and the same happened every time.

Need to figure a more clever way to check if missile has been fired or not. Thank you again for your great tutorial

I have a question though as ive come into the tutorial mid way through. Im using a joystick which has degress from 0 to 360. Im picking up the joystick value at the point of the fire button being pressed as the angle variable, but it messes up the direction the projectile fires.

This is as far as ive got but something is seriously messed up!!

`if (angle <= 135 && angle > 45)`

{

// Shoot up

angle = M_PI_2 - angle;

adjacent = self.contentSize.height - _Weapon.position.y + Margin;

opposite = tanf(angle) * adjacent;

destination = CGPointMake(_Weapon.position.x + opposite, self.contentSize.height + Margin);

//destination = CGPointMake(_Weapon.position.x + opposite, self.contentSize.height);

}

else if (angle > 135 && angle <= 225)

{

// Shoot left

adjacent = _Weapon.position.x + Margin;

opposite = tanf(angle) * adjacent;

destination = CGPointMake(-Margin, _Weapon.position.y - opposite);

//destination = CGPointMake(0,self.contentSize.height/2);

}

else if (angle > 225 && angle <= 315)

{

// Shoot down

angle = M_PI_2 - angle;

adjacent = _Weapon.position.x + Margin;

opposite = tanf(angle) * adjacent;

destination = CGPointMake(_Weapon.position.x - opposite, -Margin);

//destination = CGPointMake(self.contentSize.width/2,0);

}

else //right

{

adjacent = self.contentSize.width - _Weapon.position.x + Margin;

opposite = tanf(angle) * adjacent;

destination = CGPointMake(self.contentSize.width + Margin, _Weapon.position.y + opposite);

//destination = CGPointMake(self.contentSize.width,self.contentSize.height/2);

}