Thursday, June 5, 2014

Getting Started with Cocos2d-JS 3.0 Part 1 - First Sprite

Prerequisites

  • Knowledge of how to download, unzip and install software packages and software on your computer and how to change environment variables for your operating system of choice (Linux / Windows)
  • Basic knowledge of JavaScript (should know what closures are, what a callback function is, etc.) in other words not a complete beginner. If you need a refresher on JavaScript I suggest this tutorial.
  • Some experience using Eclipse-like IDEs would be helpful (although not necessary). If you can step through a debugger you will be fine.

Introduction

Cocos2d-JS 3.0 is an html5 framework derived from cocos2d-x. It was recommended to me by a coworker as a JavaScript framework. This will be a tutorial on how to get started with Cocos2d-JS with the most basic type of rendering -- a 9-slice sprite from the very beginning (downloading, installing, configuration and code sample). Proper scaling is crucial to resolution independent and mobile development. Later tutorials will cover event handling, physics and more advanced topics.

Because installation, configuration and setup is often the first major hurdle, much of Part 1 will be spent on creating your environment (stick with it, trust me it pays off) or at least pointing you in the right direction.

As of June 1st, 2014 Cocos2d-JS is still in beta stage so not ready for production. But it is looking very promising and is just a few weeks/months away from production (already been through alpha). Since cocos2d-JS 2.0 was rock solid for years and the team appears dedicated I have no reason to doubt 3.0 will eventually be released production ready.

This tutorial will assume you have an Ubuntu-like system. If you are familiar with unzipping and changing environment variables on Windows, you should also be able to follow along since the code will be compatible.

Advantages of Cocos2d-JS 3.0 (as of June 1st, 2014) or Why Use Cocos2d-JS?

  • HTML5 compatible so mobile friendly with wrappers like Intel Crosswalk (Android) or Ejecta (iOS). Also has JSB (JavaScript bindings) to compile with cocos2d-x to native, though the JSBs lag behind the main trunk for now.
  • JavaScript for fast development (debatable)
  • MIT License

Disadvantages (few, but worth mentioning) or Why Not to Use Cocos2d-JS

  • A little more difficult to use compared to other JavaScript frameworks. This is being resolved by the cocos2d-JS team (they acknowledge this).
  • Cutting edge (as of June 1st, 2014) so not ready for production. Not a big deal since it will be ready eventually.
  • Not too many tutorials or examples (one of the reasons I'm writing this, plus will get solved eventually when the framework is out of beta).
  • JavaScript (for people who hate / can't stand JavaScript)
The goals of this tutorial will be to setup your environment and create reusable sample code for a 9-slice sprite. A 9-slice sprite is a sprite which scales properly on all devices without distorting the image, crucial for mobile friendly games and apps.

Step 1 - Downloading Cocos2d-JS


Download the Cocos2d-JS v3.0 beta. By the time you read this, cocos2d-js may be out of beta.



The setup will ask you a few questions, mainly about Android NDK and SDK locations and ant locations. You should have ANT properly installed and setup; if not, run sudo apt-get install ant and rerun the cocos setup. We will be building for web only, so we will not bother with Android for now.

If you are having trouble setting the environment variables or downloading the prerequisites (Python 2.7.6 32-bit, not Python 3, Ant) see the section SETUP here. Unfortunately installing Python, Ant and so on is outside the scope of this tutorial; however your distro should have these preinstalled. If this is a problem for some people I can write another article about how to install packages in Ubuntu.

Step 2 - Downloading Aptana Studio


Download the Aptana Studio standalone version. Unfortunately Aptana Studio does not support OpenJDK (the default Java installation that comes with Ubuntu) so you will need to switch to Oracle JDK to use it. Switching from Open JDK to Oracle JDK is outside the scope of this tutorial; see this link for a comprehensive tutorial. If you are using Windows, Aptana Studio comes with a prepackaged JDK.

If you don't want to bother with the switch, or you hate Eclipse-style IDEs in general you can use Jetbrains Webstorm. However Jetbrains Webstorm is non-free. However Webstorm has much smarter autocompletion and if you are doing a serious project I would consider buying a license.

Note about Aptana Studio Bug #1: I encountered a bug when using Aptana Studio with Linux Mint (an Ubuntu variant) and found the solution here. It involves the default web browser and Firefox.

Note about Aptana Studio Bug #2: I encountered another bug when using Aptana Studio debugger with Firefox. If you see a window like below when running debug mode, uninstall the Firebug addon in Firefox, uninstall the Aptana Studio addon in Firefox, close all your browser windows and restart debug mode in Aptana Studio. The problem is with Firebug > 1.8.3 not being compatible with Aptana Studio (another reason to shell out the bucks for an IDE like Webstorm if you are doing a serious project, this bug has been around for quite some time).



Step 3 - Creating the Cocos2d-JS Project


Run the following from wherever you create new projects:

You should see the default Cocos2d-JS hello world screen now.



Step 4 - Importing into Aptana Studio

Start Aptana Studio from the folder you installed Aptana Studio in. It will prompt you for a workspace. Create a new folder and select a workspace for Aptana.





Click the "Import Project" button and go to "Existing Folder as New Project". Then pick the folder with the web application.



Step 5 -  Get a 9-slice Sprite

This is the easiest part of the tutorial. Get the 9-slice (9patch in Android) image for free courtesy of dibbus.com




Next, place the image in the res directory of the 01-sprite cocos2d-JS project (assuming btn_red_matte.9.png).




Now, add btn_red_matte.9.png to resource.js as one of the available resources.


Step 6 - Get the required information from the sprite

We require the following information from btn_red_matte.9.png in order to render the 9-slice sprite. Note that in Cocos2d-JS capInsets is a rectangular area, not margins of pixels to avoid scaling as in Cocos2d-x. Don't worry if you don't understand all of it; it makes more sense in Step 7 with the code.
  • Bottom left x coordinate - Cocos2d-JS treats the bottom left corner as the origin point. We will pick a portion of the image to render. The image is larger than we need, since it comes from Android and Android uses the Draw 9-patch tool to create 9-slice sprites. We do not need the black marks or the margin because Cocos2d-JS will use capInsets to define the scalable area instead (explained below). Therefore, the portion should be the red area of the image and only the red area. You will create a rectangle with the bottom left x, bottom left y, width and height then pass it in as the second parameter to the cc.Scale9Sprite constructor. 
  • Bottom left y coordinate - Same as above
  • Width - Width of rectangular area of image to render
  • Height - Height of rectangular area of image to render
  • capInsets - The capInsets are a little different than in Cocos2d-x. In iOS and Cocos2d-x you would define the capInsets as the number of pixels of top, left, bottom and right of the selected area in that order to ignore scaling. In Cocos2d-JS the "capInsets" parameter is actually an instance of cc.rect which is the stretchable area within the sprite. The green area below is the "capInsets" parameter for Cocos2d-JS. You will create this rectangle and pass it in as the third parameter to the cc.Scale9Sprite constructor.




Using GIMP, we discover that the bottom left x-coordinate of the sprite we want is 2,2 and it is 44 pixels wide and 47 pixels high. Note that GIMP doesn't treat the bottom left as (0,0) like Cocos2d-JS does so you'll have to do some simple math.

As well, using GIMP we discover that the inner green rectangle (the area we want to allow to scale) has its origin at (8,7) and is 32 pixels wide and 36 pixels high.

Step 7 - Code


At long last, the code.

First, modify the project.json file to add the "extensions" module to the cocos2d-JS boot loader. cc.Scale9Sprite is an extension.


Open up app.js and delete all the text in the file. We will code from scratch.

Fill up app.js with the following (explanation below the code):
Lines 1 to 7 should be self-explanatory. HelloWorldScene is called by main.js by the boot loader. This is unmodified from the default HelloWorld application when you first create a Cocos2d-JS project. The scene is created, which is basically a container for layers. Then the layer is created, defined below.

Lines 11 to 34 are the object properties.

Line 39 to 42 is the constructor. It calls the init() function to initialize the layer.

Lines 48 and 49 set a couple instance variables of the layer (the width and height, which we will use to scale.)

Line 50 schedules a update every frame. The update function (ticker) will be called each frame. Usually the update function will have a delta time parameter (dt) to model physics. However in this tutorial we will simply use the update function once, to draw three sprites scaled without distortion.

Line 58 creates conditions to draw the buttons. We only want to draw the buttons once (since they don't move) and check whether the buttons have been created previously. If they haven't we enter the block.

Lines 60 to 62 create the buttons. Down at lines 90 to 94 you see the create button function. For simplicity sake we have hardcoded the dimensions of the delimitation zone (area of the .png file to render) and the capInsets (area of the sprite to allow to scale). Note that the Cocos2d-JS definition of capInset is different than the Cocos2d-x definition or indeed the iOS definition.

Lines 65, 68 and 71 draw the buttons on the screen. This is done using three functions, drawButton, getWidthPercent and getHeightPercent on lines 79, 97 and 101.

For the getWidthPercent and getHeightPercent functions, it is a simple calculation of number of pixels for a given percentage. If 20 is passed to the function, it calculates 20 percent of the pixels available on the width/height of the screen and returns it. If 50 is passed to the function, it calculates 50 percent of the pixels available on the width/height of the screen and returns it.

At line 80 the drawButton function first sets the anchor point to (0,0). An anchor point is the
 "origin" point in the sprite's internal coordinate system where all the transformations are applied. It is also where the sprite's position (its x and y coordinate) is centered.  The anchor point is by default (0.5,0.5). We want it at the bottom left of the sprite, (0,0), so that when the sprite is scaled up it is scaled to the right and upwards, not below and to the left.

At line 81 we use the setContentSize function to scale the image up and down without distortion. Note that using the setScaleX and setScaleY functions will distort the sprite, ruining the whole point of a 9-slice sprite. By using setContentSize we make sure the edges are not scaled upwards and only the center of the button is scaled.

At line 82 and line 83 we set the x and y position. Since we set the anchor point at line 80 to the bottom left of the sprite, the x and y position is fixed regardless of whatever scale we set at line 81 using setContentSize.

Finally at line 84 the button is added as a child to the current layer.

You should get a screen similar to the following





Note that the corners and edges are not distorted in any way. You can now create buttons and sprites without distortion of any size with a relative percentage of the screen. Congratulations.

The entire project is available here. Note you should do it yourself by reading the above blog post instead of downloading this, since the version of Cocos2d in that zip will be drastically outdated by the time you've read this.

The next tutorial will deal with basic physics and collision detection.