Subscribe to GameFromScratch on YouTube Support GameFromScratch on Patreon Join the GFS Discord Server!
13. July 2012

I have a bit of a project I need to undertake in real life, that overlaps nicely with this site, so I am documenting the process here.

 

 

The nutshell version, I have a toddler and she is having trouble with transitions.  People have suggestion a This Then board, in terms of “Do This” “Then that” will happen.  Its remarkably simple over all, it is pretty much two pieces of velcro on a piece of cardboard and a bunch of pictures that we can stick to it.  I was about to have to print off a ton of pictures, scale them down, stick velcro to them and it dawned on me… this calls for an App!

 

 

See, I have a ton of devices around this place…  PCs, a Mac, a Galaxy Note, S3, Transformer, Playbook, iPhone, iPod and even an iPad that my daughter has pretty much claimed as her own.  So it only makes sense to implement this an application.  That said, supporting all of these platforms would be a pretty epic pain in the butt.  Also, I have very little desire to pay Apple 99$ a year just to put this app on her iPad, and I don’t want to be bothered with jailbreaking.

 

This is where my recent playing with HTML5 comes in extremely handy.  I am going to create it using a variety of technologies, include cocos2D, node/express and heroku.  The application itself is very simple, but the process is stuff that almost any moderately advanced web application is going to face.

 

First off, HTML5 is wonderful, but it has some serious limitations.  One such limitation is only having minimal local storage.  This is where your server comes in, and Node delivers in spades.  Another problem almost all web apps face is… where do I put this thing?  We will cover that as well.  Possibly at the end I will also covering delivering to device ( PhoneGap? Appcelerator? ), that’s a wait and see thing.

 

This isn’t exactly a tutorial series; this is something I have to accomplish, so I’ve decided to document the process as I go.  I wont always go into as much detail as I normally do with tutorials and I promise you, unless you have a toddler yourself, the end result wont be that exciting of a game.  However, you will see how to quickly make an HTML5 game in cocos2D, hosted in Node.js, developed quickly.  Hope you enjoy.

 

In fact, the first post is up.  This one covers hosting a cocos2D app ( our last tutorial on menus and music ) in a custom Node.js based server.

General


11. July 2012

 

In this tutorial we are going to cover playing sound effects and music files.  We are also going to implement a simple menu system to control everything.  The example sound effect and song are taken from the cocos2D HTML tests. 

 

First we need to preload our sound effect files.  Remember the preload in main.js?  Now we are going to actually use it.  Open up main.js and make the following change:

 

cc.AudioEngine.getInstance().init("mp3,ogg,wav");
   cc.Loader.shareLoader().preload([
    {type:"effect",src:"Resources/effect2"},
    {type:"bgm",src:"Resources/background"}
   ]);

 

Here we preload our song and sound effect for later playback.  It doesn’t have to be in this file, but it guarantees that it has happened when we need it.  You will need to add a pair of mp3 files in a subdirectory named Resources, background.mp3 and effect2.mp3.  Obviously you can use whatever files you want, but be sure not to add the file extension here. 

The out key call is the call to init(). This call initializes the AudioManager singleton.  The parameter are the supported file formats.  mp3, ogg and wav are the supported options.  This value determines what extension the loader will use when looking for your resources.  Each browser supports different file formats, so if you want to playback in as many browsers as possible, it is ideal to provide all three options.  For example, if you do not provide ogg, Chromium on Ubuntu will not play any sound.

 

Now lets take a look at MyFourthApp.js:

 

var MyFourthApp = cc.LayerColor.extend({
    init:function()
    {
        this.initWithColor(new cc.Color4B(0,0,0,255));
        var size = cc.Director.getInstance().getWinSize();


        cc.AudioEngine.getInstance().setEffectsVolume(0.5);
        cc.AudioEngine.getInstance().setBackgroundMusicVolume(0.5);

        var menuItem1 = new cc.MenuItemFont.create("Play Sound",this,this.playSound);
        var menuItem2 = new cc.MenuItemFont.create("Play Song",this,this.playSong);
        var menuItem3 = new cc.MenuItemFont.create("Stop Playing Song",this,this.stopPlayingSound);
        var menuItem4 = new cc.MenuItemFont.create("Exit",this,this.exit);

        menuItem1.setPosition(new cc.Point(size.width/2,size.height/2+50));
        menuItem2.setPosition(new cc.Point(size.width/2,size.height/2));
        menuItem3.setPosition(new cc.Point(size.width/2,size.height/2-50));
        menuItem4.setPosition(new cc.Point(size.width/2,size.height/2-100));

        var menu = cc.Menu.create(menuItem1,menuItem2,menuItem3,menuItem4);
        menu.setPosition(new cc.Point(0,0));

        this.addChild(menu);

        return this;
    },
    playSound:function(){
        cc.log("Playing sound");
        cc.AudioEngine.getInstance().playEffect("Resources/effect2");
    },
    playSong:function(){
        cc.log("Playing song");
        cc.AudioEngine.getInstance().playBackgroundMusic("Resources/background",false);
    },
    stopPlayingSound:function(){
        cc.log("Done playing song");
        if(cc.AudioEngine.getInstance().isBackgroundMusicPlaying())
        {
            cc.AudioEngine.getInstance().stopBackgroundMusic();
        }
    },
    exit:function(){
        document.location.href = "https://www.gamefromscratch.com";
    }
});


MyFourthAppScene = cc.Scene.extend({
    onEnter:function(){
        this._super();
        var layer = new MyFourthApp();
        layer.init();
        this.addChild(layer);
    }
});

 

The guts of this are identical to the last tutorial, with the obvious exception of changed names.

The following two lines set the background volume for both music and effects to 50%.  As you can see, AudioManager is available as a singleton object, just like Director.

 

cc.AudioEngine.getInstance().setEffectsVolume(0.5);
cc.AudioEngine.getInstance().setBackgroundMusicVolume(0.5);

 

Next up we create 4 different MenuItemFont objects:

var menuItem1 = new cc.MenuItemFont.create("Play Sound",this,this.playSound); var menuItem2 = new cc.MenuItemFont.create("Play Song",this,this.playSong); var menuItem3 = new cc.MenuItemFont.create("Stop Playing Song",this,this.stopPlayingSound); var menuItem4 = new cc.MenuItemFont.create("Exit",this,this.exit);

 

MenuItemFont is one of the available types of menuitems ( there is also a sprite based menu item ) that compose a menu.  For each one, you specify the text to display, the owning container as well as a callback function that is called when the menu is selected.  We then position each menuitem relative to the center of the screen, each one placed 50 pixels apart.   We then create our Menu item by passing in a variable number of MenuItem, like this:

var menu = cc.Menu.create(menuItem1,menuItem2,menuItem3,menuItem4);

 

Finally we position our menu and add it to our layer.

 

Next we have our 4 different callback function.  cc.log simple logs whatever text out to the console.  So if you have the developer console open in Chrome, or Firebug on Firefox, you will see the following when the code is run:

image

 

cc.Log is an effective and easy way to keep track of your code’s execution without requiring the debugger.

 

In the event of selecting “Play Sound” the playSound() method is fired, which calls:

 

cc.AudioEngine.getInstance().playEffect("Resources/effect2");

 

This uses the AudioManager singleton to play our preloaded effect “effect2” in the folder “Resources” using the playEffect() method.

 

In the event of selecting “Play Song”, the playSong method is fired, which plays a song using the function:

 

cc.AudioEngine.getInstance().playBackgroundMusic("Resources/background",false);

 

If “Stop Playing Song” is selected, the stopPlayingSound method is fired, which checks if a song is currently playing and stops it if one is.  Like this:

 

if(cc.AudioEngine.getInstance().isBackgroundMusicPlaying())
        {
            cc.AudioEngine.getInstance().stopBackgroundMusic();
        }

 

Finally, in the event Exit is called, the app simply redirects the user to a great website. Smile

 

 

As always, you can download the full project ( including all audio files ) right here.

 

Here is our application in action:

 

 

Read Tutorial 6

Programming


9. July 2012

 

I am a bit late to announce this ( like, almost 3 weeks late ), but cocos2D HTML has a new Cocos2dHTML5release.  You can read the complete release notes on their website, but there are a few changes that are of particular concern to the tutorials on this site.  Instead of going back an updating the tutorials ( for now at least ), I will show this quick updating guide in case you run into trouble getting them to run.  The changes on the whole are pretty minor.

 

 

So, if you want to update your code to run with alpha2, make the following changes.

 

 

 

MyFirstApp:

In MyFirstApp.js change:

var layer1 = cc.LayerColor.layerWithColorWidthHeight(cc.ccc4(255, 255, 0, 255), 600, 600);

To:

var layer1 = cc.LayerColor.create(cc.ccc4(255, 255, 0, 255), 600, 600);

 

var helloLabel = cc.LabelTTF.labelWithString("Hello world", "Arial", 30);

To:

var helloLabel = cc.LabelTTF.create("Hello world", "Arial", 30);

 

var scene = cc.Scene.node();

To:

var scene = cc.Scene.create();

 

 

MySecondApp:

 

In MySecondApp.js change:

var scene = cc.Scene.node();

To:

var scene = cc.Scene.create();

 

var layer1 = cc.LayerColor.layerWithColorWidthHeight( cc.ccc4(128, 128, 128, 255), 600, 600), jetSprite = cc.Sprite.spriteWithFile("./images/Jet.png");

To:

var layer1 = cc.LayerColor.create( cc.ccc4(128, 128, 128, 255), 600, 600), jetSprite = cc.Sprite.create("./images/Jet.png");

 

 

MyThirdApp:

 

In MyThirdApp.js change:

 

var scene = cc.Scene.node();

To:

var scene = cc.Scene.create();

 

 

 

 

And that’s it.  It sucks that breaking changes happen, but I think you will admit, this one is certainly for the better.   Any code from this point forward will use the newer SDK.

Programming


29. June 2012

There is now documentation!

 

This is a big boon, and one of the biggest downsides to many open source projects.  Put simply, making documentation isn’t really all that fun, so most people don’t bother.  It’s great when a project takes the effort.  Up until now I’ve used the normal Cocos2Dx documentation combined with browsing through the source code, but now with JavaScript specific documentation, the world just got a heck of a lot easier.

 

 

So, be sure to check it out.  If you haven’t been exposed to Cocos2D HTML, it is an HTML port of the popular cocos2D library.  You can check out a series of tutorials on this site, with more coming soon.  You can also see a game in action right here.

 

If you are running a similar project, don’t under-estimate the value of good documentation, it is absolutely HUGE.  So if you actually want people using what you are investing all your time into creating, consider spending some time on documentation.  Kudos to the cocos2DHTML5 team.

News


17. June 2012

 

In this tutorial we are going to look at adding keyboard and mouse support to your cocos2D application.  cocos2D was traditionally used on the iPhone, so keyboard and mouse support is very touch centric, so if you are used to desktop development and things seem a bit strange, this is why.

Alright, let’s jump right in.  We are going to create a simple game with our jet sprite from tutorial 3.  The big difference is it will respond to your mouse and keyboard activity.  Clone your previous project, this time I am calling it MyThirdApp.  Yeah, tons of thought went into these names! 

 

Now create a new file in the Classes directory named JetSprite.js. Inside, write the following code:

 

var JetSprite = cc.Sprite.extend({
    _currentRotation:0,
    ctor:function(){
        this.initWithFile("images/jet.png");
    },
    update:function(dt){
        this.setRotation(this._currentRotation);
    },
    handleKey:function(e)
    {
        if(e === cc.KEY.left)
        {
            this._currentRotation--;

        }
        else if(e === cc.KEY.right)
            this._currentRotation++;

        if(this._currentRotation < 0) this._currentRotation = 360;
        if(this._currentRotation > 360) this._currentRotation = 0;
    },
    handleTouch:function(touchLocation)
    {
        if(touchLocation.x < 300)
            this._currentRotation = 0;
        else
            this._currentRotation = 180;
    },
    handleTouchMove:function(touchLocation){
        // Gross use of hardcoded width,height params.
        var angle = Math.atan2(touchLocation.x-300,touchLocation.y-300);

        angle = angle * (180/Math.PI);
        this._currentRotation = angle;

    }
});

 

Here we are deriving our new class JetSprite from cc.Sprite using the extend() function.  During the constructor, we first call back to cc.Sprite’s constructor, then load our sprite from the file jet.png located in the images folder.  ( If you like me started your project by cloning MySecondApp, it should already be there, otherwise make sure you have an image there ).

 

The update() function is going to be called once every frame, this will make sense later.  Essentially this is where you put your object’s logic.  In this case we are simply rotating our sprite to the value in _currentRotationhandleKey(), handleTouch() and handleTouchMove() are three user defined functions that are going to be used to pass I/O events into our JetSprite object.

 

handleKey() is passed the key value ( it’s a number ) of the currently pressed key.  cocos2D defines a number of key values to represent each key in the cc.KEY value.  If you want more details check out CCKeyboardDispatcher.js in the cocos2D directory for more details.  In the event the left arrow key is held down, we decrement our rotation value, if the right arrow key is down we increment the rotation value.  Finally, if we roll over 360 or under 0 degrees, we update accordingly.  Effectively this rotates our sprite left and right on arrow key press.

 

handleTouch() is very simple.  It is passed in the touchLocation, which is an x,y value representing where on screen was touched ( or in our case, clicked ).  If its on the left half the screen, we point up ( 0 degrees ), if the touch occurred on the right half of the screen, we rotate to straight down ( 180 degrees ).  One limitation of cocos2D seems to be the lack of ability to handle right clicks, but this may just be an oversight on my behalf.  Right now, a “touch” is any mouse button click.

 

handleTouchMove() is passed in the x,y coordinates of the mouse’s currently location.  As you can tell by the comment, I rather crudely used a hardcoded value for representing half the screen width ( 300 ), obviously in non-demo code you wouldn’t do this!  What we want to do here is figure out the angle of the mouse relative to the center of our sprite ( which is located at 300,300 ).  This is done by taking the atan2 of the distance of the mouse’s coordinate from sprite’s center.  We then convert that value ( atan2 returns a value in radians ) to degrees.  Finally, we take our calculated angle and apply it as our rotation.  Essentially this will cause our sprite to rotate relative to the mouse location.

 

Now lets take a look at the heart of our application.  Create a new script file ( or rename MySecondApp.js ) called MyThirdApp.js and enter the following code:

 

var MyThirdApp = cc.LayerColor.extend(
{   _jetSprite:null,
    init:function(){
        this._super();
        this.initWithColor(new cc.Color4B(0,0,0,255));
        var size = cc.Director.getInstance().getWinSize();

        this._jetSprite = new JetSprite();
        this.setTouchEnabled(true);
        this.setKeyboardEnabled(true);

        this.setPosition(new cc.Point(0,0));

        this.addChild(this._jetSprite);
        this._jetSprite.setPosition(new cc.Point(size.width/2,size.height/2));
        this._jetSprite.scheduleUpdate();
        this.schedule(this.update);

        return true;
    },
    onEnter:function(){
        this._super();
    },
    update:function(dt){
    },
    onTouchesEnded:function (pTouch,pEvent){
        this._jetSprite.handleTouch(pTouch[0].getLocation());
    },
    onTouchesMoved:function(pTouch,pEvent){
        this._jetSprite.handleTouchMove(pTouch[0].getLocation());
    },
    onKeyUp:function(e){

    },
    onKeyDown:function(e){
        this._jetSprite.handleKey(e);
    }
});



MyThirdAppScene = cc.Scene.extend({
    onEnter:function(){
        this._super();
        var layer = new MyThirdApp();
        layer.init();
        this.addChild(layer);
    }
})

 

The bulk of this code is very similar to previous code with one major difference.  In previous examples, we created a layer, then added a color layer to it.  In this case, we simply extend from LayerColor and add our children directly to ourselves.  There is very little functional differences between the two approaches, except this one is a bit more concise.  Now let’s take a closer look at our code.

 

In our init() method, we init with a black background colour.  We then create our JetSprite object and tell cocos2D that our layer handles touch and keypad events.  In cocos2D, input is handled at the layer level, and setting these two values will result in touch and keyboard delegates being added to our object so we can handle IO events.  We then position our layer, add our _jetSprite as a child positioned in the center of our layer.  The scheduleUpdate() call is very important.  This will result in our JetSprite’s update() function getting called every frame.  If you don’t call this method, or sprite wont do anything!

 

Next up is onEnter() which is what is called when a layer becomes the active layer.  In our case, this call is very important, as it ( in cc.Layer.onEnter ) is where the touch and keyboard delegates are actually attached to our layer!  If you remove the call to this._super(), you will not receive any keyboard and mouse events!

 

Now that we are wired up to receive touch and keyboard events, we need a couple methods that will be called when they occur.  In our case we are going to use ccTouchesEnded, ccTouchesMoved, keyUp and keyDown.  When an event occurs, these methods will be called, which in turn simply call the appropriate method on our JetSprite.

 

There are two things to be aware of here.  There are ccTouch____ and ccTouches____ versions of events; to my experience, the ccTouch____ version will never be called!  If you want to handle touch/mouse events, use the ccTouches version.  The next is the way we are handling key presses,  right now since we used keyDown, holding down the key will result in it being called over and over.  If however you want to respond only once per key hit ( regardless to if it is held down or not ), use keyUp instead.

 

We also need to make sure our new classes are included in our project.  Just like the last tutorial, make the following changes to the bottom of cocos2d.js

appFiles:['MyThirdApp.js','JetSprite.js']

 

We also need to update the Scene name in main.js

var myApp = new cocos2dApp(MyThirdAppScene);

Here is the end result of all of our code.  Mouse over the canvas and the mouse will follow you.  Left and right arrow will rotate the sprite, while clicking (either button) on the left half of the screen will point the jet up, while clicking the right half will point the sprite down.

 

 


 

If you are running Chrome be aware the clicking doesn’t appear to work.  It is actually working, the sprite is being rotated.  However it is also triggering a mouse move event, immediately snapping the sprite back to where you mouse pointer is! Smile

 

As always, you can download the whole project right here.  It includes a zip of the cocos2D version I used, in case you have trouble working with the current release.

 

 

Read Tutorial 5

Programming


AppGameKit Studio

See More Tutorials on DevGa.me!

Month List