Update: This code is using an outdated version of the Cocos2D port. It’ll still work if you use the sample download linked at the end – but it’s using outdated API calls. Unfortunately I don’t have time to update the tutorial to the new release of Cocos2D. Sorry guys 🙁
This is the second tutorial in the Simple Android Cocos2D Game Tutorial series, originally written by Ray Wenderlich for the iPhone. This one builds on the first tutorial by replacing the ninja with a rotating turret, and the projectiles with cannon balls.
Getting Set Up
Ideally you’ll have followed the first tutorial, in which case you can continue from where you left of. Failing that you can download the code from the previous tutorial and use that as your base – however, I highly recommend starting from the beginning.
Now download a new player sprite and projectile sprite, put both in the ‘assets’ folder of your project. You’ll need to update your code to use these new sprites, so without further ado let’s get modifying. In the GameLayer constructor, update the player line with the following:
CCSprite player = CCSprite.sprite("Player2.png");
Next update the ccTouchesEnded method, find where you create the projectile sprite and replace with the following:
CCSprite projectile = CCSprite.sprite("Projectile2.png");
Compile and run your project, it should now look like the following screenshot:
You’ve probably noticed it doesn’t look quite right, the turret doesn’t rotate with the projectile direction. Let’s fix that now.
Rotating To Shoot
Before we can rotate the turret, we need to store a reference to the player sprite so we can rotate it elsewhere in the program. In the GameLayer class, add the following field:
protected CCSprite _player;
Next we need to use the new field, update the Constructor instantiation code to the following:
_player = CCSprite.sprite("Player2.png");
_player.setPosition(CGPoint.ccp(_player.getContentSize().width / 2.0f, winSize.height / 2.0f));
addChild(_player);
Rotating the turret with the projectile is easier said than done, we’re forced to use a little maths to work out the angle. For this we’ll use a little Trigonometry, which means remembering back to SOH CAH TOA. In this example we’ll be using the ‘TOA’, or Tangent of an angle is equal to the Opposite over the Adjacent. The following illustration should help:
As you can see from the graphic, the angle we want to rotate to is equal to the arctangent of the Y offset divided by the X offset. Seems easy enough right? Unfortunately there are two caveats you must be aware off before we can go implementing general geometry in our games:
- The maths functions in Java use and return radians, not degrees
- Cocos2D rotations are clockwise rather than anti-clockwise, which is the opposite to what is expected, as shown in the graphic below:
Fortunately the solutions are very simple. The Java Math class provides a method to convert to and from radians. The second solution is to invert the angle by multiplying by negative 1. So for example 20° x -1 = -20°. This will effectively convert the counter-clockwise rotation to the clockwise rotation Cocos2D expects:
Let’s put what we’ve learned into practice. Add the following code to the ccTouchesEnded code, just before we play the sound effect:
// Determine angle to face
double angleRadians = Math.atan((double)offRealY / (double)offRealX);
double angleDegrees = Math.toDegrees(angleRadians);
double cocosAngle = -1 * angleDegrees;
_player.setRotation((float)cocosAngle);
You may have noticed we’re using doubles rather than floats. According to Google doubles are just as fast as floats with the only trade-off being increased memory consumption. Since there is no float version of Math.atan or Math.toDegrees, it’s better to stick with doubles so we don’t waste CPU cycles converting between double and float repeatedly.
Now compile and run, and you should have a rotating turret!
Rotate Then Shoot
We’ve got a great game here, but it could be better. The turret rotates instantly, which is unlikely given we’re firing cannon balls. It would be much better for immersion and realism if we smoothly rotate the turret to its new direction. This will take a little refactoring.
Add the following field to the GameLayer class:
protected CCSprite _nextProjectile;
Replace the projectile instantiation code in ccTouchesEnded with the following:
_nextProjectile = CCSprite.sprite("Projectile2.png");
Next replace all occurrences of ‘projectile’ with ‘_nextProjectile’ within ccTouchesEnded. While you’re replacing these occurrences, make sure to delete the following lines:
addChild(projectile);
_projectiles.add(projectile);
Update the turret rotation code (also in ccTouchesEnded) with the following:
// Determine angle to face
double angleRadians = Math.atan((double)offRealY / (double)offRealX);
double angleDegrees = Math.toDegrees(angleRadians);
double cocosAngle = -1 * angleDegrees;
double rotationSpeed = 0.5 / Math.PI;
double rotationDuration = Math.abs(angleRadians * rotationSpeed);
_player.runAction(CCSequence.actions(
CCRotateTo.action((float)rotationDuration, (float)cocosAngle),
CCCallFunc.action(this, "finishShoot")));
This code will rotate the turret with a rotation speed of half a second (0.5) for half a circle’s worth of rotation (since full circle in radians is 2 PI, a half circle is 1 PI).
Once the rotation is complete, we then call a method called ‘finishShoot’. Since we haven’t created that method, we should create it now:
public void finishShoot()
{
addChild(_nextProjectile);
_projectiles.add(_nextProjectile);
}
This method adds the projectile to the game. Since it’s called once the turret has finished rotating we can guarantee that projectiles will only fire once the turret is in position.
That’s it, we’re all finished! Have a play, and enjoy your handiwork.
What’s Next?
You can download the full source code here.
In the future I should be porting Ray’s final part of the series, but in the meantime you could have a look at his tutorial and see if you can port it yourself!