Jump to content
This forum will be closing down. Please move to the respective dedicated project forums.

How to organize your Phaser Game even better


StrykerKKD
 Share

Recommended Posts

I read http://toastedware.com/?p=258 article about code organizing and i was not satisfied. Although it's a great read, but this kind of code structure has shortcomings.

These are:

  1. File structure is too simple
  2. Almost everything is public (bad encapsulation)
  3. Using prototypes when you should not
  4. Classes have hidden dependencies
  5. It's not optimalized  

The first thing i did was making a basic directory structure for the project. I separated the Javascript files based on their role in the game. This solved our first issue.

 

After that i used require.js: http://requirejs.org/ to make the encapsulation a lot better. Require.js is a Javascript file and module loader.

What does require.js offer ?

  • You will only have one script tag in your html file
  • Your game will have only one entry point
  • Separate your code into modules
  • You can separate your code into private and public sections
  • With module pattern you do not need to use prototype 
  • In your module you can use other modules(like importing classes)
  • Yo can clearly see which module you are depend on
  • You can optimize(uglify) your code

 Require.js solved the 3,,4. and 5. problems.(And helped a little with the 2. issue).

To pass the game object to the modules i made an init() function.

 

To solve the second  problem i had to remove every unnecessary public variable and function.

I made the variables private and rename them. I only had to make one function private.

//public variablethis.variable = null;//private variablevar _variable = null;//private functionvar _myFunction = function() {}

Because of it, i had to make new public functions to access some value(platforms,stars) or to make an action(update screen text).

 

With this, i made the encapsulation, readability and maintainability better.

 

The only real problem that i met was the gravity value in Phaser 1.1.6, so i had to change the gravity values. (Tutorial used 1.1.3)

 

You can find the source code here: https://github.com/StrykerKKD/FirstPhaserGame

index.html shows require.js before optimization and indexOpt.html shows after.

You can use built.js to build you own optimized code.

 

I used Brackets's and Webstorm's live preview  to test out the game.

 

tl;dr Use private variables/functions whenever you can and use require.js

 

Link to comment
Share on other sites

Nice contribution

 

But as we code html5 games, I think it's important to keep in mind some specific concerns too :

- it's important to minimize http request number : a big lone javascript file could be better  than many small ones (grunt or other tool can contatenate and minify js files)

- it's important to focus not only on readability but also on performance at execution : strong encapsulation could bring to have many local variables which could cause many (unpredictable) GC calls

 

here are jcs advices :

http://www.html5gamedevs.com/topic/2020-remove-element-from-group/?p=15803

Link to comment
Share on other sites

@jerome

Nice to know about this behavior of the GC, but I'm defining every private variable outside of the update loop.

Global variables should be used when creating something in a game loop. hmmm

For example:

function doSomething(){  globalNameSpace.value = "Cat";    //do not do this  var value = "Cat";}function update() {  doSomething();}

But there is always a workaround:

//inside a module or Phaser, so _variable is not globalvar _variable = null;function doSomething(variable) {  variable = "Cat";}function update() {  doSomething(_variable);}
Link to comment
Share on other sites

Nice work  ;)

Yep, we can't avoid sometimes to declare local temporary variables...

The point is to limit their use and to keep in mind the GC could happen whenever it "decides" and last as long as it needs according to the running JS VM and the relevant platform. The game would then suffer from lags or slowness from the user perspective.

 

Generally, having persistent objects thru the game life cycle (even outside the game loop) and pooling them if needed is a good way to limit GC interruptions in the js game dev literature.

 

I think your approach seems really good so far, although I'm not an expert. Rich or jcs could would say it better than me.

Link to comment
Share on other sites

re: local temp vars - the creation of them isn't as terrible as you may be lead to believe. If they are consistent in their types and what they hold then most modern JS engines will build up an internal 'ghost' class for them anyway and optimise for their known life cycle. The issue more is creating empty stores like blank objects/arrays and populating them with variable amounts of data, or mixed-type data, the kind of stuff that a compiler can never optimise for. It's not quite as black/white as "don't use X, use Y", it's easy to invalidate both routes.

Link to comment
Share on other sites

agreed. as long as you follow some simple 'best practices' (which you should anyway) like not mutating objects, then the code generated will be good. the problem with temporary objects is the GC - if you're allocating objects in your game loop eventually the GC will have to run. when the GC runs it takes ms away from your game, which can be enough to cause your frame-rate to 'stutter', which can look worse than just having a lower frame-rate.

 

but temporaries that are simple, atomic types (and aren't mutated) will be optimized into (the javascript equivalent of) stack variables by modern JIT compilers and won't incur GC overhead, so there's very little reason to try to "optimize away" every last local variable - you'll just end up with un-readable code.

 

I agree with the sentiment about the prototypical object model as well - and the JIT compilers do a great job with it too. (if you look on jsperf.com you'll find lots of examples of different tests comparing prototypes to other patterns and prototypes do very well in all the modern compilers).

 

I've little to say about the rest of this thread - personally I feel that require.js etc are over-engineering for the size of most of the javascript projects that I work on and I just cook my own very simple module-like system - but, each to his/her own (!), and it's great to see different alternatives put out there for people to learn about!

Link to comment
Share on other sites

Thank you both of you, rich and jcs, to have taken some time to give your precious advices.

Ok for focusing more on immutability than on temporary variables regarding GC behavior.

 

I personaly agree too about the fact to trust the core philosophy of each language and so to rely on prototype for JS rather than opt for another type of more exotic pattern (as I wouldn't opt for functional dev in Ruby, etc). But it's only the opinion of an old programmer and sysadmin aaarff ... and I understand everyone may prefer his own vision.

 

Thank you, you all who wrote in this thread for your contributions and advices.

Link to comment
Share on other sites

//inside a module or Phaser, so _variable is not globalvar _variable = null;function doSomething(variable) {  variable = "Cat";}function update() {  doSomething(_variable);}

Actually this will never work(my bad), because an object are not passed by reference. You can modify the objects properties, but you can't modify the object itself.

http://stackoverflow.com/questions/7744611/pass-variables-by-reference-in-javascript

Link to comment
Share on other sites

(you can work around this by passing javascript objects instead of simple scalar types, or by putting 'variable' inside of a javascript object. e.g. 

var o = { 0: variable };doSomething( o );

or 

var a = [variable];doSomething( a );

with doSomething defined like:

function doSomething( o ){  o[0] = 'cat';  // or whatever}

it's not the most elegant solution in the world, but it will get the job done)

Link to comment
Share on other sites

I imagine that they all preserve variable type immutability for the simple reason that not doing so would actually be more difficult than doing so. the minifier need to analyze the code to know that a particular variable was no longer in use and safe to re-use. closure compiler does some of this kind of analysis - and yui's minifier might as well- but simpler minifiers don't.

 

while closure compiler does some deeper analysis, the fact that it comes out of google where they are very aware of the JITters strengths and weaknesses would indicate to me that it wouldn't do any tricks like trying to re-use variable names in the same context.

 

plus, there would be very limited usefulness in re-using variable names like this. it isn't likely to be beneficial in any case I can think of off-hand, and if the minifier got it wrong the generated code would be completely broken...

Link to comment
Share on other sites

  • 3 months later...

Pretty cool guide for those who like to code in this way.

 

Personally I've never had an issue with prototypal inheritance (even via a constructor pattern), after all that it is what JavaScript is at its heart. But I know some devs like to try and strict things up too.

 

I like to clarify that using require.js doesn't lead you away from prototypal inheritance. Sry, if you didn't meant that it would.

 

I also checked github examples and it seems that everyone does things differently :)

 

Hence, I usually define e.g. player.js with style below (I use entity.js as example of inheritance - it would contain for example id attribute and such, depends based on the game, not included here.).

 

player.js (this is the subclass of entity)

define(["entity"], function (Entity) {  "use strict";  /**   *   *   */  var Player = function (opts) {    Entity.call(this, opts);    this.x = opts.x || 0;    this.y = opts.y || 0;    this.nickname = opts.nickname || "unnamed warrior";  };  Player.prototype = Object.create(Entity.prototype);  /**   *   *   */  Player.prototype.init = function () {     // do stuff  };  /**   *   *   */  Player.prototype.getX = function () {     return this.x;  };  /**   *   *   */  Player.prototype.setX = function (x) {     this.x = x;  };  /**   *   *   */  Player.prototype.getY = function () {     return this.y;  };  /**   *   *   */  Player.prototype.setY = function (y) {     this.y = y;  };  /**   *   *   */  Player.prototype.getNickname = function () {     return this.nickname;  };  /**   *   *   */  Player.prototype.setNickname = function (nickname) {     this.nickname = nickname;  };  return Player;});

I like to define setters & getters for class attributes which is accessible; rather than doing: somePlayer.x = 1; but this is just me.

 

If I want to use this module somewhere in my code.... I would do this:

 

game.js

define(["player"], function (Player) {  "use strict";  /**   *   *   */  var Game = function (opts) {};   /**   *   *   */  Game.prototype.init = function(){          // Example code below..        var player1 = new Player({        x: 1,         y: 1,         nickname: "Super nickname"    });        // This works as well, creates player with x: 0, y: 0, nickname: "unnamed warrior"    var player2 = new Player({});  };  return Game;});

I think that the interesting part of this code is the fact that player (and entity which is not shown) takes opts as parameter which is always an javascript object.

 

Therefore, when you initialize Player you can give parameters to it with any order you want and it just works:

var player1 = new Player({    x: 1,     y: 1,     nickname: "Super nickname"});

Obviously, this example didn't use phaser etc. and in reality, the module count gets bigger fast - but you will definitely maintain cleaner code, everything is nicely organized.

 

In my last project I had only one phaser GameState and start of the class looked like this:

define(["phaser", "jquery", "socketio", "modules/utils", "modules/player",  "modules/hero", "modules/bat", "modules/dragon", "modules/map"],  function (Phaser, $, io, Utils, Player, Hero, Bat, Dragon, Map) {  "use strict";  /**   * Actual game (main)   *   */  var GameState = function(game) {    // socket.io    this.socket = undefined;    // Player    this.player = undefined;    ..... etc etc .....

However, without using require.js the full code would have been complete madness. Thats why I recommend organizing your code  :D

Link to comment
Share on other sites

 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...