Subscribe to GameFromScratch on YouTube Support GameFromScratch on Patreon
25. July 2012

 

This post is a table of contents of sorts for the recently completed series documenting the creation of a simple game (web application) for my daughter.  Although the game is quite simple, the application itself covers quite a bit.  There are a ton of tutorials on the internet about the various individual pieces I use, but very few illustrating putting them all together to create a complete app.

 

In this tutorial, we cover:

 

Part 1 -- Node and cocos2D

Setting up a NodeJS server, that is able to serve both static and dynamic content using express.  By the end of this part we are successfully hosting a cocos2D application.

 

Part 2 -- Deploying to Heroku

This part covers deploying your Node application into the cloud, using Heroku’s completely free tier.  This part is optional, you can run your application anywhere you want so long as Node is supported.

 

Part 3 – The guts and plumbing

This part is the heart of the application itself.  It illustrates how to upload and serve data from a Node server.  The upload portion is managed using the YUI framework from Yahoo.  This is how you could make a more traditional web application using JavaScript and illustrates.

 

Part 4 – The Game

This part creates the actual “game” if you can call it that.  It illustrates creating a simple cocos2D HTML game that interacts with the NodeJS server side.

 

Part 5 – Adding a Database to the mix

Losing all your data every time Node restarts or Heroku feels like erasing them gets old quick, so I added a database to the mix.  In this case I used the CouchDB NoSQL database, hosted on IrisCouch using the Nano library.

 

Part 6 – Phonegap?

Ok, this part is actually TBD.  I am in the process of porting to PhoneGap, to bundle this application as a native phone application.  Will update here if it was successful with another tutorial post

 

 

 

The Results

 

You can see the application running here

 

It is pretty simple over all.  Choose a pair of images using the dropdowns at the top.  Then you can click to cycle through the various images.  Additionally, you can click the settings button, which will bring you to the settings app we created in Part 3.  Here you can upload new images and manage existing one.   Warning, anyone can upload images, so I take no responsibility for what they might contain!  Anyone can also delete images at any times, so if it is empty or your images disappear, this is probably why.

 

Finally, I have pushed the complete source tree up to GitHub, which is available here.

General


25. July 2012

In prior parts we setup a Node.js/Express server to serve a cocos2D HTML project, showed how to host that project in the cloud using Heroku, then we created the backbone of the application itself, providing the ability to populate the app with data, as well as a means to retrieve that data.

 

Now, it’s time to actually create the game itself.

 

As I mentioned at the very beginning, the game is remarkably simple and isn’t really a game at all.  It is a simple series of two configurable pictures, it shows both side by side, then if you click it one zooms in, then if you click again, the next image zooms in, then finally, both images are shown side by side again.  The end result is I can show my daughter a sequence of events so she can better understand cause and effect.

 

Here is a screenshot of our ultimate end result:

image

 

Not earth shattering by any means, but it a) accomplishes what I need to accomplish and can be hosted on any device and accessed anywhere I need it  b) demonstrates all of the core technologies needed to make a much more complex web hosted game or web application.

 

 

Lets jump right in and look at the code.  The graphics are powered by cocos2D HTML, a 2D JavaScript based game library.  You can read some tutorials about it here, as I am not going to get into how cocos2D works in any detail.

 

First we need our AppDelegate.js class, which fires up our cocos2D game.

var cc = cc || {};

cc.AppDelegate = cc.Application.extend({
    ctor:function () {
        this._super();
    },
    initInstance:function () {
        return true;
    },
    applicationDidFinishLaunching:function () {
        var pDirector = cc.Director.sharedDirector();

        var size = pDirector.getWinSize();
        pDirector.setAnimationInterval(1.0 / 60);
        var pScene = FirstThis.scene();
        pDirector.runWithScene(pScene);
        return true;
    },
    applicationDidEnterBackground:function () {
        cc.Director.sharedDirector().pause();
    },
    applicationWillEnterForeground:function () {
        cc.Director.sharedDirector().resume();
    }
});

 

For more information on what is happening here, check the tutorial link I posted earlier.  The most important part to us is the creation of FirstThis, which is the heart of our application.

 

Let’s take a look at FirstThis.js.  Again, I apologize for the wonky formatting, it was done to fit the blog.

 

var FirstThis = cc.LayerColor.extend({
    leftSprite:null,
    rightSprite:null,
    mode:0,
    imageChanged:function(imgName,whichSprite){
        if(this.mode != 0) // We were in transition when select box was changed!
        {
            this.resetVisibility();
            this.mode=0;
        }
        this.removeAllChildrenWithCleanup(true);

        if(this.leftSprite != null && whichSprite=="right")
        {
            this.addChild(this.leftSprite);
        }
        if(this.rightSprite != null && whichSprite=="left"){
            this.addChild(this.rightSprite);
        }

        var imageSize;
        YUI().use('node','io-base',function(Y){
            var results = Y.io("/imageSize/" + imgName, {"sync":true});
            imageSize = JSON.parse(results.responseText);
        });

        var newSpriteWidth = cc.Director.sharedDirector().getWinSize().width/2;
        var newSpriteHeight = cc.Director.sharedDirector().getWinSize().height/2;

        if(whichSprite == "left"){
            this.leftSprite = cc.Sprite.create("/image/" + imgName,
                new cc.Rect(0,0,imageSize.width,imageSize.height));
            this.addChild(this.leftSprite);
            this.leftSprite.setScale(
                (newSpriteWidth * this.leftSprite.getScaleX())/imageSize.width);
            this.leftSprite.setAnchorPoint(new cc.Point(0,1));
            this.leftSprite.setPosition(
                new cc.Point(0,cc.Director.sharedDirector().getWinSize().height));
        }
        else
        {
            this.rightSprite = cc.Sprite.create("/image/" + imgName,
                new cc.Rect(0,0,imageSize.width,imageSize.height));
            this.addChild(this.rightSprite);
            this.rightSprite.setScale(
                (newSpriteWidth * this.rightSprite.getScaleX())/imageSize.width);
            this.rightSprite.setAnchorPoint(new cc.Point(0,1));
            this.rightSprite.setPosition(
            new cc.Point(newSpriteWidth,cc.Director.sharedDirector().getWinSize().height));
        }
    },
    resetVisibility:function()
    {
        this.leftSprite.setIsVisible(true);
        this.rightSprite.setIsVisible(true);
        this.leftSprite.setPosition(
            new cc.Point(0,cc.Director.sharedDirector().getWinSize().height));
        this.rightSprite.setPosition(
                new cc.Point(cc.Director.sharedDirector().getWinSize().width/2,
                cc.Director.sharedDirector().getWinSize().height));
    },
    ctor:function()
    {
        this._super();
    },
    init:function()
    {
        this.setIsTouchEnabled(true);
        this.initWithColor(cc.ccc4(0,0,0,255));

        var that = this;

        YUI().use('node',function(Y){
            Y.one("#firstSel").on("change",function(event){
                if(event.currentTarget.get("selectedIndex") == 0) return;
                    that.imageChanged(event.currentTarget.get("value"),"left");
            });
            Y.one("#thenSel").on("change",function(event){
                if(event.currentTarget.get("selectedIndex") == 0) return;
                    that.imageChanged(event.currentTarget.get("value"),"right");
            });
        });
        this.setAnchorPoint(0,0);
        return this;
    },
    ccTouchesEnded:function (pTouch,pEvent){
        if(this.leftSprite != null && this.rightSprite != null ){
            this.mode++;
            if(this.mode == 1)
            {
                this.leftSprite.setIsVisible(true);
                this.rightSprite.setIsVisible(false);
                this.leftSprite.setPosition(
                        new cc.Point(cc.Director.sharedDirector().getWinSize().width/4,
                        cc.Director.sharedDirector().getWinSize().height));
            }
            else if(this.mode == 2)
            {
                this.leftSprite.setIsVisible(false);
                this.rightSprite.setIsVisible(true);
                this.rightSprite.setPosition(
                    new cc.Point(cc.Director.sharedDirector().getWinSize().width/4,
                        cc.Director.sharedDirector().getWinSize().height));
            }
            else{
                this.resetVisibility();
                this.mode = 0;
            }
        }

    }
});


FirstThis.scene = function() {
    var scene = cc.Scene.create();
    var layer = FirstThis.layer();

    scene.addChild(layer);
    return scene;
}

FirstThis.layer = function() {
    var pRet = new FirstThis();

    if(pRet && pRet.init()){
        return pRet;
    }
    return null;
}

 

Again, most of what is going on here is covered in the earlier tutorial, but I will give a quick overview, and point out some of the application specific oddities as I go.

 

Starting from the top, we declare a pair of variables for holding our two active sprites, as well as a value for the current “mode”, which essentially represents our click state, which will make sense shortly.

 

We then define a method imageChanged which will be called when the user selects a different value in one of the two select boxes at the top of the screen.  On change we first chack to see if the mode is not 0, which means that we are in the process of showing images to the user ( meaning a single image might be visible right now ), in which case we reset visibility and positioning so our two images are side by side again, and reset the mode back to zero.  Then we remove all of the sprites from the layer, effectively erasing the screen.

 

Next we want to check if the user is updating the left or the right image.  If the user is updating the right image for example, we check to see if the left image has been assigned a value yet, and if it has assign it back to the scene.  This test is to prevent trying to push a null sprite onto the scene if the user hasn’t selected images with both drop-downs yet.  We do this for the left and right image.

 

Next we run into a bit of a snag.  This application wants to size images so they each take up 50% of the screen.  There is a problem with this however, as when you declare a sprite with cocos2D, unless you specify the image size, it doesn’t have any dimension data!  This is ok with a game when you will probably know the image dimensions in advance, but in this situation where the images are completely dynamic, it presents a major problem!  This is a problem we solved rather nicely on the server side using node.  We will look at the changes to server.js shortly, but for now realize we add a web service call that returns the specified image dimensions.  We then make a synchronous call using YUI’s io() module to retrieve that information… note the complete lack of error handling here!  My bad.

 

We ideally want to scale our image so their width is half the screen.  We now create our sprite, depending if it is on the left or right side, but the logic is basically identical.  First we create the sprite using the filename passed as a value from our select box, and the dimensions we fetched earlier.  We then add that file to the scene, scale it so the width is 50% of the screen ( scaling it up or down as required ), then position it relative to the top left corner of the sprite, with the X value changing if the sprite is on the left or right.

 

Next up we create the function resetVisibility() which we used earlier on.  Basically it just makes both sprites visible again and puts them back in their default positions.  Next we implement a simple constructor that calls layers constructor.  This is to verify some methods needed to handle touch input are properly called. The cocos2D tutorial on handling input covers this in a bit more detail.

 

Next up is our initialization function, named appropriately enough init().  We tell cocos2D that we are going to handle touch (click) events, that we want to create a layer with a black opaque background.  Next we wire up event handlers to our two select boxes that call our imageChanged() method when, um, and image is changed.  Lastly we tell our layer to anchor using the default bottom left corner.  This call is rather superfluous, as this is the default. I like to include it for peace of mind though, as if there is something I fight with ( and hate! ) about cocos, it’s the coordinate system.

 

Next up we have our touch handler ccTouchesEnded, which will be called when the user taps the screen or when they click a mouse.  This is where the mode variable comes into play.  At a value of 0, it means the screen hasn’t been touched yet, so on first touch, we set the left image to the center of the screen and the right image as invisible.  On the next touch, we do the opposite, then on any further touches we set both images back to their default positions and reset mode back to zero.  The remaining code is simple boiler plate setup code used to create our layer, which again is covered in more detail in these tutorials.

 

In a nutshell, that is our game’s code.  You may remember earlier we made a change to server.js, let’s take a look at the file now.

 

Here is server.js in it’s entirety.  Remember, you do not need to host on Heroku to run this app.  Simple run node server.js from the command line or terminal, then hit localhost:3000 in your web browser.

 

server.js

var express = require('express'),
    server = express.createServer(),
    im = require('imagemagick'),
    files = {};

server.use('/cocos2d', express.static(__dirname + '/cocos2d') );
server.use('/cocosDenshion', express.static(__dirname + '/cocosDenshion') );
server.use('/classes', express.static(__dirname + '/classes') );
server.use('/resources', express.static(__dirname + '/resources') );

server.use(express.bodyParser());

server.get('/', function(req,res){
    res.sendfile('index.html');
    console.log('Sent index.html');
});

server.get('/settings',function(req,res){
   res.sendfile('settings.html');
   console.log('Send settings.html');
});

// API calls
server.get('/image/:name', function(req,res){
    if(files[req.params.name])
    {
        res.contentType(files[req.params.name].contentType);
        res.sendfile(files[req.params.name].path);

        console.log("Returning file" + req.params.name);
    }
});

server.get('/imageSize/:name',function(req,res){
   im.identify(files[req.params.name].path,function(err,features){
       console.log("image/" + req.params.name);
       if(err) throw err;
       else
        res.json({ "width":features.width, "height":features.height });
   });
});

server.get('/getPhotos', function(req,res){
    res.json(files);

});

server.get('/clearAll', function(req,res){
    files = {};
    res.statusCode = 200;
    res.send("");
})

server.post('/upload',function(req,res){
    files[req.files.Filedata.name] = {
        "name":req.files.Filedata.name,
        "path":req.files.Filedata.path,
        "size":req.files.Filedata.size,
        "contentType":req.files.Filedata.type,
        "description":req.body.description };
        console.log(req.files.Filedata);

    console.log(Object.keys(files).length);
    res.statusCode = 200;
    res.send("");
});
server.listen(process.env.PORT || 3000);

Most of this code we have seen before, but there are a couple of new changes.  First thing to notice is:

    im = require('imagemagick'),

We added a new library to the mix to handle image process, the venerable ImageMagick.  You need to install this library before running this code.  First we need to add it to node, that is as simple as, from a command line, cd to your project directory then type:

npm install imagemagick

If you are deploying to Heroku, you also need to update the dependencies in the package.json file, so Heroku is aware of the dependencies.  That file should now look like:

{
    "name": "firstthis",
    "version": "0.0.1",
    "dependencies": {
        "express": "2.5.x",
        "imagemagick":"0.1.x"
    },
    "engines": {
        "node": "0.8.x",
        "npm":  "1.1.x"
    }
}

Finally, you need to install ImageMagick itself.  On Windows the easiest way is to download the binary installer, while for Linux it’s probably easiest to use a package manager of your choice.  Once you install imagemagick, be sure to start a new command line/terminal so it picks up the path variables image magick sets.

 

Ok, now that we have the dependency out of the way, the code itself is trivial:

server.get('/imageSize/:name',function(req,res){
   im.identify(files[req.params.name].path,function(err,features){
       console.log("image/" + req.params.name);
       if(err) throw err;
       else
        res.json({ "width":features.width, "height":features.height });
   });
});

We get file details about the image passed in to the url ( for example with the URL /imageSize/imagename.jpg, name will = imagename.jpg ), then if no errors occur, we return the width and height as a JSON response.

 

 

Finally, we get to the actual HTML, index.html which is served by Node if you request the “/” of the website.

<html>
<head>
 <script src="http://yui.yahooapis.com/3.5.1/build/yui/yui-min.js"></script>
 <script>
  YUI().use('node','io-base',function(Y){

    Y.on("load", function(){
     var canvas = Y.DOM.byId('gameCanvas');
     canvas.setAttribute("width",window.innerWidth-30);
     canvas.setAttribute("height", window.innerHeight-70);
     Y.Get.script(['/classes/cocos2d.js']);

    });

    Y.io('/getPhotos',{
     on: {
      complete:function(id,response){
       var files = JSON.parse(response.responseText);
       var firstSel = Y.DOM.byId("firstSel");
       var thenSel = Y.DOM.byId("thenSel");

       for(var key in files)
       {
           firstSel.options.add(
             new Option(files[key].description,files[key].name));
           thenSel.options.add(
             new Option(files[key].description,files[key].name));
       }
      }
     }
    });

   });
 </script>
</head>
<body style="padding:0; margin: 0; background: black">
 <form>
  <span style="align:left;vertical-align:top;padding-top:0px">
   <label style="color:white;height:40px;font-size:26;vertical-align:middle">
       First:
   </label>
   <select style="height:40px;font-size:22;width:250" id="firstSel">
       <option selected>Drop down to choose</option>
   </select>
   <label style="color:white;height:40px;font-size:26;
   padding-left:10px;vertical-align: middle;">Then:</label>
  <select style="height:40px;font-size:22;width:250" id="thenSel">
    <option>Drop down to choose</option>
   </select>
  </span>
  <span style="float:right;vertical-align: top;margin-top:0px;top:0px;">
   <input align=right type=button value=settings id=settings
       style="height:40px;font-size:26"
      onclick="document.location.href='/settings';"  />
  </span>
 </form>
<div width=100% style="text-align:center;clear:both;">
    <canvas id="gameCanvas">
        Your browser does not support the canvas tag
    </canvas>
</div>
</body>
</html>

Everything here we have seen before, except perhaps this small chunk of extremely important code:

    Y.on("load", function(){
     var canvas = Y.DOM.byId('gameCanvas');
     canvas.setAttribute("width",window.innerWidth-30);
     canvas.setAttribute("height", window.innerHeight-70);
     Y.Get.script(['/classes/cocos2d.js']);

    });

 

This code will be executed once the page finishes loading.  As you may notice, the canvas tag gameCanvas did not have a size specified, as we want it to take the full width of the device it is run on.  Unfortunately you cannot just say width=100% and be done with it, so we set the width and height programmatically when the page loads.  Finally, to make sure that cocos2D objects aren’t loaded and set until after the Canvas tag is resized, I deferred the loading until now, so after we resize the canvas, we dynamically load the cocos2d script, making sure it initializes with the proper dimensions.

 

So, after all our hard work, here is our running completed application in action.  ( Or direct link here, if you don’t want to see it in an iframe ).  Again a warning the data for this application is completely viewer driven, I give no guarantees the content is appropriate! Please be civil ).

 

 

That basically completes the application tutorial.  There are a few issues right now:

1- It doesn’t persist user data.  If the server restarts or a certain period of time elapses, all the data stored on Heroku is lost.  This is easily fixed, but beyond the scope of this tutorial

2- There is a complete lack of hardening or error handling, so the application is probably incredibly fragile

3- There are a few HTML bugs ( oh… aren’t there always? ).  The change event doesn’t always fire when you select the first drop down the first time ( this is a browser bug, and can be worked around, but the code isn’t tutorial friendly. ).  Also, on Safari mobile, cocos2D sprite scaling doesn’t appear to work.

 

 

I hope you found this series useful.  As I was working with git anyways for the Heroku deployment, I decided to make this entire project available on github.  So if you want to fork it and play around, have fun!  I am a massive github newbie though, so don’t be shocked if I screwed something up.  If you, like me, have a child that isn’t doing transitions very well, I hope you find it useful.  I am actually making a more production worth version of this application, so if you need an online first-then board, drop me a line!

 

On a final note, I am going to look into making a PhoneGap version of this application.  If that exercise bares fruit, I will have another section for this tutorial!

Programming


21. July 2012

Now that we have a cocos2D app being served from NodeJS and being hosted in the cloud we now need to get down to the application itself.

 

One of the biggest parts of this app is actually the plumbing behind the scenes.  Essentially I need to be able to upload images to a server and retrieve them on demand.  For each image I need to be able to add a description.  So that is what we are going to create here.

 

If you were ever wondering how to create a full web application using just JavaScript on the client and server, the end result of this exercise is going to show you exactly that.

 

There are a few warnings before hand!

 

1- Heroku doesn’t persist files.  So anything you upload to this example application, it will eventually be lost.  In my actual application I am going to be hosting the actual images to my Dropbox account or using a private Redis database, but in either case, it’s not information I want to share publically.  If you have questions about how I do either, I will gladly answer them.

 

2- THIS ONE IS IMPORTANT!!! I have shared full access to this application to everyone.  That means anyone at any time can upload any picture they want!  So, be aware before you click any links, who knows what you might see!  Also, anyone can delete all the existing images too, so again, don’t expect your uploads to survive long.  It is live for demonstration purposes only, if you want to run something similar, fork it and host your own privately.  Use at your own risk!  GameFromScratch.com takes no responsibility for any content that might be updated.

 

Finally, please be civilized about it.

 

Alright, the warnings and disclaimers out of the way, let’s continue.

 

We are going to take our existing cocos2D audio sample application and add a settings button to the top right corner, like so:

image

 

In order to do so, we are going to make the following changes to Index.html, the file that is served if people request the root of our application:

index.html

<html>
<body style="padding:0; margin: 0; background: #fff;">
<div stype="width:100%;height:50px">
    <form>
        <div align=right>
            <input align=right type=button value=settings id=settings
                  onclick="document.location.href='/settings';"  />
        </div>
    </form>
</div>
<div style="text-align: center; font-size: 0">
    <canvas id="gameCanvas" width="600" height="600">
        Your browser does not support the canvas tag
    </canvas>
</div>
</body>
</html>
<script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/1.7.1/jquery.min.js">
</script>
<script src="classes/cocos2d.js"></script>

 

Really, the only major changes here is the addition of a form, with a button inside with a crude JavaScript redirect to /settings.

 

In our node server application, we now need to handle this new route:

Add the following to server.js

server.get('/settings',function(req,res){
   res.sendfile('settings.html');
   console.log('Send settings.html');
});

This will now cause any requests to /settings to return settings.html.  This document is the webpage where most of the new functionality occurs.  It allows you to select multiple images ( at once using CTRL or SHIFT click ), apply a description to each, and upload it to the server.  It also allows you to review the existing images, as well as clear them all.

 

Let’s take a look at the code. 

settings.html

 

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
        "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
    <title>First This Settings</title>
    <script src="http://yui.yahooapis.com/3.5.1/build/yui/yui-min.js"></script>
    <script>
        YUI().use('uploader', function(Y){
            var uploader =
                new Y.Uploader(
                        {type:"HTML5",
                        uploadURL:"/upload/",
                        multipleFiles:true
                        }
                );

            uploader.render("#selectFiles");

            uploader.after("fileselect", function(event){
                var files = event.fileList;
                var table = Y.one("#filenames tbody");

                if(files.length > 0)
                {
                    Y.one("#buttonUpload").set("disabled",false);
                }

                Y.each(files,function(file){
                    table.append("<tr>" +
                            "<td>" + file.get("name") + "</td>" +
                            "<td>" + file.get("size") + "</td>" +
                            "<td><input type=text id=" + file.get("name") + "></td></tr>");
                });
            });

            uploader.on("alluploadscomplete", function(event) {
                // Hackish page reload
                alert("Files added successfully");
                document.location.href="/settings";
            });

            Y.one("#buttonUpload").on("click", function(){
                if(uploader.get("fileList").length > 0){
                    var perFileVars = {};
                    Y.each(uploader.get("fileList"),function(curFile){
                        perFileVars[curFile.get("id")] =
                        {description:Y.DOM.byId(curFile.get("name")).value};
                    });
                    uploader.set("postVarsPerFile",
                        Y.merge(uploader.get("postVarsPerFile"),perFileVars));


                    uploader.uploadAll();
                }
            });


        });

        YUI().use('io-base','node',function(Y){
            Y.io('/getPhotos',{
                on: {
                    complete:function(id,response){
                        var files = JSON.parse(response.responseText);
                        Y.log(files);
                        var table = Y.one("#existingFiles tbody");
                        for(var key in files)
                        {

                            table.append("<tr>" +
                                    "<td><a href=/image/" + files[key].name + ">"
                                    + files[key].name + "</a></td>" +
                                    "<td>" + files[key].size + "</td>" +
                                    "<td>" + files[key].description + "</td></tr>");
                        }
                    }
                }
            });

            Y.one("#buttonClear").on("click", function(){
                Y.io('/clearAll',{
                    on:{
                        complete:function(id,response){
                            document.location.href="/settings";
                        }
                    }
                });
            });

        });
    </script>
</head>
<body>
<form>
<div style="border:50px">
    <div id=selectFiles>
    </div>
    <div>
        <table id="filenames">
            <thead><tr>
                <th width=350px align=left>File Name</th>
                <th align=left>File Size</th>
                <th align=left>Description</th>
            </tr></thead>
            <tbody></tbody>
        </table>
    </div>
    <div align=right>
        <br /><br />
        <hr />
        <button disabled="true" type="button" id="buttonUpload"class="yui3-button"
                style="width:250px;height:40px;align:right;">Upload Files</button>
    </div>
    <div>
        <br /><hr />
        <table id="existingFiles">
            <thead><tr>
                <th width=150px align=left>File Name</th>
                <th align=left width=60px>File Size</th>
                <th align=left>Description</th></tr></thead>
            <tbody></tbody>
        </table>
    </div>
    <div align=right>
        <br /><br />
        <hr />
        <button type="button" id="buttonHome"class="yui3-button"
                onclick="document.location.href='/'"
                style="width:250px;height:40px;align:right;">Done</button>
        <button type="button" id="buttonClear"class="yui3-button"
                style="width:250px;height:40px;align:right;">Clear All Files</button>
    </div>
</div>

</form>
</body>
</html>

 

Please ignore the wonky formatting, it was done to fit it to this blog width.

As you can see right off the hop, this code makes use of the YUI libraries, a handy set of utilities to perform many common tasks.  In this case I am using them for the Uploader control, as well as for making networked requests back to the server with Y.io.  The YUI libraries are beyond the scope of this document, but fortunately their documentation is extremely good. If you would prefer to use jQuery instead, feel free.

 

In a nutshell, here is what we are doing ( from the top ):

  • create a YUI closure Y, with the uploader module functionality supported
  • create a new uploader, HTML5 style (instead of Flash), set its upload path ( the url it will submit to ) to /upload and finally tell it to support multiple files at once.  This is a big part of why I chose it in the first place, as otherwise with the standard HTML controls, you need to upload files one by one.
  • we then set the uploader to render to the selectFiles div we will define shortly. This simply tells the control where to draw itself.
  • We then wire up an event handler for after the user has selected files. If the user has selected at least one file, we enable the upload button, we then loop through the selected files and dynamically populate the table named filenames.  Perhaps most importantly, for each table row, we create a text field for entering the description, with the filename as it’s id.
  • Next we wire up an event handler for the “alluploadscomplete” event, which is fired predictably enough, when all uploads are complete. In this case we just display a message and redirect to the settings page.
  • We then wire up a click handler for the upload button.  This loops through all of the files select, and grabs the description value we specified in the text field and merges in to the data that is going to be posted back to the server.  Essentially this means the data you enter is each description field is going to be posted along with your files back to the server.  Finally we actually perform the upload by calling uploadAll().
  • Next I create a different YUI closure with different dependencies ( io-base and node ).  I do this simply as personal preference, as I find it makes code easier to move around.  If you preferred, you could have made a single call to YUI().use.
  • This code is basically going to be executed on page load or there abouts ( we don’t really care when ), which makes a network request to /getPhotos, which is a web service that returns a JSON structure of all the photos on the server.  We then loop through the results and display them to another table, existingFiles.
  • next we wire-up another button handler, this one is if the user clicks the Clear All button, which calls the clear all web service, which predictably enough, clears all the photos. Finally it reloads the page, so the tables will be redrawn.
  • Everything else is just standard HTML.

 

There is actually quite a bit of functionality packed in to a fairly small amount of code.  As we saw, we added a number of new functions to the server, lets take a look at them. 

 

server.js

var express = require('express'),
    server = express.createServer(),
    files = {};

server.use('/cocos2d', express.static(__dirname + '/cocos2d') );
server.use('/cocosDenshion', express.static(__dirname + '/cocosDenshion') );
server.use('/classes', express.static(__dirname + '/classes') );
server.use('/resources', express.static(__dirname + '/resources') );

server.use(express.bodyParser());

server.get('/', function(req,res){
    res.sendfile('index.html');
    console.log('Sent index.html');
});

server.get('/settings',function(req,res){
   res.sendfile('settings.html');
   console.log('Send settings.html');
});

// API calls
server.get('/image/:name', function(req,res){
    if(files[req.params.name])
    {
        res.contentType(files[req.params.name].contentType);
        res.sendfile(files[req.params.name].path);

        console.log("Returning file" + req.params.name);
    }
});

server.get('/getPhotos', function(req,res){
    res.json(files);

});

server.get('/clearAll', function(req,res){
    files = {};
    res.statusCode = 200;
    res.send("");
})

server.post('/upload',function(req,res){
    files[req.files.Filedata.name] = {
        "name":req.files.Filedata.name,
        "path":req.files.Filedata.path,
        "size":req.files.Filedata.size,
        "contentType":req.files.Filedata.type,
        "description":req.body.description };
        console.log(req.files.Filedata);

    console.log(Object.keys(files).length);
    res.statusCode = 200;
    res.send("");
});
server.listen(process.env.PORT || 3000);

 

First off, we have added the files var, which is going to hold our file data in memory.  The server.post() handler for upload is probably the most significant change.  This code takes the uploaded form information and populations our files data.  One key addition though was:

server.use(express.bodyParser());

This tells express to install a piece of middleware, that actually processes the form data, and is what makes the /upload method actually have data.

/clearAll is handled by simply erasing our files data, while /getPhotos simply returns that data in JSON format.

 

 

At this point if you are following along at home, you may be wondering how to submit your changes up to heroku.  The process is fairly simple, from a command prompt in your app folder:

heroku login ( give username and password )

git add .

git commit –m “description of changes”

git push master heroku

 

 

In a nutshell these commands a) log you in to heroku b) adds all the new files to your local git repository c) commits the changes you have made into your git repository d) pushes that repository up to heroku. Heroku will automatically restart your app.

 

Here then is our application running on heroku.  Click the settings link in the top right corner to bring up the new settings page we just created.

 

 

In the next part, now that we have a server, a client and some data to work with, we will get down to the actual task of creating our simple game.

 

Here ( assuming it is running ), is our application.  Again, I warn you about whatever content might be live!

General Programming


22. June 2012

 

In this tutorial we are now going to implement a web app end to end using YUI and hosted in Node.  It isn’t actually going to do anything much, but it will illustrate everything you need to create a client and server in JavaScript using YUI App and Node.  Over time we will add more bells and whistles, but there is actually everything you need here to create a full functioning web application.

 

After looking in to it a little closer, YUI is incredibly well documented, but actually implementing YUI App Framework in a real world environment there are gaps of information missing.  Generally all you are going to find is samples with a gigantic HTML files where all of the script is located in a single file, which obviously isn’t good form nor very maintainable.  Unfortunately, when you actually set about splitting your YUI App into separate files and templates, you are completely on your own!  In fact, this tutorial may in fact be the first one on the subject on the internet, I certainly couldn’t find one. 

 

That said, there are still some great resources I referred to over and over well figuring this stuff out.  First and foremost, was the App Framework link I posted earlier.  It effectively illustrates using models and views, just not how to organize them across files in a complex project.  The GitHub contributor app was another great sample, but it again, was effectively one giant HTML file.  Finally there is the photosnear.me application’s source code up on GitHub.  It is a full YUI App/NodeJS sample and is certainly worth reading, but as an example for people learning it is frankly a bit too clever.  Plus it renders templates out using Node, something I wanted to avoid.

 

Alright, lets do a quick overview of how program flow works, don’t worry, it’s actually a lot less complicated than it looks. It is initially over-engineered for what it ends up accomplishing.  However, in the end you basically have the bones of everything you need to create a larger more complex application and a code structure that will scale with your complexity.

 

image

This ( in the image to the left ) is the file hierarchy that we are about to create.  In our root directory are two files, server.js which is our primary NodeJS application, while index.html is the heart of our web application, and where the YUI object is created.

 

Additionally, in a folder named scripts we create a pair of directories models and views.  Models are essentially your applications data, while Views are used to display your data.  Finally within the views folder we have another folder templates which is where our handlebar templates reside.  If you have done any PHP or ASP coding, templates are probably familiar to you already.  Essentially they are used to dynamically insert data into HTML, it will make more sense shortly.

 

We are going to implement one model, person.js, which stores simple information about a Person, and one view person.View.js which is responsible for displaying the Person’s information in the browser, and does so using the person.Template.

 

Now lets actually take a look at how it all works, in the order it is executed.

 

First we need our Node based server, that is going to serve the HTML to the browser ( and do much much more in the future ).  Create a new file named server.js

var express = require('express'), server = express.createServer(); server.use('/scripts', express.static(__dirname + '/scripts')); server.get('/', function (req, res) { res.sendfile('index.html'); }); server.get('*', function (req, res) { res.redirect('/#' + req.url, 302); }); server.listen(process.env.PORT || 3000);

 

Essentially we are creating an express powered server.  The server.use() call enables our server to serve static ( non-dynamic ) files that are located in the /scripts folder and below.  This is where we serve all of our javascript and template files from, if we didn’t add this call, we would either need to manually map each file or you will get a 404 when you attempt to access one of these files on server.  Next set our server up to handle two particular requests.  If you request the root of the website ( / ), we return our index.html file, otherwise we redirect back all other requests back to the root with the url appended after a hash tag.  For more details, read this, although truth is we wont really make much use of it.  Finally we start our server to listen on port 3000 ( or process.env.PORT if hosted ). Amazingly enough, these 9 lines of code provide a full functioning if somewhat basic web server.  At this point, you can open a browser and browse to http://localhost:3000, well, that is, once you start your server.

 

Starting the server is as simple as running node server.js from your command line.  This assumes you have installed NodeJS and added it’s directory to your PATH environment variable, something I highly recommend you do.  Now that we have our working server, lets go about creating our root webpage index.html.

<!DOCTYPE html> <html> <head> <title>GameFromScratch example YUI Framework/NodeJS application</title> </head> <body> <script src="http://yui.yahooapis.com/3.5.1/build/yui/yui-min.js"></script> <script src="/scripts/models/person.js"></script> <script src="/scripts/views/person.View.js"></script> <script> YUI().use('app','personModel','personView', function (Y) { var app = new Y.App({ views: { personView: {type: 'PersonView'} } }); app.route('/', function () { var person = new Y.Person(); this.showView('personView',{model:person}); }); app.render().dispatch(); }); </script> </body> </html>

 

The most important line here is the yui seed call, where we pulling in the yui-min.js, at this point we have access to the YUI libraries.  Next we link in our model and view, we will see shortly.  Ideally you would move these to a separate config file at some point in the future as you add more and more scripts.  These three lines cause all of our javascripts to be included in the project.

 

The YUI().use call is the unique way YUI works, you pass in what parts of YUI library you want to access, and it creates an object with *JUST* that stuff included, in the for of the Y object.  In this case, we want the YUI App class ( and only it! ) from the YUI framework, as well as our two classes personModel and personView, which we will see in a bit more detail shortly.  If you use additional YUI functionality, you need to add them in the use() call.

 

We create our app and configure it to have a single view named personView of type PersonView.  Then we set up our first ( and only route ), for dealing with the URL /.  As you add more functionality you will add more routes.  In the event a user requests the web root, we create a person model.  Next we show the personView and pass it the person model we just created.  This is how you connect data and views together using the YUI app framework.  We then render our app and call dispatch(), which causes our app url to be routed ( which ultimately causes our person model and view to be created. If you aren’t used to Javascript and are used to programming languages that run top down, this might seem a bit alien to you at first.  Don’t worry, you get used to it eventually… mostly).

 

Now lets take a look at our model person.js

YUI.add('personModel',function(Y){ Y.Person = Y.Base.create('person', Y.Model, [],{ getName:function(){ return this.get('name'); } },{ ATTRS:{ name: { value: 'Mike' }, height: { value: 6 }, age: { value:35 } } } ); }, '0.0.1', { requires: ['model']});

 

Remember in index.html in the YUI.use() call where we specified personModel and personView, this is how we made those classes accessible.  By calling YUI.add() we add our class into the YUI namespace, so you can use YUI.use() to included them when needed, like we did in index.html.

 

Next we create our new class, by deriving from Y.Model using Y.Base.create(),  you can find more details here.  We declare a single function getName(), then a series of three attributes, name, height and age.  We set our version level to ‘0.0.1’ chosen completely at random.  When inside a YUI.add() call, we specify our YUI libraries as a array named requires instead of in the YUI.use call.  Otherwise, it works the same as a .use() call, creating a customized Y object consisting of just the classes you need.

 

Now lets take a look at the view, person.View.js

 

YUI.add('personView',function(Y){ Y.PersonView = Y.Base.create('personView', Y.View, [], { initializer:function(){ var that=this, request = Y.io('/scripts/views/templates/person.Template',{ on:{ complete:function(id,response){ var template = Y.Handlebars.compile(response.responseText); that.get('container').setHTML(template(that.get('model').getAttrs(['name','age','height']))); } } }); }, render:function(){ return this; } }); }, '0.0.1', { requires: ['view','io-base','personModel','handlebars']});

 

Like person.js, we use YUI.add() to add personView to YUI for availability elsewhere.  Again we used Y.Base.create(), this time to extend a Y.View.  The rest that follows is all pretty horrifically hacky, but sadly I couldn’t find a better way to do things that way I want. The first horrible hack is that:this, which is simply taking a copy of PersonView’s this pointer, as later during the callback, this will actually represent something completely different.  The next hack was dealing with including Handlebar templates, something no sites that I could findon the web illustrate, because they are using a single HTML file (which makes the task of including a template trivial).

 

The problem is, I wanted to load in a Handlebars template( we will see it in a moment ) in the client  and there are a few existing options, none of which I wanted to deal with.  One option is to create your template programmatically using JavaScript, which seemed even more hacky ( and IMHO, beats the entire point of templating in the first place! ).  You can also precompile your templates, which I will probably do later, but during development this just seemed like an annoyance. The photosnear.me site includes them on the server side using Node, something I wanted to avoid ( it’s a more complex process over all, and doesn’t lend itself well to a tutorial ).  So in the end, I loaded them using Y.io. Y.io allows you to make asynchronous networking requests, which we use to read in our template file person.Template.   Y.io provides a series of callbacks, of which we implement the complete function, read the results as our template, “compile” it using Y.Handlebars, we then “run” the template using template(), passing it the data it will populate itself with.  In our case, our name, age and height attributes from our personModel.  template() after executing contains our fully populated html, which we set to our views container using the setHTML() method.

 

Finally, lets take a look at person.Template, our simple Handlebars template:

<div align=right> <img src="https://www.gamefromscratch.com/image.axd?picture=HTML-5-RPG_thumb_1.png" alt="GameFromScratch HTML5 RPG logo" /> </div> <p><hr /></p> <div> <h2>About {{name}}:</h2> <ul> <li>{{name}} is {{height}} feet tall and {{age}} years of age.</li> </ul> </div>

 

As you can see, Handlebar templates are pretty much just straight HTML files, with small variations to support templating.  As you can see, the values {{name}}, {{height}} and {{age}} are the values that are populated with data.  They will look at the data passed in during the template() call and attempt to find matching values.  This is a very basic example of what Handlebars can do, you can find more details here.

 

Now, if you haven’t done so, run your server using the command node server.js, if you have set node in your PATH.

 

Then, open a web browser and navigate to http://localhost:3000, and if all went well you should see:

 

image

 

 

Granted, not very exciting application, but what you are seeing here is a fully working client/server application with a model, view and templating .  There is one thing that I should point out at this point… in the traditional sense, this isn’t really an MVC application, there is no C(ontroller), or to a certain extent, you could look at the template as the view, and the view as the controller!  But don’t do that, it’s really quite confusing! Smile  Just know, we have accomplished the same goals, our data layer is reusable and testable, our view is disconnected from the logic and data.  Don’t worry, the benefits of all of this work will become clear as we progress, and certainly, once we start adding more complexity.

 

In the near future, we will turn it into a bit more of an application.

 

 

You can download the project source code here.

Programming


21. June 2012

 

A few weeks ago, I mentioned that I might put together an HTML5 Roleplaying game tutorial and inquired how much interest there was in the subject.  There was sufficient interest, so I dove into the subject a little bit closer and came to a bit of a conclusion.  It’s ultimately theHTML-5-RPG tools that make the game in this case.  So, in addition to a game, I would have to create an editor, which is actually where most of the guts would reside.

 

This presented an interesting problem for me, as I’ve done a lot of web development, I mostly worked server side in a traditional language like C# or Java.  For this project, I wanted it to be JavaScript end to end.  I have a few key requirements:

 

  • single language, end to end
  • server based editor, that can be run locally on the client
  • code reuse between game and editor
  • MVC/MVP or MVVM based.  ( if you don’t know these acronyms, don’t worry )
  • event driven
  • rich UI
  • run on a tablet’s browser
  • database and local/remote filesystem support
  • json-based level output, which can be used by clients in any language
  • game entity scripting, probably again, in JavaScript

 

Part of these requirements are driven by personal interest, I have always wanted to try making an MVC based game editor.  MVC means, model-view-controller ( MVP == model view presenter and MVVM == model view viewmodel ) and it is a manner of design for decoupling your data ( your game’s/editor’s objects ) from your view ( your application or webpage ).  There are a number of great advantages to implementing things this way, including testability, maintainability, reuse and perhaps most importantly, it imposes a clean separation of responsibilities between systems.

 

The single language end to end is an easy one.  JavaScript.  While JavaScript is by no means the best designed language in the world, it is a extremely well supported one with a very bright future.  It is increasingly becoming a language that every developer is going to need to know, so why fight the future?  Of course, I could use a slightly higher level implementation like Coffeescript, Closure or Dart, they all ultimately compile down to JavaScript in the end.  That said, one of the biggest reasons I want to use a single language from end to end, is so the most people can follow along without having to know or learn multiple programming languages, so I will probably go with plain Jane JavaScript.

 

Now the whole running on a server and locally, that presented a bit of a trick.  I want people to be able to follow along, and run it from their own computer for their own projects, so offline is a must.  However, I also wanted an editor available for people who are only interested in game side of the equation, which is why I want to host one on my server.  That said, I also don’t want to bog my own servers down too badly.  This presented a bit of a problem, but it was solved soon enough when I started looking into….

 

Database and local/remote file system support.  Behind the scenes, a lot of tools are actually built around a database, whether they realize it or not.  In the end, many tools end up creating their own crude database server around their own file format, or often XML.  In my view, this is awfully close to re-inventing the wheel.  If you are using database like functionality in your tool, use a database!  Fortunately when it comes to JavaScript, there are an absolute ton of options!  From redis, a simple to learn key/value based database and JSON based CouchDB to more traditional databases like MySQL and SQL Server.

 

HTML5 has some options when it comes to local storage, such as well… webStorage.  There are some pretty heavily limitations here, one of the biggies is the lack of support.  Size limits are also rather severe size limitations, in the area of 2.5-5MB, a limit that you will run into extremely quickly.  The alternative to persist these files on the server isn’t really appealing to me, when I am the one paying the server bills! Smile

 

This is where Node comes in.  Node nicely solves just about all of these problems.  Essentially I am going to develop the editor as a node based client/server, where the user has the option of installing the client and server locally, and running it just like any other application.  This gives me access to the local file system and whatever other libraries I need.  However, it also allows me to use the exact same code to provide a hosted version of the editor other users can simply run in their browser.  Essentially Node will act as the host for the DB, as a web server and as the interface between the local machine and the view.

 

Speaking of which, this leaves the view…

 

Again, as I said earlier, the majority of client/server programming I did was built over Java or C#, so the HTML5 / JavaScript approach was new to me, so I had to take a closer look at what options exist.  In short, there are an absolute ton of options… too many in fact.  However, my rather well defined needs narrows things down quite a bit.  In fact, I am down to a pair of options, and would love your opinion on them.

 

 

Option 1

 

jQuery for the UIjquery-logo1

jQueryMobiel for the mobile UI

Node for the backend

CouchDB or redis for the database layer

Express for the server bits

Backbone.js for the um, backbone ( this is where MVC comes in )

Moustache and icanhazjs for the templates

underscore, well, just to make things work

 

 

Option 2

 

YUI for the front end (desktop and mobile), routing, MVC and server bitsyuilib

Node for the backend

CouchDB or redis for the database.

Handlerbars for the templating.

 

 

 

Both have benefits and detriments, especially from the perspective of a tutorial.

 

jQuery is easily the most popular UI library out there, and there is a gigantic amount of support available ( and dozens of books ), with a gigantic community.  Backbone and Moustache are less used, but still well supported.  Unfortunately, this also means introducing a half dozen pieces of tech, a very confusing prospect.  Development on all of these products moves extremely fast, which is a double edges sword.  Finally, and this is highly personal, I hate the look of jquery and underscore code, it feels so… hackey.

 

YUI on the other hand, is from a single vendor, with much less supporting material but very good documentation and a very clean modular design.  More to the point, it is an end to end system so it is very consistent.  However, if something goes wrong the community is much smaller and the supporting materials aren’t as readily available.  Perhaps the biggest downside with YUI is the newness of it.  YUI3 is still in transition away from YUI2, and YUI App ( the YUI equivalent to Backbone ) is young and at times it shows.  From an engineering perspective though, YUI just feels more solid and less like a clever hack.

 

Right now, ease of explanation is winning out, and I am leaning towards using YUI.  Going with on all encompassing library is much easier to configure and explain to readers, so that is a big plus.

 

Any thoughts or opinions on the subject? 

 

Oh, and if this is all sounding extremely confusing, don’t worry, it really isn’t that bad.  The end product should still be a single archive you download and execute with a simple click. 

 

So, over the next few weeks ( or more ), we are going to be going off on a slightly odd tangent here at game from scratch, and crossing over into the world of web app development, I hope many of you find it interesting.  For those that don’t, don’t worry, I will still be publishing game development specific contents and tutorials too!

Design


AppGameKit Studio

See More Tutorials on DevGa.me!

Month List