Bits & Bytes

Posts Tagged ‘web’

How to Program a Walkable 2D Game Map in JavaScript

The Demonstration

The basis for many 2D games is a tiled game board that you can navigate by pressing direction keys to walk across it one square at a time. In this post, I will show you how to program the basic elements of this type of game engine. If you want to see the code run, left-click this link and use the arrow keys to walk around the board.

The screen displays a 5×5 board of squares. These squares represent the visible portion of the map. The squares are painted in different shades and colors to represent what would be different types of terrain on the map. The map is a 20×20 block of randomly-generated colors that represent the different terrains. The 5×5 board is centered on a square of the map that represents the character’s position. Squares that lie outside the map are colored black to show that we can not move onto them.

The Code

The code consists of two code files which are shown below: an HTML file and a JavaScript file. The HTML code can be copied into any file with a .html extension while the JavaScript file should be named “XoaXGameBoard2D.js” in order to work with the HTML file as it is written here. The HTML file consists of 25 divs that are 100 pixels by 100 pixels and are contained inside of one large div that is 500 pixels by 500 pixels; this is the game board and the small divs are colored to represent the terrain. The JavaScript file consists of four functions that are used to initialize and update the game board and handle key presses.

The Initialize() function is set to be called when the window is loaded. Once the window is loaded, the first set of for-loops allocates a 20×20 2d array for the qppMap array and fills it with colors via calls to the GetRandomColor() function. After the map is created, the board of visible tiles is allocated as the 2d array qppBoard and the entries are set equal to the 25 small div elements. These elements are colored via the call to the ColorBoard() function. Then we set the function KeyHandler() to be called to handle key down events. and set the variable qpBkdg to hold the large div that holds the whole map.

The function GetRandomColor() returns a color when it is called. The four colors represent different terrains with the green being generated more frequently as the base color–think of it as something like plain grass.

The ColorBoard() function is used to color the board squares initially and after every move that is made. Each color is pulled from the corresponding squares of the map and black is used to represent squares that are off the map. Note that the integers iLocX and iLocY are used to represent the position of the character, which is always located in the center board tile.

To move our character around the map, we use the function KeyHandler() to process key events when an arrow key is pushed. The function uses the four key codes to handle each of the four directional key pushes. To do this, it checks that the move does not take the character off of the map and then adjusts the coordinates of the character’s position: iLocX and iLocY. Once this is done, the board is repainted via the call to ColorBoard().

HTML File


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">

<head>
<title>XoaX.net's Javascript 2D Game Board Example</title>

<script type="text/javascript" src="XoaXGameBoard2D.js"></script>
</head>

<body>

<div id="gameboard" style="position:absolute; width:500px; height:500px; background:#aaaaaa; margin:30px;">
<div id="A11" style="left:0px; top:0px; position:absolute; width:100px; height:100px;"></div>
<div id="A12" style="left:100px; top:0px; position:absolute; width:100px; height:100px;"></div>
<div id="A13" style="left:200px; top:0px; position:absolute; width:100px; height:100px;"></div>
<div id="A14" style="left:300px; top:0px; position:absolute; width:100px; height:100px;"></div>
<div id="A15" style="left:400px; top:0px; position:absolute; width:100px; height:100px;"></div>

<div id="A21" style="left:0px; top:100px; position:absolute; width:100px; height:100px;"></div>
<div id="A22" style="left:100px; top:100px; position:absolute; width:100px; height:100px;"></div>
<div id="A23" style="left:200px; top:100px; position:absolute; width:100px; height:100px;"></div>
<div id="A24" style="left:300px; top:100px; position:absolute; width:100px; height:100px;"></div>
<div id="A25" style="left:400px; top:100px; position:absolute; width:100px; height:100px;"></div>

<div id="A31" style="left:0px; top:200px; position:absolute; width:100px; height:100px;"></div>
<div id="A32" style="left:100px; top:200px; position:absolute; width:100px; height:100px;"></div>
<div id="A33" style="left:200px; top:200px; position:absolute; width:100px; height:100px;"></div>
<div id="A34" style="left:300px; top:200px; position:absolute; width:100px; height:100px;"></div>
<div id="A35" style="left:400px; top:200px; position:absolute; width:100px; height:100px;"></div>

<div id="A41" style="left:0px; top:300px; position:absolute; width:100px; height:100px;"></div>
<div id="A42" style="left:100px; top:300px; position:absolute; width:100px; height:100px;"></div>
<div id="A43" style="left:200px; top:300px; position:absolute; width:100px; height:100px;"></div>
<div id="A44" style="left:300px; top:300px; position:absolute; width:100px; height:100px;"></div>
<div id="A45" style="left:400px; top:300px; position:absolute; width:100px; height:100px;"></div>

<div id="A51" style="left:0px; top:400px; position:absolute; width:100px; height:100px;"></div>
<div id="A52" style="left:100px; top:400px; position:absolute; width:100px; height:100px;"></div>
<div id="A53" style="left:200px; top:400px; position:absolute; width:100px; height:100px;"></div>
<div id="A54" style="left:300px; top:400px; position:absolute; width:100px; height:100px;"></div>
<div id="A55" style="left:400px; top:400px; position:absolute; width:100px; height:100px;"></div>
</div>

</body>
</html>

JavaScript File


var qpBkdg = null;
var qppBoard = null;
var qppMap = null;
var iLocX = 10;
var iLocY = 10;

function KeyHandler(qKeyEvent) {
    var iKeyDown = 0;
    var iLeftArrow = 37;
    var iUpArrow = 38;
    var iRightArrow = 39;
    var iDownArrow = 40;

    if (qKeyEvent) {
        iKeyDown = qKeyEvent.which;
    } else {
        iKeyDown = window.event.keyCode;
    }

    if (iKeyDown === iLeftArrow) {
        if (iLocX > 0) {
            iLocX = iLocX - 1;
        }
    } else if (iKeyDown === iRightArrow) {
        if (iLocX < 19) {
            iLocX = iLocX + 1;
        }
    } else if (iKeyDown === iUpArrow) {
        if (iLocY > 0) {
            iLocY = iLocY - 1;
        }
    } else if (iKeyDown === iDownArrow) {
        if (iLocY < 19) {
            iLocY = iLocY + 1;
        }
    }
    ColorBoard();
    return false;
}

function GetRandomColor() {
    var iRnd = Math.floor(Math.random()*10);
    switch(iRnd) {
        case 0:
        {
            return 'gray';
        }
        case 1:
        {
            return 'brown';
        }
        case 2:
        {
            return 'lime';
        }
        default:
        {
            return 'green';
        }
    }
}

function ColorBoard() {
    for (var iY = iLocY - 2; iY < iLocY + 3; iY++) {
        for (var iX = iLocX - 2; iX < iLocX + 3; iX++) {
            if (iY < 0 || iX < 0 || iY > 19 || iX > 19) {
                qppBoard[iY - iLocY + 2][iX - iLocX + 2].style.backgroundColor = 'black';
            } else {
                qppBoard[iY - iLocY + 2][iX - iLocX + 2].style.backgroundColor = qppMap[iY][iX];
            }
        }
    }
}

function Initialize() {
    // Generate map
    qppMap = new Array(20);
    for (var iY = 0; iY < 20; iY++) {
        qppMap[iY] = new Array(20);
        for (var iX = 0; iX < 20; iX++) {
            qppMap[iY][iX] = GetRandomColor();
        }
    }

    // Allocate the visible board
    qppBoard = new Array(5);
    for (var iY = 0; iY < 5; iY++) {
        qppBoard[iY] = new Array(5);
        for (var iX = 0; iX < 5; iX++) {
            qppBoard[iY][iX] = document.getElementById('A'+((iY+1)*10+(iX+1)));
        }
    }
    ColorBoard();

    document.onkeydown = KeyHandler;
    qpBkdg = document.getElementById('gameboard');
}

window.onload = Initialize;

Responding to Keyboard Events in JavaScript

Key Events

In this post, we demonstrate how to catch and handle a keyboard event. There are three basic key events: key down, key up, and key press. The “key press” event is a bit different, since it describes a combination of a key up and a key down event and generates a character code. So, we will ignore the key press and concentrate on the key up and key down events, which use key codes rather than character codes.

The key down and key up events are triggered when a key is pushed in and when a key is released. During the event, a key code is generated that determines which key on the keyboard is pushed or released.

Key Codes Versus Character Codes

A key code is different than a character code because the same key can generate multiple different characters. For example, one key generates both of the characters ‘1’ and ‘!’ on the keyboard. So, ‘1’ and ‘!’ have different character codes, but they are generated by the same key and correspond to the same key code. For a full list of the JavaScript key codes, consult our table of key codes.

The JavaScript Code

Below, we have a code sample that can be copied, pasted, and saved into a simple text file that should be saved with a .html extension. Then you can double-click the file, say “KeyEvent.html”, to open it with your default browser.

In the code, the Initialize() function sets the function KeyHandler() as the event handler for key down events, and it also initializes the color of the background rectangle in the div element, myrect, to medium gray. Once the KeyHandler() function is set as the handler for key down events, it is called whenever a key is pushed while the document has focus; focus is given to whatever is currently selected to receive messages, usually the window or the control that was last clicked.

Inside of the KeyHandler() function, we have iKeyDown, which will be used to hold the key code of the key that was pushed, and we have the key codes for the left arrow and right arrow keys stored in iLeftArrow and iRightArrow, respectively. The first “if” statement is used to store the key code; the method varies depending on the browser (the else branch is for IE). The second “if” statement sets the background color to red if the left arrow is pressed, blue if the right arrow is pressed, and green if any other key is pressed.

The Code in Action

To see the code running, left-click the red rectangle above to open a new window with the code running in it; press the arrow keys to see the color change. If the color does not change, then it is because the document does not have focus. I have added a border (not used in the code below) to the rectangle that is displayed when it receives focus. To see the focus change, left-click the address in the address bar of your browser and then left-click the rectangle.



<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">

<head>
<title>XoaX.net's Javascript Keyboard Event Example</title>

<script type="text/javascript">
/*<![CDATA[*/
var qpRect = null;

function KeyHandler(qKeyEvent) {
    var iKeyDown = 0;
    var iLeftArrow = 37;
    var iRightArrow = 39;

    if (qKeyEvent) {
        iKeyDown = qKeyEvent.which;
    } else {
        iKeyDown = window.event.keyCode;
    }

    if (iKeyDown === iLeftArrow) {
        qpRect.style.backgroundColor = '#ff0000';
    } else if (iKeyDown === iRightArrow) {
        qpRect.style.backgroundColor = '#0000ff';
    } else {
        qpRect.style.backgroundColor = '#00ff00';
    }
    return false;
}

function Initialize() {
    document.onkeydown = KeyHandler;
    qpRect = document.getElementById('myrect');
    qpRect.style.backgroundColor='#888888';
}

window.onload = Initialize;
/*]]>*/
</script>
</head>

<body>

<div id="myrect" style="width: 300px; height: 200px; margin: 50px;">
</div>

</body>
</html>


Specifying Colors in HTML with CSS

Colors in a computer are made up of three color channels: Red, Green, and Blue. These three channels are combined to form all colors. For example, when the three channels are at their maximum value, we get the color white. On the other hand, if all of the channels are set to zero, then we get the color black. We have several methods for setting a color in CSS.

RGB Values

background-color: rgb(255, 240, 200);

For our first method of setting a color, we use the rgb() functional notation. Inside the parentheses, the three values are given as comma separated decimal integers that range between 0 and 255. Above, we have a div that we set with the values red = 255, green = 240, and blue = 200. Since all of the channels are close to their maximum value of 255, the color is close to white. However, is has an orange tint because the red and green channels are the largest.

RGB Percentage Values

background-color: rgb(65%, 95%, 75%);

Instead of the values 0 to 255, we can use percentages. The notation is similar to the prior RGB Values except that the values are given as percentages instead of integers: rgb(65%, 95%, 75%). So, the value 0% is is the same as 0, and the value 100% is the same as 255, in RGB Values. Percentages can be given as fractional numbers, since they will be mapped to the range of values 0 to 255. For example, the color above, rgb(65%, 95%, 75%), is the same as rgb(255*.65, 255*.95, 255*.75) = rgb(165.75, 242.25, 191.25). Of course, since the color values can only be integers, these values are rounded to integers before they can be displayed. The color above has a larger green component in it, so looks green.

Hexadecimal RGB

background-color: #f4cfe5;

If you are wondering why the color values run from 0 to 255, then you might not be familiar with hexadecimal. However, you should learn it because it is a more convenient format for representing how computers store numbers. If you are not familiar with it I suggest watching our short videos on bits and bytes and binary, octal, and hexadecimal. These videos will go a long way toward clearing up this extremely important topic, which is likely to trouble you until you get it sorted out.

The hexadecimal RGB format uses a # sign to signal the hexadecimal format, which is then followed by three sets of two hexadecimal digits to represent the red, green, and blue channels:

   #f4cfe5.

These values convert to f4 = 244, cf = 207, and e5 = 229 in decimal. It takes some time to get used to hexadecimal. Once you do, however, it is more convenient to work with since the values from 0 to 255 can be represented with exactly two digits in hexadecimal. As you can see, the color above is a light purple because the red and blue channels are the largest.

Hexadecimal RGB Abbreviated

background-color: #adf;

The abbreviated hexadecimal format is similar to the two digit hexadecimal format, except that it only uses one digit to represent two digits. It does this by repeating the single hexadecimal digit. So, the abbreviated hexadecimal value #adf is the same as #aaddff:

   #adf

The color above is greenish-blue because the blue channel is the largest followed by the green.

Color Names

background-color: orange;

In addition to the formats above, it is possible to specify a color by name. There are only 17 names that are standardized. First, there is orange, which is shown above and is equal to the color #ffa500 in hexadecimal. The other 16 names are shown below, along with their hexadecimal equivalents.

black
#000000
gray
#808080
silver
#c0c0c0
white
#ffffff
maroon
#800000
red
#ff0000
purple
#800080
fuchsia
#ff00ff
green
#008000
lime
#00ff00
olive
#808000
yellow
#ffff00
navy
#000080
blue
#0000ff
teal
#008080
aqua
#0000ff

Other color designations are available in HTML 5. However, these formats are not supported in all browsers at this time.