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!
the next tutorial?
No ETA as of yet I’m afraid, I just don’t have the time to put together a full tutorial. I’ll be posting little tidbits now and then, but no Cocos2D tutorials for a while sadly.
Hi Dan,
Nice tutorials for starting android gaming.
Thank you for this.
I have some confusion related to memory allocatio.
Suppose I need an array of sprites.
I will declare them as
CCSprite mySprites = new CCSprite[MAX_SPRITES];
and while creating i will create them and add to current layer like:
for(int i=0;i<MAX_SPRITES;i++)
{
mySprites[i] = CCSprite.sprite("image_"+i+".png");
this.addChild(mySprites[i]);
}
Now sprites are added to layer but we need to access them while game flow so we will use mySprites;
So my question is when we do this.removeAllChildren(true); for this layer, will it remove all children and also mySprites sprites?
Or do we need to do something like this
for(int i=0;i<MAX_SPRITES;i++)
{
mySprites[i] = null;
}
In my game I think memory is leaking if there is simillar case.
We can also use tags to retrive sprites instead of using "CCSprite mySprites = new CCSprite[MAX_SPRITES];".
Please can you tell me what is the solution for this?
Hi BMR,
Good question! The call to removeAllChildren(true) should remove the sprite from OpenGL’s memory, this is enough to render it effectively useless. However as you have demonstrated, if you have a reference to the sprite elsewhere it will still be consuming memory. If you really want to store the sprites in a long-term array, make use of Java’s WeakReference class to store a weak reference to the sprite. As long as all of your references are WeakReference, then as soon as Cocos2D cleans up the sprite Java will know it can also garbage collect the sprite. In effect, only Cocos2D will have an actual reference to the sprite.
The only caveat to WeakReference is that the value of the reference could be NULL at any given time – so make sure to check for NULL before using the value/sprite.
Ninja edit: WeakReferences are also slower than hard references. If you’ll be using the reference a lot, it may be better to set to NULL manually as you mentioned. This will be a judgement call based on the performance of your game in real life (ie. on a device of the lowest spec you’re willing to support). I’ve never had any performance issues with WeakReferences, but I have heard of others having the odd slowdown with them.
Dan
link for the source code is down
awesome tutorial though π
also seems that now the collision detection is not workin well .. some targets gets removed when passing through a place of where an old projectile hit an old target
seems that projectiles removing is not working pretty well
Hmmm, can’t seem to recreate this bug. Can you download the attached source and see if the problem is in there? It could be the source I posted in the blog post isn’t quite right but the source in the download is.
I just downloaded the attached project and used it to create another android project
the images gets scaled down to fit the resolution of my phone [looks nicer]
but still .. same thing .. targets disappears when passing through an old collision place
want a video to show you what’s happening ? π
You found a tricky bug! In ccTouchesEnded, make sure to remove:
What was happening is we were adding the projectile to the array twice, which meant the first collision would make the projectile and target disappear.. but there was still a copy of the projectile in the array at that position, just waiting to collide invisibly with a target.
While you’re at it, change all the ArrayList instances to LinkedList, I’m not sure why I was using ArrayLists for dynamic data… LinkedList is much faster if you’re manipulating data often.
Thanks for the comment π I’ve checked the link and it appears to be working for me? Can you tell me which one isn’t working?
well , when I click on the link in here it takes me to this
http://dan.clarke.name/2011/05/wp-content/uploads/resources/cocos2d/Cocos2DSimpleGameTut2.zip
where it should be
http://dan.clarke.name/wp-content/uploads/resources/cocos2d/Cocos2DSimpleGameTut2.zip
Well spotted! Now fixed.
Great tutorial π Do you know how I could detect a sprite collision while the sprite is rotating?
The easiest solution is to create your own custom collision detection for the rectangles of the sprites, and reduce the size of the rectangle you use for collision detection. Basically reducing the size of the sprite as far as collisions are concerned, so that they have to be overlapping a little before a collision is detected. This works well for sprites that are largely square and in the middle of the sprite graphic.
However, if your sprite is rather rectangular rather than square you need to be a little more clever. Here’s three articles that should help you roll out your own rotated rectangle collision detection: http://www.ragestorm.net/tutorial?id=22 and http://stackoverflow.com/questions/5920638/java-collision-detection-for-rotated-rectangles and finally: http://archive.gamedev.net/reference/programming/features/2dRotatedRectCollision/page2.asp.
hi ,Dan , I want to ask , cocos2d android-1 .jar can compile to Blackberry eclipse SDK or Nokia Ovi Store-> j2ME SDK ???
if can’t , Dan , can you make a cocos2d blackberry version and nokia j2ME version????
androis / blackberry / nokia same is java to development , so use the cocos2d framework make blackberry java version and nokia j2ME version is not difficult, right ^_^
can you make a cocos2d blackberry version and cocos2d nokia j2ME version ??
Hi Steven. I’m sorry – but I don’t actually create or maintain the Cocos2D port to Android. For Blackberry, I think you’ll need to do the work yourself. You can download the source code from GitHub, and take it from there.
Hello,
I am getting an error in the ‘public void update(float dt)’ section of your code. The error comes from these lines:
for (CCSprite target : _targets)
{
CGRect targetRect = CGRect.make(target.getPosition().x – (target.getContentSize().width),
target.getPosition().y – (target.getContentSize().height),
target.getContentSize().width,
target.getContentSize().height);
—> if (CGRect.intersects(projectileRect, targetRect))
—> targetsToDelete.add(target);
}
The error says that “The method intersects(CGRect, CGRect) is undefined for the type CGRect”
Looks like someone might have changed the Cocos2D code in the latest update. How can I fix this?
I am having this same problem! Any help would be much appreciated!!1
Hey I am having same error! The error lies in the .jar file. How you resolved this error. Please help!
Hi Dan, excelent tutorial. You know how can you define your app only support portrait orientation? If I set CCDirector.sharedDirector().setDeviceOrientation(CCDirector.kCCDeviceOrientationPortrait); my app restarts everytime I change the orientatios of the device (nexus one). Hope you can help me. Greetings!!!!
Hi Dan,
I’ve been following your program, but I can’t get it to run. It keeps giving various errors on the following lines:
if (CGRect.intersects(projectileRect, targetRect))
and
target.setPosition(winSize.width + (target.getContentSize().width / 2.0f), actualY);
Error message: The method setPosition(CGPoint) in the type CCSprite is not applicable for the arguments (float, int)
Any help would be appreciated!
Hi! I had the same problem. I downloaded source from github and linked it to a project instead of jar (I don’t use it anymore). If you need more info – just say.
Hi,
I tried the tutorial works fine. Good Job!!!
if i rename a file in the assets folder and also in the code.. it simply fails saying that it could not find the file.. really frustrated..
Please help..
Ted
Dan,
I would like to thank you for the tutorials. They were quite helpful. I finished them and decided to attempt to convert the next Ray Wenderlich tutorial to Android by myself. I am doing well with it so far but I am having an issue. I don’t know if it’s cocos2d related or if it’s just me doing something wrong.
I have my tiled map set so that 0,0 (bottom left corner) is at 0,0 on my screen. Basically the bottom left corner of the map is anchored to the bottom left corner of my screen. I placed my player character at 100,100 so just off the bottom left corner. I can move left, right up and down without a problem.
My issue comes when my player character crosses past the center of the screen in either height or width. I would assume the code as it is for iPhone would have the character remain centered in the screen as he continues to move and have the map move in the background. In my code the player character continues past the middle of the screen on towards the opposite edge at which point I can’t move him anymore because I can’t touch past the edge of the screen. I have posted about it on stackoverflow ( http://stackoverflow.com/questions/9493746/cocos2d-android-github-version-how-do-i-keep-a-player-sprite-centered-on-the ) in more detail.
I’m out of ideas. Do you have any suggestions?
Very thanks for your tutorial. It’s very useful.
Sorry for my english.
“Si nu bucchΓ¬”
Forza Napoli
Dan, may I made ββthis tutorial for my thesis?
I hope you let him. Please
Thnkyou Mr. Dan Clarke.
Dan ,your this tut is so nice and i am very thank full to this . i am also need your help in adding some titled background in cocos 2d for android…
Hello Dan,
Why is it that cocos2d library in android doesn’t have a good documentation and a good javadoc?
It’ll be much more easier to understand if there is better javadoc or more documentation in android cocos2d. All I’ve searched uses C++/ Objective C in iOS in which they import/port it to eclipse to make it android app.
I’m looking forward for more of your tutorial like making spritebatch, camera view (orthographic camera), animation..
One more thing is that I am hoping for a cocosbuilder for windows.
Thanks! π
Hi Dan,
Could I create animation from 3 images?
Hello Dan,
Your Tutorial is really Nice. Now im working with a game in Which i have to move an animated sprite(from sprite sheet). Is there any way to animate sprite sheet by cocos 2d?
i’m so confused to work with this please help me. my game has a normal sprite no cocos2d or and engine used here. NOw i want to move my character on touch event by getting touch location? is it possible to use cocos2d here?
Please help me with bouncing image tutorial.