Subscribe to GameFromScratch on YouTube Support GameFromScratch on Patreon
31. May 2012



Alright I admit it, something shiny and new came along and of course I am attracted to it!  As I mentioned a few days back, the Cocos2d-x team released Cocos2d-html, an HTML5 port of theCocos2dHTML5 popular Cocos2D iPhone game library.


I have never worked with a Cocos2D library yet, although Sony’s GameEngine2D is based heavily on it.  So I jumped in to the HTML5 version and I have to say I am extremely impressed so far.  Therefore I am going to do a (simple?) tutorial series on using Cocos2D-x to create HTML5 games.  I am not sure exactly how much detail I am going into but it should be kind of fun.  Expect a setup and Hello World tutorial post anytime.


Don’t worry though, I am still working on new PlayStation Suite tutorials, still intend to do an HTML5/RPG tutorial ( although whether I use HTML5, or a library like Cocos2D-x is up in the air, working with a library is so much nicer! ) in the near future and yes, I am going to finish my C++ game tutorial too.  What can I say, I love juggling projects.


So, if you are interested in developing 2D games targeting HTML5 web browsers be sure to keep an eye on this space, something will be coming soon!


If on the other hand, you are a Cocos2D veteran and you seem me doing something stupid, please let me know!




29. May 2012


This is one of those libraries I really have been meaning to check out.  Every time I turn around it seems like it’s been ported to another platform and today is no different!cocos2dbanner  Cocos2D can now target HTML5.



In the developers own words:


We are happy to announce that Cocos2d-html5 alpha is released!

This is the first version of Cocos2d-html5. Most of functionality and test cases from Cocos2d-X are implemented in Cocos2d-html5. Currently, Cocos2d-html5 utilizes canvas for rendering, the API is almost the same as Cocos2d-X and Cocos2d-iPhone. High level API will be wrapped in next phase, which will offer nicer interface for Javascript programmers and will also be compatible with the javascript binding of Cocos2d-x & Cocos2d-iPhone.

Cocos2d-html5 has two menu implementations. One is DOM menu, and the other one is canvas menu. DOM menu will run more efficiently, but the drawback is that all menu items will always above the canvas.

Test cases has being tested and works as expected on

  • Chrome 16 & 18,
  • Safari 5.1,
  • IE 9 & 10
  • Firefox 12.0.


You can run through all of the demos in the browser using the above link, or check out the getting started guide here.  At this point I think Cocos2D is basically available on every platform for every language short of BASIC for the Amstrad, but I am sure there is a port in the works!


I really have to check this library out one of these days.


16. May 2012


I asked a few days back about how many people would be interested in an HTML5/RPG series.  Between comments and emails I’ve received, there appears to be a fair bit of interest and I’ve decided to go ahead with it.  As HTML gaming is somewhat new to me, I am not sure yet exactly the format the series will take.  I think it will be a hybrid between a blog and a tutorial series; combining musings with instruction, with the ever present caveat that “I might be doing something really stupid!”.



Anyways, one of those things I always hated about browser development was cross browser support.  As I do more and more research, it seems that Internet Explorer is still very unpleasant to support.  What I got to wondering was, just how many people actually use IE anymore, I surely don’t 99.9% of the time.  So I took a look at this sites statistics since the start of 2012:




I was rather shocked to  see a few things.  First, I’ve known Chromes popularity has been growing, but I had no idea it was to that degree.  I suppose I shouldn’t be shocked, I use Chrome almost exclusively.  What I found perhaps the next most shocking is that Safari is beating out Internet Explorer!  I know both benefit from being bundled with the OS, but given the 10 to 1 sales gap between Windows and Mac, you would figure IE would be much higher.


That said, all of these statistics are fairly meaningless, as the audience to a site like GameFromScratch are going to be much more technically savvy than most sites.


I mostly just found them interesting and to use it as an excuse to not officially support IE when developing my tutorials, at least until Windows 8 ships with the next version.

Totally Off Topic

13. May 2012


I am just thinking about what project I want to embark on next and this is one currently in my head.  I am going to continue focusing on creating PlayStation Suite SDK tutorials but as a sideHTML-5-Logo project I am starting to think about creating a Massively Single Player Offline Role Playing Game ( MSPORPG! ) tutorial series.


It seems that, like it or not, HTML5 is going to only become more and more important to game development.  Therefore it is an area I have some interested in personally learning more about.  I have prior Javascript and HTML experience, as well as more recent Node and Appcelerator experience, but as a gaming platform it will all be new to me, which is kind of fun. Also I grew up on CRPGs, games like Ultima and the old AD&D Gold Box games are the reason I am a gamer, so I have always wanted to make a simple RPG.  With the recent releases ( and success ) of Dungeons of Dredmor and Legend of Grimrock, it’s obvious that the old school role playing games are still popular.



So I ask you all, would you be interested in a series of tutorials covering a) HTML5 gaming b) making an RPG?  Again, this would be in addition to the regular tutorials/topics I cover.


So… interested?


29. March 2012


If you frequent developer related forums, this question probably caused your eyes to roll and the bile to rise in your throat.  People with absolutely no development experience get the game creation bug and of all things want to start with MMOs.  Amazingly enough, there is now a very simple answer…  start here.





See, the folks over at Mozilla decided to create an HTML5 MMO to showcase the use of Web Sockets a technology for easy two way communication between a webpage and server ( that isn’t without it’s controversy ).  Additionally it makes use of HTML features including Canvas, local storage, web workers and audio.  Node is used on the server side.  Here is the game in action on my desktop:






It is graphically simple, but when I played there were 73 other players online and it played fairly flawlessly.  I also fired it up on my Android Phone ( Samsung Galaxy Note ) and here are the results:









And… failure.  It never leaves the connecting to server screen.  Perhaps it’s just me, but for now it simply doesn’t work on my phone.  Will try later on my ICS tablet and iPhone, it may just be launch day jitters so I will try again later.




Failures aside, why am I talking about a primitive browser based MMO on this site?  Well, that’s because Mozilla made the complete source code available on GitHub!



Here for example is the code from character.js for defining the players character.  As you can see, once you get over the Javascriptisms, it’s quite clean and easy to follow:


define(['entity', 'transition', 'timer'], function(Entity, Transition, Timer) { var Character = Entity.extend({ init: function(id, kind) { var self = this; this._super(id, kind); // Position and orientation this.nextGridX = -1; this.nextGridY = -1; this.orientation = Types.Orientations.DOWN; // Speeds this.atkSpeed = 50; this.moveSpeed = 120; this.walkSpeed = 100; this.idleSpeed = 450; this.setAttackRate(800); // Pathing this.movement = new Transition(); this.path = null; this.newDestination = null; this.adjacentTiles = {}; // Combat = null; this.unconfirmedTarget = null; this.attackers = {}; // Health this.hitPoints = 0; this.maxHitPoints = 0; // Modes this.isDead = false; this.attackingMode = false; this.followingMode = false; }, clean: function() { this.forEachAttacker(function(attacker) { attacker.disengage(); attacker.idle(); }); }, setMaxHitPoints: function(hp) { this.maxHitPoints = hp; this.hitPoints = hp; }, setDefaultAnimation: function() { this.idle(); }, hasWeapon: function() { return false; }, hasShadow: function() { return true; }, animate: function(animation, speed, count, onEndCount) { var oriented = ['atk', 'walk', 'idle']; o = this.orientation; if(!(this.currentAnimation && === "death")) { // don't change animation if the character is dying this.flipSpriteX = false; this.flipSpriteY = false; if(_.indexOf(oriented, animation) >= 0) { animation += "_" + (o === Types.Orientations.LEFT ? "right" : Types.getOrientationAsString(o)); this.flipSpriteX = (this.orientation === Types.Orientations.LEFT) ? true : false; } this.setAnimation(animation, speed, count, onEndCount); } }, turnTo: function(orientation) { this.orientation = orientation; this.idle(); }, setOrientation: function(orientation) { if(orientation) { this.orientation = orientation; } }, idle: function(orientation) { this.setOrientation(orientation); this.animate("idle", this.idleSpeed); }, hit: function(orientation) { this.setOrientation(orientation); this.animate("atk", this.atkSpeed, 1); }, walk: function(orientation) { this.setOrientation(orientation); this.animate("walk", this.walkSpeed); }, moveTo_: function(x, y, callback) { this.destination = { gridX: x, gridY: y }; this.adjacentTiles = {}; if(this.isMoving()) { this.continueTo(x, y); } else { var path = this.requestPathfindingTo(x, y); this.followPath(path); } }, requestPathfindingTo: function(x, y) { if(this.request_path_callback) { return this.request_path_callback(x, y); } else { log.error( + " couldn't request pathfinding to "+x+", "+y); return []; } }, onRequestPath: function(callback) { this.request_path_callback = callback; }, onStartPathing: function(callback) { this.start_pathing_callback = callback; }, onStopPathing: function(callback) { this.stop_pathing_callback = callback; }, followPath: function(path) { if(path.length > 1) { // Length of 1 means the player has clicked on himself this.path = path; this.step = 0; if(this.followingMode) { // following a character path.pop(); } if(this.start_pathing_callback) { this.start_pathing_callback(path); } this.nextStep(); } }, continueTo: function(x, y) { this.newDestination = { x: x, y: y }; }, updateMovement: function() { var p = this.path, i = this.step; if(p[i][0] < p[i-1][0]) { this.walk(Types.Orientations.LEFT); } if(p[i][0] > p[i-1][0]) { this.walk(Types.Orientations.RIGHT); } if(p[i][1] < p[i-1][1]) { this.walk(Types.Orientations.UP); } if(p[i][1] > p[i-1][1]) { this.walk(Types.Orientations.DOWN); } }, updatePositionOnGrid: function() { this.setGridPosition(this.path[this.step][0], this.path[this.step][1]); }, nextStep: function() { var stop = false, x, y, path; if(this.isMoving()) { if(this.before_step_callback) { this.before_step_callback(); } this.updatePositionOnGrid(); this.checkAggro(); if(this.interrupted) { // if Character.stop() has been called stop = true; this.interrupted = false; } else { if(this.hasNextStep()) { this.nextGridX = this.path[this.step+1][0]; this.nextGridY = this.path[this.step+1][1]; } if(this.step_callback) { this.step_callback(); } if(this.hasChangedItsPath()) { x = this.newDestination.x; y = this.newDestination.y; path = this.requestPathfindingTo(x, y); this.newDestination = null; if(path.length < 2) { stop = true; } else { this.followPath(path); } } else if(this.hasNextStep()) { this.step += 1; this.updateMovement(); } else { stop = true; } } if(stop) { // Path is complete or has been interrupted this.path = null; this.idle(); if(this.stop_pathing_callback) { this.stop_pathing_callback(this.gridX, this.gridY); } } } }, onBeforeStep: function(callback) { this.before_step_callback = callback; }, onStep: function(callback) { this.step_callback = callback; }, isMoving: function() { return !(this.path === null); }, hasNextStep: function() { return (this.path.length - 1 > this.step); }, hasChangedItsPath: function() { return !(this.newDestination === null); }, isNear: function(character, distance) { var dx, dy, near = false; dx = Math.abs(this.gridX - character.gridX); dy = Math.abs(this.gridY - character.gridY); if(dx <= distance && dy <= distance) { near = true; } return near; }, onAggro: function(callback) { this.aggro_callback = callback; }, onCheckAggro: function(callback) { this.checkaggro_callback = callback; }, checkAggro: function() { if(this.checkaggro_callback) { this.checkaggro_callback(); } }, aggro: function(character) { if(this.aggro_callback) { this.aggro_callback(character); } }, onDeath: function(callback) { this.death_callback = callback; }, /** * Changes the character's orientation so that it is facing its target. */ lookAtTarget: function() { if( { this.turnTo(this.getOrientationTo(; } }, /** * */ go: function(x, y) { if(this.isAttacking()) { this.disengage(); } else if(this.followingMode) { this.followingMode = false; = null; } this.moveTo_(x, y); }, /** * Makes the character follow another one. */ follow: function(entity) { if(entity) { this.followingMode = true; this.moveTo_(entity.gridX, entity.gridY); } }, /** * Stops a moving character. */ stop: function() { if(this.isMoving()) { this.interrupted = true; } }, /** * Makes the character attack another character. Same as Character.follow but with an auto-attacking behavior. * @see Character.follow */ engage: function(character) { this.attackingMode = true; this.setTarget(character); this.follow(character); }, disengage: function() { this.attackingMode = false; this.followingMode = false; this.removeTarget(); }, /** * Returns true if the character is currently attacking. */ isAttacking: function() { return this.attackingMode; }, /** * Gets the right orientation to face a target character from the current position. * Note: * In order to work properly, this method should be used in the following * situation : * S * S T S * S * (where S is self, T is target character) * * @param {Character} character The character to face. * @returns {String} The orientation. */ getOrientationTo: function(character) { if(this.gridX < character.gridX) { return Types.Orientations.RIGHT; } else if(this.gridX > character.gridX) { return Types.Orientations.LEFT; } else if(this.gridY > character.gridY) { return Types.Orientations.UP; } else { return Types.Orientations.DOWN; } }, /** * Returns true if this character is currently attacked by a given character. * @param {Character} character The attacking character. * @returns {Boolean} Whether this is an attacker of this character. */ isAttackedBy: function(character) { return ( in this.attackers); }, /** * Registers a character as a current attacker of this one. * @param {Character} character The attacking character. */ addAttacker: function(character) { if(!this.isAttackedBy(character)) { this.attackers[] = character; } else { log.error( + " is already attacked by " +; } }, /** * Unregisters a character as a current attacker of this one. * @param {Character} character The attacking character. */ removeAttacker: function(character) { if(this.isAttackedBy(character)) { delete this.attackers[]; } else { log.error( + " is not attacked by " +; } }, /** * Loops through all the characters currently attacking this one. * @param {Function} callback Function which must accept one character argument. */ forEachAttacker: function(callback) { _.each(this.attackers, function(attacker) { callback(attacker); }); }, /** * Sets this character's attack target. It can only have one target at any time. * @param {Character} character The target character. */ setTarget: function(character) { if( !== character) { // If it's not already set as the target if(this.hasTarget()) { this.removeTarget(); // Cleanly remove the previous one } this.unconfirmedTarget = null; = character; } else { log.debug( + " is already the target of " +; } }, /** * Removes the current attack target. */ removeTarget: function() { var self = this; if( { if( instanceof Character) {; } = null; } }, /** * Returns true if this character has a current attack target. * @returns {Boolean} Whether this character has a target. */ hasTarget: function() { return !( === null); }, /** * Marks this character as waiting to attack a target. * By sending an "attack" message, the server will later confirm (or not) * that this character is allowed to acquire this target. * * @param {Character} character The target character */ waitToAttack: function(character) { this.unconfirmedTarget = character; }, /** * Returns true if this character is currently waiting to attack the target character. * @param {Character} character The target character. * @returns {Boolean} Whether this character is waiting to attack. */ isWaitingToAttack: function(character) { return (this.unconfirmedTarget === character); }, /** * */ canAttack: function(time) { if(this.canReachTarget() && this.attackCooldown.isOver(time)) { return true; } return false; }, canReachTarget: function() { if(this.hasTarget() && this.isAdjacentNonDiagonal( { return true; } return false; }, /** * */ die: function() { this.removeTarget(); this.isDead = true; if(this.death_callback) { this.death_callback(); } }, onHasMoved: function(callback) { this.hasmoved_callback = callback; }, hasMoved: function() { this.setDirty(); if(this.hasmoved_callback) { this.hasmoved_callback(this); } }, hurt: function() { var self = this; this.stopHurting(); this.sprite = this.hurtSprite; this.hurting = setTimeout(this.stopHurting.bind(this), 75); }, stopHurting: function() { this.sprite = this.normalSprite; clearTimeout(this.hurting); }, setAttackRate: function(rate) { this.attackCooldown = new Timer(rate); } }); return Character; });




So now, the next time somebody asks you “I want to create an MMO, now what?”, you have an easy answer.  Send them over to Browser Quest.



Oh, and for the record… starting your game development career off with an MMO is still a right stupid idea! Smile



EDIT: As per a comment on reddit, this code doesn’t appear to be released under a specific license yet.  Keep that in mind before using any of this code in your own project.  Of course, this should be rectified shortly.


GFS On YouTube

See More Tutorials on!

Month List