Sokoban – HTML5 Game Technical Document

Scope:
A simple yet attractive tile based game developed in HTML5. It showcases the seamless capabilities of HTML5 in gamification.

Objective:

Sokoban was developed with an objective of exploring the basic features of HTML5 games. Another motive behind the game is to show how Ignatiuz can provide efficient gamification solutions to meet industry needs.

Target Audience:

Sokoban is designed keeping audience of all the age group and genders in mind. It’s simple UI and attractive design make it easy and interesting to play.

Target Platforms:

The game works perfect on desktop. It is browser independent and works on almost all of the commonly used browsers. Increased use of mobile devices was kept in mind while developing this game. The game works smooth on mobile touch devices including Android and iOS devices.

Technology Used:

HTML5 is used as the basic canvas element for development of Sokoban. It is written in JavaScript.

The game is dependent on third Party Library Dependency: jquery-1.4.2.min.js, jquery-ui-1.8.4.custom.min.js, jquery.cookie.js

The best part is that the game does not run on any additional plugin like flash which makes it very light to operate and play. It loads quickly and does not require the user to download any additional plugin.

Required Elements:
1. Imagery inside the ‘Images’ folder.
2. Xml file for map routes inside the ‘maps’ folder.
3. Java scripts files inside the ‘scripts’ folder.
4. CSS file inside the ‘css’ folder.
5. Game run command in JavaScript language to be added in html page.

Sokoban.html
Scope: main user interface file for Sokoban game.
Roles:

1. Contains the required JavaScript and css files.

 <link type="text/css" href="css/game.css" rel="StyleSheet">
 <script src="Scripts/jquery-1.4.2.min.js" type="text/javascript"></script>
 <script src="Scripts/jquery-ui-1.8.4.custom.min.js" type="text/javascript"></script>
 <script src="Scripts/jquery.cookie.js" type="text/javascript"></script>
 <script src="Scripts/GameJs/LevelStatistics.js" type="text/javascript"></script>
 <script src="Scripts/GameJs/DrawableElements.js" type="text/javascript"></script>
 <script src="Scripts/GameJs/Map.js" type="text/javascript"></script>
 <script src="Scripts/GameJs/Game.js" type="text/javascript"></script>

2. Includes the required user interface elements like div and canvas elements.

 <canvas id="canvas" width="250" height="250"> Sorry your browser does not support
   Canvas. Please use different browser: <a href="http://www.mozilla-europe.org/en/firefox/">Get 
   Firefox</a> or <a href="http://www.google.com/chrome">Get Chrome</a> </canvas>

3. Initializes the next level and game finish popups.

 $("div#dialog").dialog(
                {
                    autoOpen:false,
                    modal: true,
                    width: 250
                });
        $("div#gameOverDialog").dialog(
        {
            autoOpen: false,
            modal: true,
            width: 250
        });

4. Initializes and run the Sokoban game.

 document.sokobanGame = new Game();
 document.sokobanGame.Run();

 

Game.js
Scope:
main JavaScript file incorporating the functions and methods of all other JavaScript files.
Roles:
1. Initializes the game variables
2. Configures/creates the canvas element

 _canvas = document.getElementById('canvas');

        if (_canvas && _canvas.getContext) {

            _canvasContext = _canvas.getContext('2d');

            _canvasBuffer = document.createElement('canvas');
            _canvasBuffer.width = _canvas.width;
            _canvasBuffer.height = _canvas.height;
            _canvasBufferContext = _canvasBuffer.getContext('2d');
            return true;
        }

3. Initializes the Image Repository class to load all the images required in the game

 _imageRepository = new ImageRepository();

4. Detects device/platform for best performance

 this.getDevice = function () {
        var deviceName = "Web";

        if (navigator.userAgent.match(/iPad/i) != null) {
            deviceName = "iPad";
        }
        else if (navigator.userAgent.match(/iPod/i) != null) {
            deviceName = "iPod";
        }
        else if (navigator.userAgent.match(/iPhone/i) != null) {
            deviceName = "iPhone";
        }
        else if (navigator.userAgent.match(/Android/i) != null) {
            deviceName = "Android";
        }

        return deviceName;
    }

5. Configures elements based on device detections

 this.deviceName = this.getDevice();
    	if (this.deviceName != "Web") {	
            		this.useTouch = true;
		document.getElementById("wasd").style.display = "none";
		document.getElementById("wasd-touch").style.display = "block";			
            	 }
            	else
	{
		document.getElementById("wasd").style.display = "block";
		document.getElementById("wasd-touch").style.display = "none";
	}

6. Checks cookie for game level

 var cookie = $.cookie("sokoban");
        if (cookie != null) {
            //user already finished some maps
            mapToLoad = cookie;

        } else {
            $.cookie("sokoban", 1, { expires: 365 });

        }

7. Initializes the Map class to load the xml map file

 _map = new Map(_imageRepository);

8. Initializes interval for updating the game view

 this.InitialUpdateDraw = setInterval(this.InitialUpdateRun, this.CheckInterval);

9. Checks if level/game is completed

 if (_map.Finished) {
 $(document).unbind('keyup');
 document.getElementById("u").removeEventListener('touchstart', document.sokobanGame.MoveUp, false);
 document.getElementById("d").removeEventListener('touchstart', document.sokobanGame.MoveDown, false);
 document.getElementById("l").removeEventListener('touchstart', document.sokobanGame.MoveLeft, false);
 document.getElementById("r").removeEventListener('touchstart', document.sokobanGame.MoveRight, false);

10. Displays message if level/game finished

 if (_map.Number == 16) {
                //this is last map
                $("div#gameOverDialog").dialog("open");
                return;
            }
            $("div#dialog").dialog("open");
            $("div#dialog button").bind('click', function () {
                document.sokobanGame.LoadNextLevel();
                $(this).unbind('click');
            });

11. Loads next level if current level finished

 this.LoadNextLevel = function () {
		$("div#dialog").dialog("close");
		var nextLevel = ++_map.Number;
		$.cookie("sokoban", nextLevel, { expires: 365 });
		document.sokobanGame.LoadContent();
	}

 

Map.jS
Scope:JavaScript file to load and create the map based on level
Roles:
1. Loads xml map file based on current level

 this.LoadMap = function (levelNumber) {
        $.ajax({
            type: "GET",
            url: "maps/map"+levelNumber+".xml",
			//url: "maps/map16.xml",
            dataType: "xml",
            success: this.ParseXmlMap,
            context: this
        });
    }

2. Parses the xml map file

 this.ParseXmlMap = function (xml) {

        this._width = parseFloat($(xml).find('Level').attr('Width'));
        this._height = parseFloat($(xml).find('Level').attr('Height'));
        this.Number = parseFloat($(xml).find('Level').attr('No'));

        this._map = new Array(this._width)

        for (var x = 0; x < this._map.length; x++) {
            this._map[x] = new Array(this._height)
         }
 }

3. Draws the map according to parsed xml file

 this.Draw = function (canvasContext) {

        var xCanvasPos = 0;
        var yCanvasPos = 20;
        var tileSize = 30;
        for (var y = 0; y < this._height; y++) {
            xCanvasPos = 0;
            for (var x = 0; x < this._width; x++) {

                var img = this._map[x][y].GetImage();
                if (img != null) {
                    // draw image
                    canvasContext.drawImage(img, xCanvasPos, yCanvasPos);

                } else {
                    // draw rectangle
                    canvasContext.fillStyle = this._map[x][y].GetFillStyle();
                    canvasContext.fillRect(xCanvasPos, yCanvasPos, tileSize, tileSize);
                }

                xCanvasPos += tileSize;
            }
            yCanvasPos += tileSize;
        }

4. Initializes the LevelStatistics class

 var _levelStatistics = new LevelStatistics(this);

5. Updates the LevelStatistics class variables

 this.Update = function () {
        //check for end game conditions
        if (this._goals > 0 && this._goalsAchived == this._goals) {
            this.Finished = true;
        }
        _levelStatistics.Update();
    }

 

DrawableElements.js
Scope:JavaScript file to load the imagery used in the game
Roles:
1. Loads all the required graphics used in the game

 this.LoadContent = function () {
        _imgLoaded = 0;

        //go through all properties and load images
        for (var key in this) {
            if (this[key] != null) {

                //this even will not fire in Chrome if Image is cached !! Workaround needed       
                this[key].onload = function () {
                    _imgLoaded++;
                }
            }
        }

2. Executes methods for movement of the player in the map

this.MoveBox = function(boxCell, moveBoxToCell, moveToX, moveToY)
               {
                //we need to move the box too
                this.Pushes++;   

                var posToMove = boxCell.constructor;
                var nextObject = moveBoxToCell.constructor;
                }

3. Validates movement of the player i.e. the player can move only in cells and not on walls

 this.ValidateMove = function (targetCell, nextCell) 
	{
		if(targetCell && nextCell)
		{
			var posToMove = targetCell.constructor;

			if (posToMove == Wall) {
				// wall is next, player cannot move there
				return false;
			}	

			return true;
		}        
        return false;
    }

4. Updates the game statistics i.e. movement count, goals achieved

 this._map._goalsAchived++;


LevelStatistics.js
Scope: JavaScript file to track and display the game statistics
Roles:
1. Contains the variables for game statistics

 var player = _map.GetPlayer();
     _pushes = player.Pushes;
    _moves = player.Moves;

2. Creates the visual element on game canvas to display the game statistics

 this.Draw = function (context) {
        var x = _map.OnScreenWidht + 10;
        var y = 20;

        context.fillStyle = "rgb(127,0,0)";
        context.strokeStyle = "rgb(0,0,0)";
        x += 20;
        context.strokeRect(x, y, 100, 85);
        x += 5;
        y += 20;
        context.font = "bold 12px sans-serif";
        context.fillText('Level ' + _map.Number + ' / 40', x, y);
        y += 25;
        context.fillText('Pushes : ' + _pushes, x, y);
        y += 25;
        context.fillText('Moves : ' + _moves, x, y);
    }

Leave a comment