How To Make a Cross-Platform Game with Cocos2D Javascript Tutorial: Getting Started
In this tutorial, you will port the raywenderlich.com classic Ninjas Going Pew-Pew game to Cocos2D-Javascript. I like implementing this game when learning new frameworks because it’s simple, but covers the most important aspects of making a game. By Ray Wenderlich.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
How To Make a Cross-Platform Game with Cocos2D Javascript Tutorial: Getting Started
35 mins
Shooting Projectiles
At this point, the ninja is just begging for some action – so let’s add shooting! There are many ways you could implement shooting, but for this game you are going to make it so when the user taps the screen, it shoots a projectile from the player in the direction of the tap.
I want to use a cc.MoveTo action to implement this to keep things at a beginner level, but in order to use this you have to do a little math. This is because the cc.MoveTo requires you to give a destination for the projectile, but you can’t just use the touch point because the touch point represents just the direction to shoot relative to the player. You actually want to keep the bullet moving through the touch point until the bullet goes off-screen.
Here’s a picture that illustrates the matter:
So as you can see, you have a small triangle created by the x and y offset from the origin point to the touch point. You just need to make a big triangle with the same ratio – and you know you want one of the endpoints to be off the screen.
Ok, so onto the code. First, add a new array for the projectiles at the top of the file:
_projectiles:[],
Then you have to enable touches on your layer – but how to handle that depends if your game is being run on a mobile, desktop, or browser device. So add the following code to the beginning of onEnter, right after the call to this._super():
if( 'touches' in sys.capabilities ) {
this.setTouchEnabled(true);
}
if( 'mouse' in sys.capabilities ) {
this.setMouseEnabled(true);
}
This registers the onMouse callbacks to be called if there’s mouse input, or the onTouch methods to be called if there’s touch input.
Then add a comma after gameLogic method, and add these new methods:
locationTapped:function(location) {
// Set up initial location of the projectile
var projectile = cc.Sprite.create(s_projectile);
projectile.setPosition(20, winSize.height/2);
// Determine offset of location to projectile
var offset = cc.pSub(location, projectile.getPosition()); // 1
// Bail out if you are shooting down or backwards
if (offset.x <= 0) return;
// Ok to add now - we've double checked position
this.addChild(projectile);
// Figure out final destination of projectile
var realX = winSize.width + (projectile.getContentSize().width / 2);
var ratio = offset.y / offset.x;
var realY = (realX * ratio) + projectile.getPosition().y;
var realDest = cc.p(realX, realY);
// Determine the length of how far you're shooting
var offset = cc.pSub(realDest, projectile.getPosition());
var length = cc.pLength(offset);
var velocity = 480.0;
var realMoveDuration = length / velocity;
// Move projectile to actual endpoint
projectile.runAction(cc.Sequence.create( // 2
cc.MoveTo.create(realMoveDuration, realDest),
cc.CallFunc.create(function(node) {
cc.ArrayRemoveObject(this._projectiles, node);
node.removeFromParent();
}, this)
));
// Add to array
projectile.setTag(2);
this._projectiles.push(projectile);
},
onMouseUp:function (event) {
var location = event.getLocation();
this.locationTapped(location);
},
onTouchesEnded:function (touches, event) {
if (touches.length <= 0)
return;
var touch = touches[0];
var location = touch.getLocation();
this.locationTapped(location);
}
Again, I'd like to split the explanation here based on whether you're familiar with Cocos2D-iOS already or not.
[spoiler title="I'm Familiar with Cocos2D-iOS"]
Here are some things to point out for those transitioning to Cocos2D-Javascript from Cocos2D-iOS:
- The helper methods to perform math on points that you know and love like ccpAdd, ccpSub are still here - they're just named slightly differently. cc.pSub is the equivalent of ccpSub for example.
- You can create actions in a handy chain just like you can in Cocos2D-iOS.
[/spoiler]
[spoiler title="I'm Completely New to Cocos2D"]
The onMouseUp and onTouchesEnded methods get the location of the mouse click/touch and forward it on to locationTapped.
The beginning of this method loads up the projectile sprite and sets the initial position as usual. You then determine where you wish to move the projectile to, using the vector between the player and the touch as a guide, according to the algorithm described previously.
Note that the algorithm isn’t ideal. You’re forcing the bullet to keep moving until it reaches the offscreen X position – even if you would have gone offscreen in the Y position first! There are various ways to address this including checking for the shortest length to go offscreen, having your game logic callback check for offscreen projectiles and removing rather than using the callback method, etc. but for this beginner tutorial you’ll keep it as-is.
The last thing you have to do is determine the duration for the movement. You want the bullet to be shot at a constant rate despite the direction of the shot, so again you have to do a little math. You can figure out how far you’re moving by using the handy cc.pLength function.
Once you have the distance, you just divide that by the velocity in order to get the duration. This is because velocity = distance over time, or in other words time = distance over velocity.
The rest is setting the actions just like you did for the targets.
[/spoiler]
And that's it - save the file and refresh your browser, and now your ninja should be able to fire away at the oncoming hordes!
Collision Detection
So now you have shurikens flying everywhere – but what your ninja really wants to do is to lay some smack down. So let’s add in some code to detect when your projectiles intersect your targets.
There are various ways to solve this with Cocos2D, including using one of the included physics libraries: Box2D or Chipmunk. However to keep things simple, you are going to implement simple collision detection yourself.
You already have been keeping track of the monsters and projectiles in arrays. All you need to do is periodically check to see if any of them are colliding.
To do this, add a comma after onTouchesEnded and add this new method:
update:function (dt) {
for (var i = 0; i < this._projectiles.length; i++) {
var projectile = this._projectiles[i];
for (var j = 0; j < this._monsters.length; j++) {
var monster = this._monsters[j];
var projectileRect = projectile.getBoundingBox();
var monsterRect = monster.getBoundingBox();
if (cc.rectIntersectsRect(projectileRect, monsterRect)) {
cc.log("collision!");
cc.ArrayRemoveObject(this._projectiles, projectile);
projectile.removeFromParent();
cc.ArrayRemoveObject(this._monsters, monster);
monster.removeFromParent();
}
}
}
}
The above should be pretty clear. You just iterate through your projectiles and monsters, creating rectangles corresponding to their bounding boxes, and use CGRectIntersectsRect to check for intersections. If any are found, you remove them from the scene and from the arrays.
Note that you don't have to create separate "toDelete" arrays like you do in Objective-C, because it's safe to remove elements from an array while iterating through in Javascript.
You just need one more thing before you’re ready to roll – schedule this method to run as often as possible by adding the following line to your onEnter method:
this.scheduleUpdate();
Save the file and refresh your browser, and now when your projectiles intersect targets they should disappear!