Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

MVC Bricks for ASP.net

0.00/5 (No votes)
4 May 2011 1  
Learn how to create a simple game using ASP.net MVC, jQuery, State Machine and CSS3 gradients

Table of Contents

Introduction

It's been a long time since I wrote my last article on the ASP.Net subject. This time I decided to write something about it, and more specifically, about ASP.Net MVC.

The MVC approach is, by far, my favorite way of doing things in ASP.Net. The simple fact that you don't have the code behind, nor view state, deserves much of my consideration. That goes without saying that you don't care about post backs.

Also, the application uses the awesome jQuery library to make our lives easier. As the readers will see, I tried to use jQuery wherever I could. This library is really a blessing for those of us developers who need to work on web applications.

System Requirements

To use the MVC Bricks game provided with this article, if you already have Visual Studio 2010, that's fine. If you don't, you can download the following 100% free development tool directly from Microsoft:

  • Visual Web Developer 2010 Express
  • Also, you MUST open it in a CSS3-enabled browser, because this application uses CSS3 Webkit Gradient.

    Splash Screen

    When the game starts, there is a simple jQuery animation, which makes the "MVC BRICKS" title raise up to the middle of the screen. This is accomplished by animating the top css property of the title div:

            function showSplashScreen() {
                $('.subTitle').css('visibility', 'hidden');
                $('.press').css('visibility', 'hidden');
                $('.title').animate({
                    top: 200
                }, 1000, 'swing', function () {
                    // Animation complete.
                    $('.subTitle').css('visibility', 'visible');
                    $('.press').css('visibility', 'visible');
                });
            }
    

    The above code shows that, when the animation ends, the title reaches the middle of the screen and the subtitle is shown.

    Game Controls

    The game is all controlled by the keyboard. When the game is in Intro mode, you can use the space bar to start a new game. When the game is playing, you use the space bar again if you want to pause the game. And then use the space bar again to resume the game. When the game is over, you push the space bar once again to open the game Intro screen.

    Use the left arrow key and right arrow key to move the falling piece to the left or to the right. Press the up arrow key to rotate the piece 90 degrees. Finally, press the down arrow key to speed up the fall of the piece.

    Key Command
    Space bar Start game / Pause game / Resume game / Restart game
    Left arrow key Move falling piece to the left
    Right arrow key Move falling piece to the right
    Down arrow key Speed up falling piece

    The Game Rules

    You must be very familiar with this kind of game, but I have to explain the game rules anyway.

    Here we have an empty 10 x 16 board, containing 160 empty positions. As soon as the game starts, the game engine will randomly generate one new piece at a time, which will fall from the top of the board, falling at the speed of 1 square per second. When the falling piece find an obstacle (that is, part of another piece that is fixed at the bottom of the board) then it can't fall anymore, so that falling piece gets stuck. Then the game engine will produce new random pieces, and they are piled up until the pile reaches the top of the board, and at this moment the game ends. The user will have to control each falling piece, by moving it to the left, to the right, or rotating it, placing the new piece in the lowest possible empty place in the board where the new piece fits in, in a way to avoid the piled pieces to reach the top of the board. Also, when the user fills any the board rows, these rows are cleared, thus giving some extra space and prolonging the game.

    The "I" shape The "L" shape The "J" shape The "O" shape
    The "T" shape The "S" shape The "Z" shape

    The game engine can randomly generate any of the above shapes. As we can see, each shape is associated with a letter which resembles it.

    For each cleared line, the user scores a total of 10 multiplied by the game level. That is, each row cleared in the first level will give 10 points. The second level will give 20 points per cleared row, and so on.

    Each level is completed when the user has cleared 10 rows. That is, to reach the 5th level, the user must have cleared 40 rows.

    When the game is over, the game score is compared with the previous high score, and replaces it if there is a new record.

    The Next piece gives the user the opportunity to place the current piece in a way that makes it easier to accomodate the next falling piece.

    Model View Controller

    The beauty of MVC, as I see it, is its adherence to the principle of separation of concerns. Unlike "classic" (non-MVC) ASP.net, you don't put business logic on your views. Instead, you use the views only for presentation logic (such as parsing and rendering of raw data or input validation), and for presentation itself. On the other side, you reserve the Controllers (or another layer, such as Service layer) for your business rules.

    If you look at the javascript in the application, you won't find the business logic. Instead, you'll find out that it is really thin and light. Fortunately, since I already had the Bricks game logic from previous projects, I was able to maintain my managed code almost intact on the server side, and make the view communicate with it through a new ViewModel I designed specifically for this MVC project.

    The Model

    The model is defined by the BoardViewModel and BrickViewModel classes, and contains all information needed by the View to render the game board, the score board and to know whether the game is over or not. As we can see below, most of the properties of BoardViewModel class are native types, except for the Bricks and Next properties, which are 2-dimensional arrays of the BrickViewModel and hold the data for the bricks and empty spaces that forms the current snapshot of the game board and the bricks corresponding to the Next piece that will fall from the top of the game board.

    The low-level BrickViewModel class has informations about each individual brick: row, column and the color name. These values will be used by the View to find the corresponding divs and update their background color accordingly.

    		public class BrickViewModel
    		{
    			public int Row { get; set; }
    			public int Col { get; set; }
    			public string Color { get; set; }
    		}
    
    		public class BoardViewModel
    		{
    			public BoardViewModel()
    			{
    				IsGameOver = false;
    			}
    
    			public BrickViewModel[] Bricks { get; set; }
    			public int Score { get; set; }
    			public int HiScore { get; set; }
    			public int Lines { get; set; }
    			public int Level { get; set; }
    			public BrickViewModel[] Next { get; set; }
    			public bool IsGameOver { get; set; }
    		}
    	}
    

    The View

    The view really contains no business logic (in our case, no game logic). Here we can see its basic goals:

    • Set up a timer, which calls the Controller every 200 milliseconds to get a ViewModel containing an updated game board snapshot (serialized as JSON).
    • Parse the returned JSON to render the board, score, high score, level and next piece.
    • Set up another timer that calls the Controller every 1000 milliseconds to request a new movement for the falling pieces.
    • Listen to the keyboard events, and calls the Controller to start a new game, to pause of resume the game, to move or rotate the falling piece, or to restart the game.

    This is pretty much what the View does. Notice that, in a traditional (non-MVC) ASP.net application, we would have the code behind class, which would probably hold some business logic. Thanks to MVC, the principle of separation of concerns is maintained, and we can move the business logic away from our View.

    We handle user's gesture by attaching a function to the keydown event of the page document using jQuery syntax:

    	$(document).keydown(function (event) {
    	switch (event.keyCode) {
    		case 32: //space
    			if (gameState.current_state == 'intro')
    				gameState.process('play');
    			else if (gameState.current_state == 'paused')
    				gameState.process('continue');
    			else if (gameState.current_state == 'gameOver')
    				gameState.process('showIntro');
    			else
    				gameState.process('pause');
    			break;
    		case 37: //left
    			if (gameState.current_state == 'playing')
    				moveLeft();
    			break;
    		case 38: //up
    			if (gameState.current_state == 'playing')
    				moveUp();
    			break;
    		case 39: //right
    			if (gameState.current_state == 'playing')
    				moveRight();
    			break;
    		case 40: //down
    			if (gameState.current_state == 'playing')
    				moveDown();
    			break;
    	}
    });
    

    The following table shows the events (such as timer ticks or key pressing), the jQuery-like ajax call on the BricksView side and the methods (actions) called on the BricksController side.

    Event / Key The View code The Controller code
            
            Every 1000 milliseconds
    			
    $(document).everyTime(1000, function (i) {
    	$.ajax({
    	type: "GET",
    	url: "Tick",
    	cache: false,
    	dataType: "json",
    	error: function (xhr, status, error) {
    		//alert(xhr.status);
    	},
    	success: function (json) {
    	}
    	});
    });
    			
    			
    
    			
    public ActionResult Tick()
    {
    	BricksManager.Instance.Presenter.Tick();
    	return new JsonResult() 
    	{ Data = "", 
    	JsonRequestBehavior = 
    	JsonRequestBehavior.AllowGet };
    }
    
    
    
    			
            
            Every 200 milliseconds
    $(document).everyTime(200, function (i) {
    if (gameState.current_state == 'playing') {
    
    $.ajax({
    	type: "GET",
    	url: "GetBoard",
    	cache: false,
    	//                    data: {},
    	dataType: "json",
    	error: function (xhr, status, error) {
    		//alert(xhr.status);
    		//alert(error);
    	},
    	success: function (json) {
    		...
    	}
    });
    }
    });
    			
    			
    			
    			
    			
    public ActionResult GetBoard()
    {
    	return new JsonResult() { Data = 
    	BricksManager.Instance.CurrentBoard, 
    		JsonRequestBehavior = 
    		JsonRequestBehavior.AllowGet };
    }
    
    
    
    
    
    
    
    
    			
    function initializeBoard() {
    $.ajax({
    	type: "GET",
    	url: "InitializeBoard",
    	cache: false,
    	dataType: "json",
    	error: function (xhr, status, error) {
    		alert(xhr.status);
    	},
    	success: function (json) {
    	}
    });
    }
    			
    			
    			
    public ActionResult InitializeBoard()
    {
    	BricksManager.Instance.InitializeBoard();
    	
    	return new JsonResult() { Data = "", 
    	JsonRequestBehavior = 
    	JsonRequestBehavior.AllowGet };
    }
    		
    		
    		
    			
    function moveLeft() {
    $.ajax({
    	type: "GET",
    	url: "MoveLeft",
    	cache: false,
    	dataType: "json",
    	error: function (xhr, status, error) {
    		//alert(xhr.status);
    	},
    	success: function (json) {
    	}
    });
    }
    			
    			
    			
    			
    public ActionResult MoveLeft()
    {
    	BricksManager.Instance.Presenter.MoveLeft();
    	return new JsonResult() { Data = "", 
    	JsonRequestBehavior = 
    	JsonRequestBehavior.AllowGet };
    }
    		
    		
    		
    			
    function moveRight() {
    $.ajax({
    	type: "GET",
    	url: "MoveRight",
    	cache: false,
    	dataType: "json",
    	error: function (xhr, status, error) {
    	//alert(xhr.status);
    	},
    	success: function (json) {
    	}
    });
    }
    			
    		
    		
    public ActionResult MoveRight()
    {
    	BricksManager.Instance.Presenter.MoveRight();
    	return new JsonResult() { Data = "", 
    	JsonRequestBehavior = J
    	sonRequestBehavior.AllowGet };
    }
    		
    		
    		
    		
    			
    function moveDown() {
    $.ajax({
    	type: "GET",
    	url: "MoveDown",
    	cache: false,
    	dataType: "json",
    	error: function (xhr, status, error) {
    		//alert(xhr.status);
    	},
    	success: function (json) {
    	}
    });
    }
    			
    			
    			
    			
    public ActionResult MoveDown()
    {
    	BricksManager.Instance.Presenter.MoveDown();
    	return new JsonResult() { Data = "", 
    	JsonRequestBehavior = 
    	JsonRequestBehavior.AllowGet };
    }
    		
    		
    		
    			
    function moveUp() {
    $.ajax({
    	type: "GET",
    	url: "MoveUp",
    	cache: false,
    	dataType: "json",
    	error: function (xhr, status, error) {
    	//alert(xhr.status);
    	},
    	success: function (json) {
    	}
    });
    }
    			
    			
    			
    			
    public ActionResult MoveUp()
    {
    	BricksManager.Instance.Presenter.Rotate90();
    	return new JsonResult() { Data = "", 
    	JsonRequestBehavior = 
    	JsonRequestBehavior.AllowGet };
    }
    		
    		
    		
    			

    Here goes basically all the html we need for the View. Notice that all elements you see on the game screen are there, except for the bricks:

    	<body>
    		<br />
    		<div class="screen">
    			<div id="title" class="title">
    				<img src="../../Content/images/Title.png" />
    				<div class="subTitle">©2011 Marcelo Ricardo de Oliveira<br />
    				Made for The Code Project<img src="../../Content/images/Bob.png" class="bob"/></div>
    				<br />
    				<div class="press">Press SPACE to start game!</div>
    			</div>
    			<div class="centerPanel">
    				<div class="board">
    				</div>
    				<div class="scorePanel">
    					<div>
    						Score</div>
    						<div id="divScore" class="scoreText">000000</div>
    						<br />
    					<div>
    						HiScore</div>
    						<div id="divHiScore" class="scoreText">000000</div>
    						<br />
    					<div>
    						Lines</div>
    						<div id="divLines" class="scoreText">0</div>
    						<br />
    					<div>
    						Level</div>
    						<div id="divLevel" class="scoreText">0</div>
    						<br />
    					<div>
    						Next</div>
    						<div id="divNext" class="scoreText"></div>
    						
    				</div>
    			</div>
    			<div id="gamePaused">
    				GAME PAUSED<br />Press SPACE to continue!</div>
    			</div>
    			<div id="gameOver">
    				GAME OVER<br />Press SPACE to restart!</div>
    			</div>
    		</div>
    	</body>
    	</html>
    

    This happens because the bricks are divs generated dinamically, when the application starts. Instead of hard-coding this divs into the html, the dinamic generation makes it a lot easier to control how the bricks are generated, how they are rendered and so on.

    Here is the code that generates all the bricks, incluiding the bricks in the "Next" section on the score board. Notice how elegant is the jQuery syntax for appending html to existing html elements:

            function createCells() {
                for (var row = 0; row < 16; row++) {
                    for (var col = 0; col < 10; col++) {
                        var divId = 'cell_' + row + '_' + col;
                        var imgId = 'img_' + row + '_' + col;
                        var divTag = '<div id="' + divId + '" name="brick" class="colorChip clearfix"></div>';
                        $(divTag).appendTo('.board');
                    }
                    $('<div class="clear">').appendTo('.board');
                    $('</div>').appendTo('.board');
                }
    
                for (var row = 0; row < 2; row++) {
                    for (var col = 0; col < 4; col++) {
                        var divId = 'next_' + row + '_' + col;
                        var imgId = 'nextImg_' + row + '_' + col;
                        var divTag = '<div id="' + divId + '" name="brick" class="colorChip clearfix"></div>';
                        $(divTag).appendTo('#divNext');
                    }
                    $('<div class="clear">').appendTo('#divNext');
                    $('</div>').appendTo('#divNext');
                }
            }
    

    One of the most important parts on the BricksView side is the game board rendering. Notice that we don't use images for the bricks; instead, we use the CSS3 Webkit Gradient generator, which works only on CSS3 enabled browsers:

    	$('#divScore').text(json.Score);
    	$('#divHiScore').text(json.HiScore);
    	$('#divLines').text(json.Lines);
    	$('#divLevel').text(json.Level);
    
    	$.each(json.Bricks, function (i, val) {
    		$('#cell_' + val.Row + '_' + val.Col).css('background-image', 
    		'-webkit-gradient(linear, left top, right bottom, color-stop(0.0, ' + val.Color + '), 
    		color-stop(1.0, rgba(0, 0, 0, 0.0)))');
    		$('#cell_' + val.Row + '_' + val.Col).css('border-color', val.Color);
    	});
    
    	for (var row = 0; row < 2; row++) {
    		for (var col = 0; col < 4; col++) {
    			$('#next_' + row + '_' + col).css('background-image', 
    			'-webkit-gradient(linear, left top, right bottom, color-stop(0.0, #000), color-stop(1.0, #000))');
    			$('#next_' + row + '_' + col).css('border-color', '#333');
    		}
    	}
    
    	$.each(json.Next, function (i, val) {
    		$('#next_' + val.Row + '_' + val.Col).css('background-image', 
    		'-webkit-gradient(linear, left top, right bottom, color-stop(0.0, ' + val.Color + '), 
    		color-stop(1.0, rgba(0, 0, 0, 0.0)))');
    		$('#next_' + val.Row + '_' + val.Col).css('border-color', val.Color);
    	});
    

    This is a snapshot of the Json shown by Json viewer. Notice the red bricks represented in Json:

    jsonviewer.png

    And here are the Json data of Score, High Score, Lines and Level information presented in the view:

    jsonviewer2.png

    The Controller

    As we can see below, the Controller is even dumber than the View. Its goal is merely to expose actions that are consumed/called by the view, call the hard-working method on the GameManager side, and return (or not) a JSON-serialized ViewModel.

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.Web.Mvc;
    using MVCBricks.Core;
    
    namespace MVCBricks.Controllers
    {
        [System.Web.Mvc.OutputCache(NoStore = true, Duration = 0, VaryByParam = "*")]
        public class BricksController : Controller
        {
            public ActionResult Index()
            {
                return View();
            }
    
            public ActionResult GetBoard()
            {
                return new JsonResult() { Data = BricksManager.Instance.CurrentBoard, 
                    JsonRequestBehavior = JsonRequestBehavior.AllowGet };
            }
    
            public ActionResult Tick()
            {
                BricksManager.Instance.Presenter.Tick();
                return new JsonResult() { Data = "", JsonRequestBehavior = JsonRequestBehavior.AllowGet };
            }
    
            public ActionResult MoveLeft()
            {
                BricksManager.Instance.Presenter.MoveLeft();
                return new JsonResult() { Data = "", JsonRequestBehavior = JsonRequestBehavior.AllowGet };
            }
    
            public ActionResult MoveUp()
            {
                BricksManager.Instance.Presenter.Rotate90();
                return new JsonResult() { Data = "", JsonRequestBehavior = JsonRequestBehavior.AllowGet };
            }
    
            public ActionResult MoveRight()
            {
                BricksManager.Instance.Presenter.MoveRight();
                return new JsonResult() { Data = "", JsonRequestBehavior = JsonRequestBehavior.AllowGet };
            }
    
            public ActionResult MoveDown()
            {
                BricksManager.Instance.Presenter.MoveDown();
                return new JsonResult() { Data = "", JsonRequestBehavior = JsonRequestBehavior.AllowGet };
            }
    
            public ActionResult InitializeBoard()
            {
                BricksManager.Instance.InitializeBoard();
                
                return new JsonResult() { Data = "", JsonRequestBehavior = JsonRequestBehavior.AllowGet };
            }
        }
    }
    

    The Finite State Machine

    As you might expect, the game must have a means to control the states, so that you don't have falling pieces before the game has started, or after the game is over. Also, the score shouldn't be displayed while in the Intro mode.

    At first I was using boolean variables on the javascript side to handle the game state (i.e. "isPlaying", "isPaused", "isGameOver", and so on), but this didn't seem elegant nor efficient. So, at the middle of the development I noticed the application had to use a better state management, such as a finite state machine (FSM). So I searched and finally found out a simple, easy-to-use finite state machine for JavaScript, written by Anthony Blackshaw.

    The implementation for Blackshaw's FSM is quite simple. First, you declare a state machine object (in our case, gameState). Then you add transitions to the state machine instance. Each transition defines:

    • FSM.prototype.add_transition = function ( action, state, callback, next_state )

    1. The action name, which is invoked to process the transition.
    2. The initial state the machine must hold for the transition to work.
    3. The callback function which is called when the transition occurs.
    4. The next state to which the state machine is switched at the end of the transition.

    And here is the real implementation:

        <script type="text/javascript">
    
            //Finite State Machine for JavaScript
            //by Anthony Blackshaw
            //http: //antsdev.wordpress.com/2008/06/18/a-simple-js-finite-state-machine/
    
            var gameState = new FSM("intro");
    
            gameState.add_transition("play", "intro", changeIntroToPlaying, "playing");        
            gameState.add_transition("pause", "playing", changePlayingToPaused, "paused");
            gameState.add_transition("continue", "paused", changePausedToPlaying, "playing");
            gameState.add_transition("end", "playing", changePlayingToGameOver, "gameOver");
            gameState.add_transition("showIntro", "gameOver", changeGameOverToIntro, "intro");
    

    The play action hides the subtitle, and animates the top of the title o the position 0 (top of the screen). Besides, both the scorePanel and the boar are made visible thanks to an fade-in effect animation on their opacity css property..

            function changeIntroToPlaying() {
                initializeBoard();
                $('.subTitle').css('visibility', 'hidden');
                $('.press').css('visibility', 'hidden');
    
                $('.title').animate({
                    top: 0
                }, 1000, 'swing', function () {
                    // Animation complete.
                    $('.scorePanel').animate({
                        opacity: 1.0
                    }, 1000, 'swing', function () {
                        $('.scorePanel').css('visibility', 'visible');
                    });
    
                    $('.board').animate({
                        opacity: 1.0
                    }, 1000, 'swing', function () {
                        $('.board').css('visibility', 'visible');
                    });
                });
            }
    

    The pause action just shows the gamePaused div over the game board.

            function changePlayingToPaused () {
                $('#gamePaused').css('visibility', 'visible');
            }
    

    The pause itself in the game play happens because the state is switched to "paused" and as we can see below, the controller's action is not called unless the game state machine is on the "playing" state:

    	...
    	$(document).everyTime(200, function (i) {
    
    		if (gameState.current_state == 'playing') {
    
    			$.ajax({
    				type: "GET",
    				url: "GetBoard",
    	...
    

    The continue action just hides the gamePaused div over the game board.

            function changePausedToPlaying() {
                $('#gamePaused').css('visibility', 'hidden');
            }
    

    Since the state is switched back to "playing", the game continues because the controller's action can now be called.

    The end action just shows the gameOver div over the game board, indicating that the pile of bricks reached the top of the board.

            function changePlayingToGameOver () {
                $('#gameOver').css('visibility', 'visible');
            }
    

    The showIntro action fades-out both the score panel and the game board, and animates the title back to the center of the screen. In addition, the subtitle credits are shown again.

            function changeGameOverToIntro() {
                $('#gameOver').css('visibility', 'hidden');
    
                $('.scorePanel').animate({
                    opacity: 0.0
                }, 1000, 'swing', function () {
                    $('.scorePanel').css('visibility', 'hidden');
                });
    
                $('.board').animate({
                    opacity: 0.0
                }, 1000, 'swing', function () {
                    // Animation complete.
                    $('.board').css('visibility', 'hidden');
                    $('.title').animate({
                        top: 200
                    }, 1000, 'swing', function () {
                        // Animation complete.
                        $('.subTitle').css('visibility', 'visible');
                        $('.press').css('visibility', 'visible');
                    });
                });
            }
    

    The Game Manager

    The GameManager is a class that holds all methods needed by the BricksController so that the BricksView requests can be communicated to the game engine, and the responses can be sent back to the BricksView.

    	using System;
    	using System.Collections.Generic;
    	using System.Linq;
    	using System.Text;
    
    	namespace MVCBricks.Core
    	{
    		public class GameManager : MVCBricks.Core.IView
    		{
    			private static GameManager instance = null;
    			private static BricksPresenter presenter = null;
    			private static BoardViewModel currentBoard = null;
    
    			private GameManager()
    			{
    				currentBoard = new BoardViewModel();
    				currentBoard.Bricks = new BrickViewModel[] { };
    
    				presenter = new BricksPresenter(this);
    				presenter.InitializeBoard();
    				presenter.Tick();
    			}
    

    When the DisplayScore is called, all the scoreboard data are gathered so that they can be consumed by the view through a single call from the controller.

    			public void DisplayScore(int score, int hiScore, int lines, 
    			int level, MVCBricks.Core.Shapes.IShape next)
    			{
    				currentBoard.Score = score;
    				currentBoard.HiScore = hiScore;
    				currentBoard.Lines = lines;
    				currentBoard.Level = level;
    				currentBoard.Next = GetBricksArray(next.ShapeArray.GetUpperBound(1) + 1, 
    				next.ShapeArray.GetUpperBound(0) + 1, next.ShapeArray);
    			}
    

    The GetBricksArray method converts both the game board bricks array and the next shape array into a system of colors which the view can understand.

    			private BrickViewModel[] GetBricksArray(int rowCount, int colCount, IBrick[,] array)
    			{
    				var bricksList = new List<BrickViewModel>();
    
    				for (var row = 0; row < rowCount; row++)
    				{
    					for (var col = 0; col < colCount; col++)
    					{
    						var b = array[col, row];
    						if (b != null)
    						{
    							bricksList.Add(new BrickViewModel()
    							{
    								Row = row,
    								Col = col,
    								Color = b.Color.ToString().Replace("Color [", "").Replace("]", "")
    							});
    						}
    						else
    						{
    							bricksList.Add(new BrickViewModel()
    							{
    								Row = row,
    								Col = col,
    								Color = "rgba(0, 0, 0, 1.0)"
    							});
    						}
    					}
    				}
    				return bricksList.ToArray();
    			}
    

    Final Considerations

    Thanks a lot for reading my MVC Bricks article. I hope it may be useful for you in some way, either by the MVC concepts covered here, or the jQuery syntax which makes the javascript section thin and elegant, or even because the fun of the game itself. Feel free to comment below. Please share your ideas, complaints, advices so the next articles will get better and better.

    History

    • 2011-04-23: Initial version.
    • 2011-04-29: Images corrected. 
    • 2011-05-03: Json viewer images attached. 
    • 2011-05-05: Game Manager explained.

    License

    This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

    A list of licenses authors might use can be found here