Subscribe to GameFromScratch on YouTube Support GameFromScratch on Patreon

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 , ,

9. June 2012

 

As I have been working with Cocos2D-html ( and PlayStation GameEngine2D which is based on cocos2D ) lately, I decided to take a look at what the cocos2D book situation was like.  While about half as large as my Unity 3D book round-up there is still a nice, growing collection of cocos2D related books.

 

The cocos2D book round-up is a collection of every currently published book on the subject of cocos2D.  It contains all of the details you may want to know when selecting a book ( author, page count, rating, links to Amazon, Safari Books Online and the publisher, publish date, table of contents, publisher description, etc ) all on a single page.  So if you are in the market for a book on cocos2D, or just want to see what all is available, start here.

 

I am to be as comprehensive as possible, so if I’ve missed a book on the subject, or made a mistake, please let me know!

General ,

8. June 2012

 

Just a quick note to say I’ve organized my recent Cocos2D posts into a traditional tutorial series, like this site’s C++ and PlayStation Mobile tutorials.  You can now access them from the right hand navigation list, or by clicking here

 

Right now we are at 3 tutorials, plus one related post.  More tutorials are on the way.

News , ,

8. June 2012

 

This tutorial is going to look at the basics of sprites and an area that initially confuses many people ( myself included! ), the positioning system used by Cocos2D.  I am going to build off the code we wrote in Tutorial 2, with a few minor changes.  First thing you need to do is make a copy of the MyFirstApp folder and paste it as the aptly named MySecondApp, so you end up with a folder looking like this:

 

image

 

Now we need to make a few changes. First, you can delete the file MyFirstApp.js within the MySecondApp directory as we are going to create a new one now.  Create another js file, MySecondApp.js.  We will worry about the contents in a few minutes.

 

First we need to make a couple small alterations to our existing code.  Open up Cocos2d.js and scroll to the very bottom.  We want to change:

 

appFiles:['MyFirstApp.js']

to

appFiles:['MySecondApp.js']

Now open up main.js and change:

 

var myApp = new cocos2dApp(MyFirstAppScene);

 

to

 

var myApp = new cocos2dApp(MySecondAppScene);

 

That is all the changes we need to make to our boilerplate code.  In future tutorials, I will just assume you have already performed this step.

 

Finally before we get to coding, we need a sprite to work.  In your newly created folder, create a directory named images and save an image file there.  I am using this sprite, which has the file name Jet.png.  You of course can use whatever sprite you want.

jet

 

Now lets fill in the code for MySecondApp.js

 

var MySecondApp = cc.Layer.extend(
{
    init:function()
    {
        var layer1 = cc.LayerColor.create(
            new cc.Color4B(128, 128, 128, 255), 600, 600),
            jetSprite = cc.Sprite.create("./images/Jet.png");

        layer1.setPosition(new cc.Point(0.0,0.0));
        layer1.addChild(jetSprite);
        jetSprite.setPosition(new cc.Point(0.0,0.0));

        this.addChild(layer1);
        return true;
    }
});


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

 

Much of this code should look virtually identical to MyFirstApp.js, as not much has changed.  Obviously our variable name has changed, but onEnter() is otherwise identical.

 

In our init() function, we are simply creating a background layer that is a solid gray color with the dimensions of 600x600 ( the same as our Canvas tag ).  We then create a cc.Sprite object using our Jet.png.  This is all that is involved in loading and creating a sprite object.  Now it's just a matter of adding the sprite to our layer, and adding our layer to MySecondApp’s layer. 

 

Lets load up our new app in the web browser and see what’s what!

image

 

As you can see, we have a 600x600 solid layer of gray and if you look closely in the bottom corner of our screen, you can see 1/4 of our jet sprite.  Depending on your background, this result might seem remarkably odd, as there are two factors at play here.

 

First, as I said in a previous tutorial, Cocos2D puts the origin at the bottom left hand corner, not the more traditional top left.  So if you have ever worked with textures or bitmap graphics before ( outside of OpenGL ), this is going to seem a little backwards to you.

 

Why would they do it this way?  I am guessing that  they followed the OpenGL way of doing things.  Now, why would OpenGL buck the trend and set the origin to the bottom left?  Well that involves a trip back to high school math and Cartesian coordinates.  If you think in those terms, suddenly starting from the bottom left makes a bit more sense.  Remember this graph:

 

2D_Cartesian_Coordinates

 

In this particular case, if you are dealing with positive coordinates, you are in the upper right hand quadrant.  So if something is at say, position (2,2), it is above and to the right of the origin.

 

In the end it doesn’t really matter why, just be aware that the positions in Cocos2D start at the bottom left and you will be fine.

 

The next factor here is, where is the sprite’s anchor point.  Its all nice and good to say our jet is at (0,0)… but what part of our Jet is at (0,0)?  This is where the anchor point comes into effect.  The anchor point is where sprite translations ( rotating, translating, etc ) are performed relative to.  In Cocos2D, by default the pivot point is in the middle of the sprite, like illustrated by the red dot:

jetpivot

 

It is traditional to place the anchor point at the top left corner, but there are some real advantages to placing it at the center.  The least of which is rotation.  Consider the effects of rotating a sprite 1 degree per frame.

 

Here is the result with the pivot about the center:

(The code for the above animation is available here )

 

 

And here is the same image with the sprite anchor point moved to the top left:

 

(The code for the above animation is available here )

 

 

If you want to alter the anchor point, you can do it with this code:

jetSprite.setAnchorPoint(cc.PointMake(0,1));

 

The parameter to setAnchorPoint() is the new origin, in OpenGL coordinates.  0,0 == Bottom left, 0,1 == Top left, 1,0 == Top right, 1,1 == Bottom right.  Truth of the matter is, you should just adjust to the Cocos way of doing things unless you have a very good reason.

 

Alright… back to example, doing things the Cocos2D way.

 

Let say for example we wanted our sprite to be at the top right hand corner, we would accomplish this with the following code:

var size = cc.Director.sharedDirector().getWinSize(); jetSprite.setPosition(new cc.Point( size.width - jetSprite.getContentSizeInPixels().width/2, size.height - jetSprite.getContentSizeInPixels().height/2) );

 

Now here is the updated init() method with the Jet sprite centered to the screen.

 init:function()
    {
        var layer1 = cc.LayerColor.create(
            new cc.Color4B(128, 128, 128, 255), 600, 600),
            jetSprite = cc.Sprite.create("./images/Jet.png");

        layer1.setPosition(new cc.Point(0.0,0.0));
        layer1.addChild(jetSprite);

        var size = cc.Director.getInstance().getWinSize();
        jetSprite.setPosition(new cc.Point(size.width/2,size.height/2));

        this.addChild(layer1);
        return true;
    }

 

And finally, our end results:

image

 

Again, you can download the entire project here.

 

 

Read Tutorial 4

Programming , ,

7. June 2012

 

After spending so many years as a Windows/Visual Studio developer, Intellisense is a simple must have in my arsenal.  In fact, I believe my repeated use of Intellisense actually severedWebStormAndCocos2d the part of my brain that remembers functions and variables!

 

As I’ve been doing a lot of JavaScript development recently ( between Node.js and now Cocos2D ), I needed to recreate the Visual Studio experience.  I am an avid Notepad++ fan, but I never did get autocomplete working to my liking.  Then I discovered JetBrain’s WebStorm IDE and everything was right with the world.  If you want a full blown IDE for HTML and JavaScript development, you really can’t beat WebStorm ( it is however, not free software ).  Working with Node, auto-competition worked right out of the box.  With Cocos2D-html, there is a bit more work to do.

 

Here, we are going to cover that configuration process.  This process actually applies to configuring any JavaScript library to work with WebStorm, not just Cocos2D.

 

First things first, we need a project.  I am going to create one out of the code I created in the second Cocos2d tutorial. A WebStorm project is simply a directory full of code.  Let’s create one now.

 

Load up WebStorm ( I’m using 4.0.1 ).  Select File->New Project:

image

 

In the Create New Project dialog, under the location select the folder containing your project’s code, then in the Project name field enter your app directory name.  You may have to fight a bit with the UI to get it to work right, it really wants to create a new directory.  If you are creating a completely new project, you can ignore this bit.

 

image

 

Now it will prompt  you:

 

image

 

This is what we want, click yes.  This will simply create a .idea folder within your directory with the project data.  If all went well, you should now have a project like this:

 

image

 

Now the we have a working project, we can adjust the settings on it.  In the menu, select File->Settings:

image

 

Now scroll down and expand JavaScript and select Libraries:

image

 

On the right had side of the screen, locate and click the Add button:

image

 

Name it Cocos2D and click the Attach button:

image

 

Navigate to and select the Cocos2d folder, then click OK:

image

 

You may want to repeat the process for box2d and CocosDenshion ( audio library ) if you wish to use them as well.  Click OK when done.

 

Back in the settings menu, Click the Apply then OK button.

image

 

Now, when you are editing your JavaScript code, you will now have complete auto-completion data for the entire Cocos2D library:

 

image

 

 

Ahhhh, just like being back in Visual Studio with full Intellisense support. My brain can happily go back to forgetting all about functions and member variables! Smile

 

 

If you are looking for a solid JavaSript IDE, you really should check out WebStorm.  If you are looking for a great HTML5 based game library, you should check out Cocos2d-html. As you can see, they work well together.

 

The eagle-eyed viewer may have noticed the Sublime text project files. This is another great IDE (sorta) option that is worth checking out.  I switch back and forth between the two.

General , , ,

Month List

Popular Comments

Defold Engine Tutorial Series: Application Basics
Subscribe to GameFromScratch on YouTube Support GameFromScratch on Patreon


5. July 2016

 

In the previous tutorial we covered installing the Defold Engine and creating your first project.  Today we are going to be looking at the overall structure of a Defold game, as it varies slightly from previous game engines.  I assume at this point you’ve followed the previous tutorial, created an empty project and have it open in the Defold editor.

 

There is an HD video of this tutorial available here.

 

The default project should look something like:

image

 

The built-ins folder is exactly that.  It contains several resources and scripts usable in your game, but we will talk about that later.  The more important thing to be aware of here is the main folder and it’s contents.

Inside this folder we find main.collection, logo.atlas and a folder containing an image.  A collection is a very important concept in Defold.  In this particular usage, you can think of a collection as a level.  If we double click main.collection for example it will open up a scene editor, like so:

image

 

A collection isn’t simply a level however...  what it is is a container for game objects or other collections.  We will look at game objects in a second.  So yes, Collections can be used to compose game levels, but they have other purposes.  The also perform the role of “prefabs” in other engines.  For example you might have a prefab for an enemy, that contained the scripts controlling it, the graphics you used to draw it, the sounds it plays, etc.  This collection can then be used to “instance” a version of your prefab one or more times.

 

A moment ago I mentioned Game Objects.  This is another cornerstone to understanding Defold.  With main.collection open in the editor, you should see in the Outliner, the following:

image

 

You can see here that our collection (main.collection) contains a single Game Object named go, which in turn contains a single component sprite.  You’ll notice with sprite selected that the Properties show our sprite comes from /main/logo.atlas and uses the animation or image named logo and renders using the built in material sprite.material. 

If you’ve worked with a component based game engine, such as Unity or Unreal, should be immediately comfortable with this concept.  Basically Game Objects are the entities or items that make up your game.  You will notice with a game object selected, it has very few properties:

image

Basically a Game Object contains positioning information (location, rotation and scale) and an Id, which we will use later.

What makes a Game Object useful is it’s a container for Components.  If you right click a Game Object in the Outliner, you can Add Component (or press I)

image

 

You can then chose a component from the following list:

image

This is how you can attach a Sprite, Collision Object, Sound effect, etc... to your Game Object.

 

So in a nutshell, a Collection is just that, a collection of things.  Generally Game Objects and other Collections.  A Game Object is an entity within your game world.  It has a position and ID and is a container for several components.  Components are the things that make up your game object, sprites, sounds, etc.  Got it?

 

So then... what about code?

 

Well every game engine has a game loop somewhere.  A game loop is a pretty simple concept, it’s a loop that runs over and over again and looks something like this primitive psuedo code:

 

while(gameRunning()){
    updateWorld()
    updatePhysics()
    getInput()
    render()
}

 

The exact contents of the game loop change from engine to engine but if you look at enough engines you will notice they are all very similar in the end.  It’s a loop that runs, checks for input, updates the game world, updates the physics engine if one exists then renders the results.  The actual game loop itself is often hidden away in a game engine and Defold is no exception.  So how then do you program your game?

Well first off you create a script.  A script is just another type of asset that can be added to your project.  To do so, right click the folder you want the asset to be created in (main), then select New->Script File.

image

 

Next you want to name your script.  In Defold scripts have the .script extension. 

image

 

This will create a new script file.  Double click it to bring it up in the editor:

function init(self)
    -- Add initialization code here
    -- Remove this function if not needed
    print("Game Created")
end

function final(self)
    -- Add finalization code here
    -- Remove this function if not needed
end

function update(self, dt)
    -- Add update code here
    -- Remove this function if not needed
end

function on_message(self, message_id, message, sender)
    -- Add message-handling code here
    -- Remove this function if not needed
end

function on_input(self, action_id, action)
    -- Add input-handling code here
    -- Remove this function if not needed
end

function on_reload(self)
    -- Add reload-handling code here
    -- Remove this function if not needed
end

 

I added a simple print() function call in the init() method.  Remember what I was saying about game loops earlier?  Well in Defold, during it’s game loop it makes various callback function calls at different points in the loop.  In this case init() is called when the script is first created, final() is called when it is done.  All the other methods are called each pass through the loop. We will look at each of these methods in detail in the future.

 

So now that we have a script, we actually have to attach it to something to get it to run.  Thankfully we already have a game object in main.collection named go.  Let’s attach the script to it.  Make sure main.collection is being edited, then in the outliner, locate go, right click it and select Add Component From File.

image

 

Select main.script then click OK:

image

 

Now that script will be attached to that game object.

 

This leaves one last question... how does the Defold engine know to create our main.collection to start all of this off in the first place?  Well that’s where game.project comes in.  This file contains several global configuration settings for your game.

image

 

In this particular case however, it’s the bootstrap that we are interested in.

image

 

You can think of main_collection as the entry point of your game.

 

That’s it for this tutorial.  Now that we have a good understanding of what the pieces that go together to make a game, next we will jump in and start getting our fingers dirty.  This tutorial is not going to be covering Lua programming.  If you are new to the Lua language, be sure to check out this tutorial series and you will learn everything you need to know to get up to speed.

 

The Video

GameDev News

blog comments powered by Disqus

Month List

Popular Comments