Subscribe to GameFromScratch on YouTube Support GameFromScratch on Patreon

20. November 2014

 

Not sure how much use this will be to anyone, but I thought I would share it all the same…  I recently found myself working with Phaser without the comforts of my friend TypeScript.  One of the great things TypeScript brings to the table is a built in package system, something Javascript is sorely lacking ( at least until ECMAScript 6 arrives ).  Of course, you don’t need a modular package system, but it certainly makes things cleaner, especially as project scope increases.  

 

In the land of module loaders, there are two commonly used front runners.  RequireJS and Browserify.  I believe RequireJS is by far the more popular option, so that’s the route I pursued.  That Phaser already shipped with a RequireJS template was certainly an added bonus!

 

So, let’s take a look at how you use that template.  First you have to get it… I didn’t make the obvious git pun, aren’t you proud?

 

Anyways, as the line above might hint, the template is available in the Phaser Git repository.  That said, pulling the entire repository just to get a single directory is stupidly overkilled.  Unfortunately, it’s all the only option you’ve got with Git.  Sadly there is no ability to download a single folder with git or using the Github web interface.  However, Github is compatible with SVN, and this is one area where SVN shines compared to git.

 

The template we want is in the directory https://github.com/photonstorm/phaser/tree/master/resources/Project%20Templates/RequireJS.  Using SVN ( assuming you have it installed, if you have a Mac with Xcode installed, you should have SVN, if you are on Windows it’s available in cygwin ), you can download just this folder using the command:

 

 

This will download the contents of that folder on github to the local directory ./RequireJS, which it will create.  ( In fact, the command will fail if it already exists ).

 

Now that we have the template, what exactly do we do with it?

 

Well the first thing we need to do is resolve the dependencies.  That is, download all the libraries that it depends on.  This particular template uses the bower package manager.  To be honest, it’s rather overkill, as this package only has two dependencies… requireJS and Phaser, but oh well, it’s always useful to learn and becomes more useful as your project increases in complexity ( kinda like RequireJS actually ), so let’s look at how we use Bower.

 

First you need to install it, and in order to do that, you need to have NodeJS installed.  Node is getting to be the underlying technology for many programming tools, so you should probably have it installed at this point anyways.  To install Node either download and run the installer or use for favourite package manager.  Ironic this, using a package manager to install a package manager to install a different package manager….  Oh, you also need to have git installed.  Again on Mac OS git is installed with Xcode ( or possibly even on it’s own, not certain ) and you can get it on Windows in cygwin or using the installer.

 

Ok, now that you’ve got now installed, you need to install Bower.  To install it globally ( system wide ), simply open a console/terminal window and type

npm install -g bower

 

Now, in your terminal window, change to the directory you installed the template ( ./RequireJS in my case ) and run:

bower install
 
This will read the local file bower.json, and go and download the specified libraries.  This should be the end of it.
 
Unfortunately right now, it isn’t as there is a small bug in the RequireJS template that needs to be fixed first.  Thankfully it’s pretty simple.
 
Open the file /src/main.js and locate the paths section and change it to:
 
        paths: {
        	phaser:   'libs/phaser/build/phaser.min'
        },

The key changes are the addition of /build/ to the path and the removal of the extra comma at the end of the line.  Now if you run “bower install”, it will go out and resolve all (two!) of your dependencies… yeah, like I said, kinda overkill at the moment.

 

Let’s take a quick look at the project you are left with:

Proj

 

 

It’s a nice clean expandable layout to build a game around.  The assets folder is where, predictably enough, you put your assets.  src/libs is where bower resolved libraries are download and should generally be safely ignored.  Bower.json is the configuration file that tells Bower what libraries your project depends on, as well as a bit of metadata about your project.  It’s a standard JSON structure that looks like:

 bower.json

{
  "name": "Phaser-RequireJS",
  "version": "0.1.0",
  "description": "Phaser Hello World with RequireJS",

  "authors": [
    "mike <mike@gamefromscratch.com>"
  ],

  "license": "MIT",

  "dependencies": {
    "requirejs": "latest",
    "phaser": "latest"
  },

  "ignore": [
    "src/libs"
  ]
}

 

Next up is our index.html which is a boot loader of sorts.

index.html

<!doctype html>
<html>
    <head>
        <meta charset="UTF-8" />
        <title>hello phaser-requirejs</title>
        <script data-main="src/main" src="src/libs/requirejs/require.js"></script>
    </head>
    <body>
    </body>
</html>

 

This file is pretty much just kicking off the require.js script.  That parameter data-main is important, as it tells require.js the entry point of our application.  Lets look at it next.

main.js

(function () {
    'use strict';

    requirejs.config({
        baseUrl: "src/",
        
        paths: {
        	phaser:   'libs/phaser/build/phaser.min'
        },

        shim: {
        	'phaser': {
        		exports: 'Phaser'
        	}
        }
    });
 
    require(['phaser', 'game'], function (Phaser, Game) {
		var game = new Game();
		game.start();
    });
}());

 

This file is scarier looking, but generally not something you have to touch, except when you add new libraries.  Basically you are calling the config() method of requirejs pathing to the phaser library.  require then makes a closure that creates a new instance of Game and calls game.start().

Now the nice part about using a template… you don’t have to write this ugly code, someones already done it for you!  The only time you need to edit this file is when you add new external dependencies.  

Finally, let’s look at game.js, which is basically the starting point of your new game:

game.js

define([
    'phaser'
], function (Phaser) {
    'use strict';

    function Game() {
        console.log('Making the Game');
    }
    
    Game.prototype = {
    	constructor: Game,

        start: function() {
            this.game = new Phaser.Game(800, 600, Phaser.AUTO, '', {
                preload: this.preload,
                create: this.create
            });
        },

        preload: function() {
            this.game.load.image('logo', 'assets/phaser.png');
        },
		
        create: function() {
            var logo = this.game.add.sprite(this.game.world.centerX, this.game.world.centerY, 'logo');
            logo.anchor.setTo(0.5, 0.5);
        }
    };
    
    return Game;
});

 

This looks pretty much like a standard Phaser application.  The only difference is that again it’s wrapped in a closure with a dependency on Phaser.

 

You can read more about defining modules in Require here.

 

I will admit this seems like massive overkill.  It’s quite possible that it is actually, I’ve yet to decide.  The require module system adds some ugliness to the whole process, all module systems do ( and frankly this is why Typescript’s import system is so nice ).

 

For a small project with few dependencies, this is certainly all overkill.   However, as you add more and more dependencies and your code becomes more and more complex, taking a modular approach to managing complexity pays greater and greater rewards.

 

In the end though, is it worth it?   I’m not entirely sure.  Frankly if I had a complex enough application to justify all of this, I’d just use Typescript personally.

Programming

20. November 2014

 

The JavaScript game library MelonJS reached the milestone 2.0.0 release yesterday.

Melonlogo

 

The key features of this release are:

 

  • Shape-based collision layer has replaced the tile-based collision layer.
  • WebGL support has landed! (Currently alpha-quality)
  • Many fixes and improvements with collision detection in isometric maps.
  • Physics bodies now support multiple shapes.
  • Automatic collision response handling is enabled by default, and fully customizable.
  • Support for most of the new features in Tiled 0.10, like :
  • Shape scaling and rotation (especially in TMX maps,)
  • TMX Tileset animations

 

You can read more about the release here.

 

You can read the change log here.

 

You can download MelonJS here.

 

You can view the source here.

 

You can see a rabbit with a waffle on it’s head here.

 

WebGL support is a nice add, as with it’s recent adoption in both Safari and Internet Explorer, it is certainly the future of HTML5 gaming.  While it’s popular I have never actually taken more than a cursory look at MelonJS, I really should jump in a bit deeper one of those days.

News

18. November 2014

 

In this video tutorial, we create our first LibGDX project.  This tutorial covers that basic layout of a LibGDX application, adding new assets to a project and looks at how the initial project is composed and how multiple platforms are supported.  We then go through the initial code, then look in depth at coordinate systems, then move on to creating a sprite.  Finally we look at positioning, scaling and rotating a sprite with a SpriteBatch.

 

This tutorial assumes you already have your development environment setup and know how to run a LibGDX application in your chosen IDE.  If you do no, please watch these videos first.

 

The following is the video tutorial.  It was recorded in 1080p and the complete version can be viewed here.

 

 

The following is the final code generated:

 

package com.gamefromscratch;

import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;

public class HelloWorld extends ApplicationAdapter {
   SpriteBatch batch;
   Texture img;
   Sprite sprite;
   
   @Override
   public void create () {
      batch = new SpriteBatch();
      img = new Texture("HelloWorld.png");
      sprite = new Sprite(img);
      sprite.setPosition(
            Gdx.graphics.getWidth()/2 - sprite.getWidth()/2,
            Gdx.graphics.getHeight()/2 - sprite.getHeight()/2);
      sprite.setScale(1.0f,2.0f);
   }

   @Override
   public void render () {
      Gdx.gl.glClearColor(0, 0, 0, 1);
      Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
      batch.begin();
      batch.draw(sprite, sprite.getX(),sprite.getY(),sprite.getWidth()/2,
            sprite.getHeight()/2,sprite.getWidth(),
            sprite.getHeight(),sprite.getScaleX(),
            sprite.getScaleY(),sprite.getRotation());
      batch.end();
   }
}

 

Here is the image that was used in this tutorial:

HelloWorld

Programming , , ,

17. November 2014

 

As you may be able to tell from some of my recent posts I’ve been working more and more often on my iPad when it comes to creating pixel graphics.  When working on my PC or Mac I have my trusty Wacom Intuos tablet, but on iPad its obviously a much different beast.  First off, unlike say the Surface Pro, the iPad screen simply isn’t made for working with a tablet, so most of the tablets that are on the market are basically glorified fake fingers.  This however is slowly changing as we will see shortly.

 

When it comes to choosing an iPad stylus you are given an amazing variety of options, but actually remarkably little in terms of difference.  Essentially what I am saying is, although there are literally hundreds of stylus available, other than ascetics, they are basically 99% identical.  Let’s look at the various types available.

 

The Fake Finger

 

As I said earlier, most stylus on the market are basically just a fake fingertip on a stick.  When you go to your local Best Buy, 95% of tablets fit this description.  Here is my trusty virtual finger:

s1

 

This is a generic Targus stylus, which can be had on Amazon for about $10.  I picked this one because I found it attractive and comfortable to hold.  If you are looking at a stylus and its $20 or under, all of the following probably applies to it.  They will do the deed, but your contact can be best described as… mushy.  Here is the stylus in action:

s2

 

Let’s just say, high precision work isn’t going to be fun with these.  Basically its about the same as using your finger tip, just without the rest of your hand to get in the way.

 

The Good

  • Cheap
  • More pen like than using your finger
  • Works on any device with any application

The Bad

  • low precision
  • resistance when dragging on screen can be awful
  • palm on screen gives applications fits.

 

 

Personally, I wanted a stylus with a bit more precision, which lead me…

 

The More Precise Finger

 

When you want to do highly detailed and sharp lines, a standard stylus is not a great option.  The contact surface is simply too fat and you have to spend a lot of time extremely zoomed in to pull it off.  Fortunately there is an option and it works incredibly well.

The Adonit Jot.  The Jot can be had for about $30, here’s mine:

s3

 

As you can see, the tip is actually a clear flat disk on a pivot.  As I said earlier, it’s fragile, so you really want to make sure you put the cap on it when not using it.  It’s also magnetized, so it can stick to the side of your device.  I personally find it annoying as hell because it means I have to pull all the change of it before I can use it, and I’m not exaggerating either, the magnet on this thing is stupidly strong…

 

Here is a typical scene when I remove it from my pocket:

5

There is a nice rubberized grip, but the key feature is that tip, here it is in action:

s4

It really does enable you to create very highly precise lines and the friction between the tip and screen is somewhat similar to paper and pencil.  The palm detection will get infuriating though, although I have found a very simple work around Ill share later.  I also have found at certain angles it stops tracking occasionally.

 

The Good

  • Reasonably inexpensive
  • Works on any device with any application
  • High Precision
  • Comfortable

 

The Bad

  • highly fragile tip, always use the cap when not in use!
  • 2 – 3x more money than a typical stylus
  • doesn’t track right at about a 45 degree angle
  • palm on screen causes fits

 

For high precision drawing, this is hands down my favorite.  The difference between using one of these and a standard stylus is night and day.  However sometimes the LAST thing you want is to be super precise.  If for example you are painting, you’d rather have that painterly feel instead.  Well, there is a stylus for you too!

 

The Paint Brush

 

Next up is Nomad Paintbrush Stylus, which retails for about $40.  Here’s mine:

s5

The key feature here is the paint brush style tip:

s6

 

It really feels like a paint brush when you use it.  It’s an impressive trick they have pulled off and it allows you to achieve effects, like feathering and flicking, that you simply can’t with other stylus.  That said, this is useful for painting and that is absolutely it.  Precision is almost impossible ( that’s kinda the point ), and even selecting buttons and such can be a bit irritating.

 

The Good

  • Effectively mimics a paint brush
  • It’s cool, admit it, you find it cool.  It looks cool, it feels cool, so far as stylus go, this one is pretty freaking cool.
  • Most “natural” feel all of stylus used, glides nicely on surface
  • Works on any device

 

The Bad

  • low precision, its by design of course, but it can get annoying
  • getting pretty pricey at $40
  • palm gets in the way

 

 

The Next Generation

 

The future is here and two new stylus are leading the pack!

 

The first is from Wacom, the Intuos Creative Stylus 2 for $80 and the Adoit Jot Touch for $100.  These two products are very similar in strengths and weaknesses.

 

The Wacom Intuos:

s8

 

The Adoit Touch with PixelPoint:

s9

 

Both of these offer 2048 levels of sensitivity, something unique to them in this roundup.  Both are Bluetooth powered.  Both have small “nubs” and both have palm detection software to prevent the annoyances that all of the above offer.

 

Neither is ready for prime time either, which is infuriating, as I so wanted to buy both of them!  That’s my inner gadget addiction talking, and that is a very strong addiction indeed.

 

So, what’s wrong?  It’s two fold, software support and execution.

 

The biggest problem is, in order to take advantage of what these stylus have to offer, it needs to be supported in the application.  Sadly each has only about half a dozen applications that support them.  When an application does support them, the reviews seem to be mixed.  I read review after review for both products trying to decide which one to purchase and in the end decided neither appears ready for prime time.  Too many applications I use ( ProCreate, iDraw, OneNote ) simply aren’t supported yet.  Many reviews state that palm detection didn’t work, or spoke of constant disconnects.

 

Make no mistake, Bluetooth stylus are the future, it’s just not quite the future yet.  Apparently when you use the right application and the device works properly, the experience is amazing.  I hope they get the kinks worked out quickly.

 

Palm Detection, Ghetto-Style

 

One common problem with all but the newest stylus is palm detection.  When you draw with pen and paper, the worst that happens if your palm touches the screen is you might smudge your work slightly.  On a tablet however, the outcome is quite different.  Either your drawing is disrupted completely, or worse, you draw where your palm touches.

 

The work around?

s10

 

I decided to try drawing wearing my workout gloves.  Standard weight lifting gloves have a leather pad across the palm, and at least on an iPad Air, it completely prevented my palm from registering.  Of course, you do have to get used to drawing with a pair of gloves on, but really it becomes natural almost instantly.  A pair of cycling gloves should probably do the trick, you just want to make sure it’s thick at the point the palm makes contact.

Art ,

16. November 2014

 

 

First off, let's start of with a big disclaimer!  This is more of an experiment that worked than a tutorial, as I can’t actually recommend you do anything I just did!  Let’s just say, I did a few really hack worthy things to get this particular example to work.  That said, it does work and the results are cool enough I figured I’d share them.  So, there is the warning…  all the code in this example works, but it might be a really really really bad idea! ;)

 

Ok, disclaimer covered…  now on to what we are actually going to cover, using a 3D model in Phaser.  Out of the box Phaser doesn’t actually have any support for 3D, but Three.js certainly does.  So what we are going to do is use Three.js to load a model and render it to a canvas, which then can be used as the image source for a Phaser Sprite.  If you’ve got no prior experience with Three.js I previously did a two part ( part one, part two ) series on Three.js, from which this a lot of this code was copied.

 

So let’s jump right in with the code.  It’s heavily commented, but doesn’t really explain any Three.js or Phaser specifics, see earlier tutorials for that.  We are going to render a threejs scene to a canvas, and then use that canvas as a sprite.  In this example, the Threejs scene will be visible, while in real life you would set it to invisible.

 

Index.html

<!DOCTYPE html>

<html lang="en">
<head>
    <meta charset="utf-8" />
    <title>Model rendered in ThreeJS and used in Phaser</title>
    <script src="http://cdnjs.cloudflare.com/ajax/libs/three.js/r67/three.js"></script>
    <script src="phaser.js"></script>
    <script src="Main.js"></script>
</head>
<body>
<h1>Model rendered in ThreeJS and used in Phaser</h1>
<table width="600">
    <tr>
        <th>Canvas rendered in Phaser</th>
        <th>ThreeJS rendered to Canvas</th>
    </tr>
    <tr>
        <td width="300px" align="center"><div style="width:298px;height:298px;" id="content"></div></td>
        <td width="300px" align="center">
            <div id="hiddenContent" style="width:298px;height:298px;visibility:visible;"></div>
        </td>
    </tr>
</table>
</div>
</body>
</html>

 

Main.ts

///<reference path="./three.d.ts"/>
/// <reference path="phaser.d.ts"/>

module Test {

    export class RunningState extends Phaser.State {

        // Create the Threejs variables we need
        renderer:THREE.WebGLRenderer;
        scene:THREE.Scene;
        threeJSCamera:THREE.Camera;
        mesh:THREE.SkinnedMesh;

        // Create the Phaser variables
        sprite:Phaser.Sprite;

        // This flag indicates our model is loaded and ready to be poked and proded
        goTime:boolean = false;

        constructor() {
            super();

            // ThreeJS setup
            this.renderer = new THREE.WebGLRenderer({ alpha: true });

            this.renderer.setSize(256, 256);
            this.renderer.setClearColor(0x0000FF, 1);
            // ThreeJS renders to a (normally) hidden canvas in the hiddenContent div
            // In real life you'd set it's visiblity to false
            document.getElementById('hiddenContent').appendChild(this.renderer.domElement);

            // Create the scene and a camera
            this.scene = new THREE.Scene();
            this.threeJSCamera = new THREE.PerspectiveCamera(75
                , 1
                , 0.1, 1000);

            this.threeJSCamera.position = new THREE.Vector3(0, 0, 1.5);

            // Like framebuffers, the results are upside down once rendered.
            // As I couldn't find a way to flip UVs in PIXI, I instead render the scene upside down
            // by rotating the camera 180 degrees on Z axis
            this.threeJSCamera.rotateZ(Math.PI);
        }

        create() {
            // Set this because we want to display FPS details
            this.game.time.advancedTiming = true;

            // Load the model exported from Blender in threejs format
            // Model loads async
            var modelLoader = new THREE.JSONLoader();
            var that = this;
            modelLoader.load("model/model.jsm", (geometry, materials) => {

                // This is the callback for when our model is loaded, set everything up here
                this.mesh = new THREE.SkinnedMesh(geometry, new THREE.MeshFaceMaterial(
                    materials));

                this.mesh.position.y -=1; // Center vertically about origin

                // add the loaded mesh and a light to the scene
                that.scene.add(this.mesh);
                that.scene.add(new THREE.AmbientLight(new THREE.Color(1.0, 1.0, 1.0).
                    getHex()));

                // Start threejs up
                that.renderThreeJS(null);

                // For some reason, you dont get lighting for a while...
                // This is a gross hack... basically we wait a second and then create a Phaser Sprite
                // and then render the canvas to our sprite.
                setTimeout( () => {
                    that.sprite = this.game.add.sprite(0, 0, null);
                    that.renderThreeJS(that.sprite);
                    // Set goTime flag so we know it's go time baby.
                    that.goTime = true;
                },1000);
            });
        }

        // This is the function that actually call Threejs's render
        // Then we set the Sprite's texture to the canvas
        // Warning, this may be horrible for performance.
        renderThreeJS(target:Phaser.Sprite) {
            if (this.renderer) {
                this.renderer.render(this.scene, this.threeJSCamera);

                if(target) {
                    target.texture.destroy(true);
                       target.setTexture(PIXI.Texture.fromCanvas(
                        <HTMLCanvasElement>document.getElementById("hiddenContent")
                            .children[0], PIXI.scaleModes.DEFAULT));
                }
            }
        }

        // Let's just print out debug framerate
        render(){
            this.game.debug.text("FPS" + this.game.time.fps,0,20);
        }

        // Just so we can see that updates to the ThreeJS scene result in the Sprite being updated
        // We simply rotate by 0.01 radians per frame... note this is running as fast as possible
        // So will rotate differently on different machines...
        update(){
            if(this.goTime) {
                this.mesh.rotateY(0.01);
                this.renderThreeJS(this.sprite);
            }
        }

        start() {
        }
    }

    export class SimpleGame{
        game:Phaser.Game;
        constructor(){
            // Create a game, and our RunningState, passing true to make it start right away
            this.game = new Phaser.Game(256, 256, Phaser.WEBGL, 'content');
            this.game.state.add("RunningState",RunningState,true);
        }
    }
}

// On window load, create an instance of our SimpleGame
window.onload = () => {
    var three = new Test.SimpleGame();
};

 

And the code running:

 

 

Now there are a few horrific hacks in this code.  First off, there is a “warm up” in Threejs, where for a few ms, it renders without lighting… no clue why, so I just brute force by delaying for a second after Threejs rendering starts.  The next brutal hack is to get the Phaser texture to show, I destroy the previous version… there HAS to be a better way to do this.  Interestingly, in Chrome the results to the canvas are flipped.  However, on Safari they are not!  The joy of HTML5.

 

As you can see though, mixing 3D objects in a 2D Phaser game is very much possible and actually fairly easy.

 

 

Programming

Month List

Popular Comments

Goo Create Engine Updated
Subscribe to GameFromScratch on YouTube Support GameFromScratch on Patreon


18. March 2016

 

Goo Create, an HTML5 WebGL game engine with a full editor, just released a new version.

 

New in this release:

  • New particle system component
  • Visual behavior editor
  • Synchronous behavior state transitions
  • Improved action dialog with search
  • Collapsible panels
  • 65K vertex meshes no longer split

Plus several other fixes and improvements:

Improvements

  • The current selection is added to the URL and restored if the page is refreshed.
  • Shadows now work on mobile devices.
  • New entities are now added as children of the currently selected entity. If no entity is selected, new entities become children of the scene.
  • Added a checkbox to the Sound Component panel to specify whether sounds should automatically be played when the scene is played.
  • Added a checkbox to the Sound Component panel to make sounds 2D (not using 3D spatial effects).
  • Added a “Mute” button to the default publishing templates. Clicking it will mute all the sounds in the scene.
  • Added an option for focusing the canvas element in published scenes. When embedding a scene into another website you usually don’t want the canvas to be focused automatically.
  • Added the possibility of specifying alpha values in the color picker control/component. To make use of this new control for script parameters just make a parameter of type “vec4”, and control “color.”
  • Added the possibility of loading Ads immediately after it is loaded or after the whole page is loaded. This option is available in the “Serve Ad” app.
  • Better-looking HTML component default content.
  • The scene title is displayed as the page title and, consequently, shows up in the browser tab.
  • Mesh back-faces now render with correct normals.

Bug fixes

  • Fixed the Tween Opacity actions, QuadComponent and gizmos so that they use TransparencyBlending instead of CustomBlending.
  • Fixed texture repeating so that it works with NPOT textures.
  • Fixed the OrbitCam panning so that it works on touch devices.
  • Removed the restriction of the light shadow casting size and range being integers.
  • Fixed the ButtonScript and PickAndExit action so that they use the “touchend” event to open links, in order to support iOS9.
  • Fix a bug that caused tooltips to say visible after starting to drag a sortable item (e.g. scripts or behavior actions).
  • Made sure the “allowfullscreen” attribute is added to the “Embed code” for published scenes.
  • Added missing ctx.playTime to the autocompletion in the script editor.
  • Fixed a bug that caused the names displayed in the scene Hierarchy would to not overflow properly in Firefox.
  • Made the the text editor search box larger because text would sometimes get clipped in Firefox.
  • Fix a bug in the publishing template that prevented the bottom bar from being hidden unless the “Maximize Button” option was enabled.
  • Fixed a bug which caused the timeline keyframe easing function to not be saved sometimes.

You can read more about the new release here.

GameDev News

blog comments powered by Disqus

Month List

Popular Comments