Overview
|
Asteroids.html
|
Common.js
|
Asteroids.js
|
Digit.js
|
Text.js
|
AstroRock.js
|
AstroShip.js
|
AstroShot.js
|
AstroUFO.js
|
GNU License
|
|
Coding a JavaScript Game
2012-03-13 17:00 By Jason Birch
Second in a series of how to code articles. Demonstrating coding an Asteroids Clone game with various technologies. This example using JavaScript, implementing as an application on a web page.
In this article I have converted my Asteroids Clone code into JavaScript. I am now publishing the source code here under the GNU General Public License License as a series of articles describing how to write code using various technologies.
The code here can be freely distributed, but only on the condition that a credit to Jason Birch is maintained in the source code files and running application.
The source code is broken down into the classes which make the application, in the tabs on the left of this article. The tabs are placed in order from the simplest classes to the more complex classes last. There is a commentary accompanying each source code file explaining how the code operates.
The keys to play are: '<' Rotate left, '>' Rotate right, 'Z' Fire, 'X' Thrust, ' ' Hyperspace, '1' Start new game.
Object orientated code can be written in JavaScript, but it is a little awkward and not as tidy as it could be. But considering JavaScript is a scripting language it handles applications like this one better than I would have expected. Other examples of games I have written in JavaScript include:
Required Environment
To run a JavaScript application, any web browser can be used.
To develop JavaScript applications, any text editor can be used.
Writing Code
Any text editor can be used to create and edit JavaScript source code.
Compiling Code
JavaScript code does not need to be compiled. It can be executed directly from the source code.
Distributing Application
The JavaScript source code files are simply copied onto a file system or a web server.
Running Application
JavaScript can be run directly from the operating system by opening an HTML file in a web browser. Or it can be run from a web server in the same way.
|
The application runs in a web browser, this requires an HTML page to host the application. The HTML defines a play area in a DIV tag, and the size and colour of the area applied to the play area DIV are defined as a style called Display. The HTML page has to load the JavaScript code required for the application. The first JavaScript file provides general functions and is named Common.js, this file is loaded in the header of the HTML as it contains code required by the BODY tag of the HTML file. All other code is loaded after the DIV tag, as it refers to the DIV tag within the code. The BODY tag contains calls to functions in Common.js which capture user key presses and makes them available to the application code.
REF: 1.0
|
<!--
// Asteroids Clone JavaScript
// Copyright (C) 1997 Jason Birch
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<HTML>
<HEAD>
<TITLE>Asteroids</TITLE>
<STYLE>
.Display
{
COLOR: #55DD55;
BACKGROUND-COLOR: #000000;
POSITION: RELATIVE;
WIDTH: 512;
HEIGHT: 400;
OVERFLOW: HIDDEN;
}
</STYLE>
<SCRIPT LANGUAGE='JavaScript' SRC='Common.js'></SCRIPT>
</HEAD>
<BODY onKeyPress='KeyDown = true; DoKey(event);' onKeyUp='KeyDown = false; DoKey(event);'>
<DIV ID='Display' CLASS='Display'></DIV>
<SCRIPT LANGUAGE='JavaScript' SRC='Text.js'></SCRIPT>
<SCRIPT LANGUAGE='JavaScript' SRC='Digit.js'></SCRIPT>
<SCRIPT LANGUAGE='JavaScript' SRC='AstroShot.js'></SCRIPT>
<SCRIPT LANGUAGE='JavaScript' SRC='AstroRock.js'></SCRIPT>
<SCRIPT LANGUAGE='JavaScript' SRC='AstroShip.js'></SCRIPT>
<SCRIPT LANGUAGE='JavaScript' SRC='AstroUFO.js'></SCRIPT>
<SCRIPT LANGUAGE='JavaScript' SRC='Asteroids.js'></SCRIPT>
</BODY>
</HTML>
|
|
The code in this file is considered general code which may be shared across all objects. However, in this case it only contains a system function to capture key presses made by a user and makes them available to the application. The function flags certain events like thrust and rotate, as these events require to continue from the key press and end only when the key is released.
REF: 2.0
|
// Asteroids Clone JavaScript
// Copyright (C) 1997 Jason Birch
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
/***************/
/* Game flags. */
/***************/
var Key = 0;
var KeyDown = false;
var ThrustFlag = false;
var RotateLeftFlag = false;
var RotateRightFlag = false;
function DoKey(e)
{
if (window.event)
Key = window.event.keyCode;
else if (e)
Key = e.which;
else
Key = 0;
if (Key && KeyDown)
{
if (Key == 44)
RotateLeftFlag = true;
else if (Key == 46)
RotateRightFlag = true;
else if (Key == 120)
ThrustFlag = true;
}
else if (Key && !KeyDown)
{
if (Key == 188)
RotateLeftFlag = false;
else if (Key == 190)
RotateRightFlag = false;
else if (Key == 88)
ThrustFlag = false;
}
}
|
|
The main application code defines constants which define parameters of the application and are handy to be able to alter when migrating the application to an alternate environment.
REF: 3.0
|
// Asteroids Clone JavaScript
// Copyright (C) 1997 Jason Birch
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
/*************/
/* Constants */
/*************/
var WINDOW_WIDTH = 512;
var WINDOW_HEIGHT = 400;
var START_ROCKS = 5;
var MAX_ROCKS = 100;
var HISCORE_XOFFSET = 45;
var HISCORE_YOFFSET = 20;
var GAMEOVER_XOFFSET = 110;
var GAMEOVER_YOFFSET = 20;
var INSERTCOIN_XOFFSET = 130;
var INSERTCOIN_YOFFSET = 20;
var HISCORE_SCALE = 0.15;
|
Game objects are created. There are fewer global variables required for this implementation as the variables made global in other implementations are not required in the JavaScript implementation of the code.
REF: 3.1
|
/******************/
/* Game Variables */
/******************/
var Count;
var FirstRock;
var NextRock;
var Ship = new AstroShip();
var UFO = new AstroUFO();
var HiScore = new Digit();
var GameOver = new Text();
var InsertCoin = new Text();
var Rock = new Array();
for (Count = 0; Count < MAX_ROCKS; ++Count)
{
Rock[Count] = new AstroRock();
Rock[Count].AstroRockCreate("Display", "Rock_" + Count);
}
|
Game objects are initialized and a timer is started to handle the animation of the application.
REF: 3.2
|
/*******************************/
/* Initialise game components. */
/*******************************/
GameOver.TextSetLocation("Display", "GameOver",
WINDOW_WIDTH / 2 - GAMEOVER_XOFFSET,
WINDOW_HEIGHT / 2 - GAMEOVER_YOFFSET,
200, false, "GAME OVER", 0.3);
InsertCoin.TextSetLocation("Display", "InsertCoin",
WINDOW_WIDTH / 2 - INSERTCOIN_XOFFSET,
WINDOW_HEIGHT / 2 - INSERTCOIN_YOFFSET,
15, true, "INSERT COIN", 0.3);
HiScore.DigitSetLocation("Display", "HiScore",
WINDOW_WIDTH / 2 - HISCORE_XOFFSET,
HISCORE_YOFFSET, HISCORE_SCALE);
Ship.AstroShipSetArea("Display", "AstroShip", WINDOW_WIDTH, WINDOW_HEIGHT, 0.3);
UFO.AstroUFOSetArea("Display", "AstroUFO", WINDOW_WIDTH, WINDOW_HEIGHT, 0.3);
FirstRock = START_ROCKS;
NextRock = FirstRock;
for (Count = 0; Count < FirstRock; ++Count)
Rock[Count].AstroRockSetArea(WINDOW_WIDTH, WINDOW_HEIGHT, -1, -1, 3);
/*******************/
/* Start animating */
/*******************/
TimerID = window.setInterval("Timer();", 50);
|
In other implementations of the code a message loop is used to handle messages coming from the operating system into the application. However the JavaScript is running in a web browser which does not have a message loop facility, so key presses are captured in a browser event and flagged, and then the flags are used in the applications timer event.
REF: 3.3
|
/****************/
/* Animate game */
/****************/
function Timer()
{
var Count;
var ShotCount;
var RockFound;
/*************************/
/* 'X' key press, trust. */
/*************************/
if (ThrustFlag)
Ship.AstroShipThrust();
/*******************************/
/* '<' key press, rotate left. */
/*******************************/
else if (RotateLeftFlag)
{
Ship.AstroShipIncAngle(false);
Ship.AstroShipIncAngle(false);
}
/********************************/
/* '>' key press, rotate right. */
/********************************/
else if (RotateRightFlag)
{
Ship.AstroShipIncAngle(true);
Ship.AstroShipIncAngle(true);
}
/*************************/
/* 'Z' key press, shoot. */
/*************************/
else if (KeyDown && Key == 122)
{
Key = 0;
Ship.AstroShipShoot();
}
/******************************/
/* ' ' key press, hyperspace. */
/******************************/
else if (KeyDown && Key == 32)
{
Key = 0;
Ship.AstroShipHyperspace();
}
/*****************************/
/* 'F1' key press, new game. */
/*****************************/
else if (KeyDown && Key == 49)
{
Key = 0;
if (Ship.AstroShipGetLives() == 0)
{
if (Ship.AstroShipGetScore() > HiScore.DigitGetNumber())
HiScore.DigitSetNumber(Ship.AstroShipGetScore());
Ship.AstroShipReset();
UFO.AstroUFODestroy();
UFO.AstroUFOGetShot().AstroShotDestroy();
FirstRock = START_ROCKS;
NextRock = FirstRock;
for (Count = 0; Count < MAX_ROCKS; ++Count)
Rock[Count].AstroRockDestroy();
for (Count = 0; Count < FirstRock; ++Count)
Rock[Count].AstroRockSetArea(WINDOW_WIDTH, WINDOW_HEIGHT, -1, -1, 3);
}
}
|
Animate application objects and check for collisions between the objects.
REF: 3.5
|
Ship.AstroShipMove();
UFO.AstroUFOMove();
RockFound = false;
for (Count = 0; Count < MAX_ROCKS; ++Count)
{
if (Rock[Count].AstroRockGetSize() != Rock[Count].INACTIVE)
RockFound = true;
/*****************************/
/* Check for ship collision. */
/*****************************/
if (Ship.AstroShipGetCrash() == false
&& UFO.AstroUFOCollide(Ship.AstroShipGetXOffset(),
Ship.AstroShipGetYOffset(),
Ship.AstroShipGetWidth(),
Ship.AstroShipGetHeight()))
Ship.AstroShipSetCrash(true);
if (UFO.AstroUFOGetShot().AstroShotActive() == true
&& Ship.AstroShipCollide(UFO.AstroUFOGetShot().AstroShotGetXOffset(),
UFO.AstroUFOGetShot().AstroShotGetYOffset(),
2, 2))
Ship.AstroShipSetCrash(true);
if (Ship.AstroShipGetCrash() == false
&& Rock[Count].AstroRockCollide(Ship.AstroShipGetXOffset(),
Ship.AstroShipGetYOffset(),
Ship.AstroShipGetWidth(),
Ship.AstroShipGetHeight()))
{
Ship.AstroShipSetCrash(true);
Rock[++NextRock].AstroRockSetArea(WINDOW_WIDTH, WINDOW_HEIGHT,
Ship.AstroShipGetXOffset(),
Ship.AstroShipGetYOffset(),
Rock[Count].AstroRockGetSize());
}
/*************************/
/* Check for shot rocks. */
/*************************/
for (ShotCount = 0; ShotCount < Ship.AstroShipGetShotCount(); ++ShotCount)
{
if (Ship.AstroShipGetShot(ShotCount).AstroShotActive() != false)
{
if (UFO.AstroUFOCollide(Ship.AstroShipGetShot(ShotCount).AstroShotGetXOffset(),
Ship.AstroShipGetShot(ShotCount).AstroShotGetYOffset(),
2, 2))
{
Ship.AstroShipSetScore(Ship.AstroShipGetScore() + 100);
Ship.AstroShipGetShot(ShotCount).AstroShotDestroy();
}
if (Rock[Count].AstroRockCollide(
Ship.AstroShipGetShot(ShotCount).AstroShotGetXOffset(),
Ship.AstroShipGetShot(ShotCount).AstroShotGetYOffset(),
2, 2))
{
Ship.AstroShipSetScore(Ship.AstroShipGetScore()
+ 5*Rock[Count].AstroRockGetSize());
Ship.AstroShipGetShot(ShotCount).AstroShotDestroy();
if (NextRock+1 < MAX_ROCKS)
Rock[++NextRock].AstroRockSetArea(WINDOW_WIDTH, WINDOW_HEIGHT,
Rock[Count].AstroRockGetXOffset(),
Rock[Count].AstroRockGetYOffset(),
Rock[Count].AstroRockGetSize());
}
}
}
Rock[Count].AstroRockMove();
}
|
When no more rock objects exist, move to the next game level with an extra rock.
REF: 3.6
|
if (RockFound == false)
{
++FirstRock;
NextRock = FirstRock;
for (Count = 0; Count < FirstRock; ++Count)
Rock[Count].AstroRockSetArea(WINDOW_WIDTH, WINDOW_HEIGHT, -1, -1, 3);
}
GameOver.TextSetVisible(Ship.AstroShipGetLives() == false);
InsertCoin.TextSetVisible(GameOver.TextGetVisible() == false
&& Ship.AstroShipGetLives() == false);
|
Draw the game objects.
REF: 3.7
|
/*****************/
/* Draw graphics */
/*****************/
InsertCoin.TextDraw();
GameOver.TextDraw();
HiScore.DigitDraw();
Ship.AstroShipDraw();
UFO.AstroUFODraw();
for (Count = 0; Count < MAX_ROCKS; ++Count)
Rock[Count].AstroRockDraw();
}
|
|
This is the first example of a class in this applications JavaScript. JavaScript does't really handle classes vary neatly, however it is a scripting language so it does OK for what it is. In JavaScript a class has a constructor in which the classes instance variables are created by referring to them for the first time and initializing them with values. Constant declarations are handled in the same way as variable declarations. Then the member functions of the class are initialized in the same way as the member variables, which isn't very tidy as the function names need to be kept unique across all classes in the application.
REF: 4.0
|
// Asteroids Clone JavaScript
// Copyright (C) 1997 Jason Birch
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
/**********************/
/* Object Constructor */
/**********************/
function Digit()
{
this.DIGITS = 10;
this.Name = "";
this.xOffset = 0;
this.yOffset = 0;
this.Scale = 0;
this.TextColour;
this.NumberValue = 0;
this.Decimal = new Array();
this.DigitDraw = DigitDraw;
this.DigitSetLocation = DigitSetLocation;
this.DigitGetNumber = DigitGetNumber;
this.DigitSetNumber = DigitSetNumber;
}
|
Handle drawing the digits of the numeric value of this class. JavaScript does not support drawing primitives so the digits are defined as image files and then instances of the image files are created in the DIV area of the application. Changing the digit values involves changing the image used by an instance of an IMG tag.
REF: 4.1
|
function DigitDraw()
{
var DigitCount;
var Count;
var Divide;
var TempNumber;
/*****************************/
/* Plot score current value. */
/*****************************/
TempNumber = this.NumberValue;
for (DigitCount = this.DIGITS - 1; DigitCount >= 0; --DigitCount)
{
Divide = TempNumber%10;
TempNumber = Math.floor(TempNumber / 10.0);
/***************************/
/* Draw the current score. */
/***************************/
document.getElementById(this.Name + "_" + DigitCount).src = this.Decimal[Divide];
}
}
|
Initialize the class. Use an array of image names to define digits which can be displayed by the class. The following images are used:
REF: 4.2
|
function DigitSetLocation(WindowID, NewName, NewxOffset, NewyOffset, NewScale)
{
var DigitCount;
this.Name = NewName;
this.xOffset = NewxOffset;
this.yOffset = NewyOffset;
this.Scale = NewScale;
this.Decimal[0] = "AsteroidsJS/images/0.gif";
this.Decimal[1] = "AsteroidsJS/images/1.gif";
this.Decimal[2] = "AsteroidsJS/images/2.gif";
this.Decimal[3] = "AsteroidsJS/images/3.gif";
this.Decimal[4] = "AsteroidsJS/images/4.gif";
this.Decimal[5] = "AsteroidsJS/images/5.gif";
this.Decimal[6] = "AsteroidsJS/images/6.gif";
this.Decimal[7] = "AsteroidsJS/images/7.gif";
this.Decimal[8] = "AsteroidsJS/images/8.gif";
this.Decimal[9] = "AsteroidsJS/images/9.gif";
for (DigitCount = 0; DigitCount < this.DIGITS; ++DigitCount)
{
var NewElement = document.createElement("img");
document.getElementById(WindowID).appendChild(NewElement);
NewElement.id = this.Name + "_" + DigitCount;
NewElement.style.visibility = "visible";
NewElement.style.position = "absolute";
NewElement.style.left = this.xOffset;
NewElement.style.top = this.yOffset;
NewElement.style.width = 64 * this.Scale;
NewElement.style.height = 128 * this.Scale;
NewElement.src = this.Decimal[0];
this.xOffset += 80 * this.Scale;
}
}
|
Though JavaScript does not appear to have a concept of private class variables, still handle requests to get and set member variables in a controlled fashion.
REF: 4.3
|
function DigitGetNumber()
{
return this.NumberValue;
}
function DigitSetNumber(NewNumber)
{
this.NumberValue = NewNumber;
}
|
|
In JavaScript a class has a constructor in which the classes instance variables are created by referring to them for the first time and initializing them with values. Constant declarations are handled in the same way as variable declarations. Then the member functions of the class are initialized in the same way as the member variables, which isn't very tidy as the function names need to be kept unique across all classes in the application.
REF: 5.0
|
// Asteroids Clone JavaScript
// Copyright (C) 1997 Jason Birch
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
/**********************/
/* Object Constructor */
/**********************/
function Text()
{
this.INFINATE_FRAMES = -1;
this.Name = "";
this.xOffset = 0;
this.yOffset = 0;
this.Scale = 0;
this.Visible = false;
this.LastVisible = false;
this.FlashVisible = false;
this.Active = false;
this.Frames = this.INFINATE_FRAMES;
this.FrameCount = 0;
this.Flash = false;
this.TextString = "";
this.Length = 0;
this.Letter = new Array();
this.TextDraw = TextDraw;
this.TextSetLocation = TextSetLocation;
this.TextSetVisible = TextSetVisible;
this.TextGetVisible = TextGetVisible;
}
|
Handle drawing the characters of the string value of this class. JavaScript does not support drawing primitives so the characters are defined as image files and then instances of the image files are created in the DIV area of the application. Displaying the character values involves changing the IMG tag properties of the characters.
REF: 5.1
|
function TextDraw()
{
var DigitCount;
if (this.Active == true)
{
/********************************************/
/* Display only for given number of frames. */
/********************************************/
if (this.FrameCount != this.INFINATE_FRAMES)
--this.FrameCount;
if (this.FrameCount == false && this.Flash == true)
{
this.FrameCount = this.Frames;
this.FlashVisible = !this.FlashVisible;
}
else if (this.FrameCount == false)
{
this.Active = false;
}
/****************************/
/* Plot Text current value. */
/****************************/
for (DigitCount = 0; DigitCount < this.Length; ++DigitCount)
{
if (this.TextString[DigitCount] >= '!'
&& this.TextString[DigitCount] <= 'Z')
{
/************************/
/* Erase previous Text. */
/************************/
if (this.Active == false
|| this.Visible == false
|| this.FlashVisible == false)
{
document.getElementById(this.Name + "_"
+ DigitCount).style.visibility = "hidden";
}
else
/**************************/
/* Draw the current Text. */
/**************************/
{
document.getElementById(this.Name + "_"
+ DigitCount).style.visibility = "visible";
}
}
}
}
}
|
Initialize the class. Use an array of image names to define characters which can be displayed by the class, the values are in the same order as they appear in the ASCII table of characters. Then create an instance of an IMG tag for each character to be displayed. The following images are used:
REF: 5.2
|
function TextSetLocation(WindowID, NewName,
NewxOffset, NewyOffset,
NewFrames, NewFlash,
NewText, NewScale)
{
var Count;
var DigitCount;
var xOffsetOrig;
this.Name = NewName;
xOffsetOrig = NewxOffset;
this.xOffset = NewxOffset;
this.yOffset = NewyOffset;
this.Frames = NewFrames;
this.Flash = NewFlash;
this.TextString = NewText.toUpperCase();
this.Length = this.TextString.length;
this.Scale = NewScale;
// !
this.Letter[0] = "AsteroidsJS/images/exclamation.gif";
// "
this.Letter[1] = "AsteroidsJS/images/quote.gif";
// #
this.Letter[2] = "AsteroidsJS/images/hash.gif";
// $
this.Letter[3] = "AsteroidsJS/images/dollar.gif";
// %
this.Letter[4] = "AsteroidsJS/images/percent.gif";
// &
this.Letter[5] = "AsteroidsJS/images/ampersand.gif";
// '
this.Letter[6] = "AsteroidsJS/images/apostrophe.gif";
// (
this.Letter[7] = "AsteroidsJS/images/openbracket.gif";
// )
this.Letter[8] = "AsteroidsJS/images/closebracket.gif";
// *
this.Letter[9] = "AsteroidsJS/images/asterisk.gif";
// +
this.Letter[10] = "AsteroidsJS/images/plus.gif";
// ,
this.Letter[11] = "AsteroidsJS/images/comma.gif";
// -
this.Letter[12] = "AsteroidsJS/images/minus.gif";
// .
this.Letter[13] = "AsteroidsJS/images/fullstop.gif";
// /
this.Letter[14] = "AsteroidsJS/images/slash.gif";
// 0
this.Letter[15] = "AsteroidsJS/images/0.gif";
// 1
this.Letter[16] = "AsteroidsJS/images/1.gif";
// 2
this.Letter[17] = "AsteroidsJS/images/2.gif";
// 3
this.Letter[18] = "AsteroidsJS/images/3.gif";
// 4
this.Letter[19] = "AsteroidsJS/images/4.gif";
// 5
this.Letter[20] = "AsteroidsJS/images/5.gif";
// 6
this.Letter[21] = "AsteroidsJS/images/6.gif";
// 7
this.Letter[22] = "AsteroidsJS/images/7.gif";
// 8
this.Letter[23] = "AsteroidsJS/images/8.gif";
// 9
this.Letter[24] = "AsteroidsJS/images/9.gif";
// :
this.Letter[25] = "AsteroidsJS/images/colon.gif";
// ;
this.Letter[26] = "AsteroidsJS/images/semicolon.gif";
// <
this.Letter[27] = "AsteroidsJS/images/less.gif";
// =
this.Letter[28] = "AsteroidsJS/images/equal.gif";
// >
this.Letter[29] = "AsteroidsJS/images/grater.gif";
// ?
this.Letter[30] = "AsteroidsJS/images/question.gif";
// @
this.Letter[31] = "AsteroidsJS/images/at.gif";
// A
this.Letter[32] = "AsteroidsJS/images/A.gif";
// B
this.Letter[33] = "AsteroidsJS/images/B.gif";
// C
this.Letter[34] = "AsteroidsJS/images/C.gif";
// D
this.Letter[35] = "AsteroidsJS/images/D.gif";
// E
this.Letter[36] = "AsteroidsJS/images/E.gif";
// F
this.Letter[37] = "AsteroidsJS/images/F.gif";
// G
this.Letter[38] = "AsteroidsJS/images/G.gif";
// H
this.Letter[39] = "AsteroidsJS/images/H.gif";
// I
this.Letter[40] = "AsteroidsJS/images/I.gif";
// J
this.Letter[41] = "AsteroidsJS/images/J.gif";
// K
this.Letter[42] = "AsteroidsJS/images/K.gif";
// L
this.Letter[43] = "AsteroidsJS/images/L.gif";
// M
this.Letter[44] = "AsteroidsJS/images/M.gif";
// N
this.Letter[45] = "AsteroidsJS/images/N.gif";
// O
this.Letter[46] = "AsteroidsJS/images/O.gif";
// P
this.Letter[47] = "AsteroidsJS/images/P.gif";
// Q
this.Letter[48] = "AsteroidsJS/images/Q.gif";
// R
this.Letter[49] = "AsteroidsJS/images/R.gif";
// S
this.Letter[50] = "AsteroidsJS/images/S.gif";
// T
this.Letter[51] = "AsteroidsJS/images/T.gif";
// U
this.Letter[52] = "AsteroidsJS/images/U.gif";
// V
this.Letter[53] = "AsteroidsJS/images/V.gif";
// W
this.Letter[54] = "AsteroidsJS/images/W.gif";
// X
this.Letter[55] = "AsteroidsJS/images/X.gif";
// Y
this.Letter[56] = "AsteroidsJS/images/Y.gif";
// Z
this.Letter[57] = "AsteroidsJS/images/Z.gif";
for (DigitCount = 0; DigitCount < this.Length; ++DigitCount)
{
if (this.TextString[DigitCount] == '\n')
{
this.xOffset = xOffsetOrig;
this.yOffset += 80 * this.Scale;
}
else if (this.TextString[DigitCount] >= '!' && this.TextString[DigitCount] <= 'Z')
{
var NewElement = document.createElement("img");
document.getElementById(WindowID).appendChild(NewElement);
NewElement.id = this.Name + "_" + DigitCount;
NewElement.style.visibility = "hidden";
NewElement.style.position = "absolute";
NewElement.style.left = this.xOffset;
NewElement.style.top = this.yOffset;
NewElement.style.width = 64 * this.Scale;
NewElement.style.height = 64 * this.Scale;
NewElement.src = this.Letter[this.TextString.charCodeAt(DigitCount)
- '!'.charCodeAt(0)];
this.xOffset += 80 * this.Scale;
}
else
this.xOffset += 80 * this.Scale;
}
}
|
Though JavaScript does not appear to have a concept of private class variables, still handle requests to get and set member variables in a controlled fashion.
REF: 5.3
|
function TextSetVisible(NewVisible)
{
if (NewVisible != this.LastVisible)
{
this.Visible = NewVisible;
this.LastVisible = this.Visible;
this.FlashVisible = this.Visible;
this.Active = true;
this.FrameCount = this.Frames;
}
}
function TextGetVisible()
{
return this.Active;
}
|
|
In JavaScript a class has a constructor in which the classes instance variables are created by referring to them for the first time and initializing them with values. Constant declarations are handled in the same way as variable declarations. Then the member functions of the class are initialized in the same way as the member variables, which isn't very tidy as the function names need to be kept unique across all classes in the application.
REF: 6.0
|
// Asteroids Clone JavaScript
// Copyright (C) 1997 Jason Birch
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
/**********************/
/* Object Constructor */
/**********************/
function AstroRock()
{
this.ROCK_WIDTH = 64;
this.ROCK_HEIGHT = 64;
this.ERASE = 3;
this.INACTIVE = 4;
this.NEW_POSITION = -1;
this.HYPERSPACE = -500;
this.Name = "";
this.xMax = 0;
this.yMax = 0;
this.Size = this.INACTIVE;
this.xOffset = this.HYPERSPACE;
this.yOffset = this.HYPERSPACE;
this.xVelocity = 0;
this.yVelocity = 0;
this.AstroRockDraw = AstroRockDraw;
this.AstroRockMove = AstroRockMove;
this.AstroRockSetArea = AstroRockSetArea;
this.AstroRockCollide = AstroRockCollide;
this.AstroRockDestroy = AstroRockDestroy;
this.AstroRockGetSize = AstroRockGetSize;
this.AstroRockGetXOffset = AstroRockGetXOffset;
this.AstroRockGetYOffset = AstroRockGetYOffset;
this.AstroRockCreate = AstroRockCreate;
}
|
Handle drawing the object of this class. JavaScript does not support drawing primitives so the object is defined as image files and then instances of the image files are created in the DIV area of the application. Displaying the object involves changing the IMG tag properties of the object.
REF: 6.1
|
function AstroRockDraw()
{
if (this.Size < this.INACTIVE)
{
if (this.Size == this.ERASE)
{
this.Size = this.INACTIVE;
document.getElementById(this.Name).style.visibility = "hidden";
}
if (this.Size < this.INACTIVE)
{
document.getElementById(this.Name).style.top = this.yOffset;
document.getElementById(this.Name).style.left = this.xOffset;
document.getElementById(this.Name).style.visibility = "visible";
}
}
}
|
Move the object of this instance of this class by one animation frame.
REF: 6.2
|
function AstroRockMove()
{
if (this.Size < this.INACTIVE)
{
if (this.xOffset < 0 - this.ROCK_WIDTH)
this.xOffset = this.xMax + this.ROCK_WIDTH;
else if (this.xOffset > this.xMax + this.ROCK_WIDTH)
this.xOffset = 0 - this.ROCK_WIDTH;
if (this.yOffset < 0 - this.ROCK_HEIGHT)
this.yOffset = this.yMax + this.ROCK_HEIGHT;
else if (this.yOffset > this.yMax + this.ROCK_HEIGHT)
this.yOffset = 0 - this.ROCK_HEIGHT;
this.xOffset += this.xVelocity;
this.yOffset += this.yVelocity;
}
}
|
Initialize the object to a random position for creating a new object or to a specific location when manipulating an existing object. The following images are used:
REF: 6.3
|
function AstroRockSetArea(Width, Height, NewxOffset, NewyOffset, NewSize)
{
this.xMax = Width;
this.yMax = Height;
if (NewxOffset == this.NEW_POSITION)
{
this.Size = 0;
if (Math.floor(Math.random() * 2) == 0)
{
this.xOffset = Math.random() * this.xMax;
this.yOffset = this.yMax * Math.floor(Math.random() * 2);
}
else
{
this.yOffset = Math.random() * this.yMax;
this.xOffset = this.xMax * Math.floor(Math.random() * 2);
}
}
else
{
this.Size = NewSize;
this.xOffset = NewxOffset;
this.yOffset = NewyOffset;
}
do
{
this.xVelocity = Math.floor(Math.random()*6*(this.Size+1) - 3*(this.Size+1));
this.yVelocity = Math.floor(Math.random()*6*(this.Size+1) - 3*(this.Size+1));
} while(this.xVelocity == 0 || this.yVelocity == 0);
document.getElementById(this.Name).src = "AsteroidsJS/images/AstroRock_"
+ this.Size + ".gif"
}
|
Check this instance of an object for a collision with a specific location.
REF: 6.4
|
function AstroRockCollide(xPos, yPos, Width, Height)
{
var Collision = false;
if (this.Size < this.INACTIVE)
{
if ((Collision = xPos + Width > this.xOffset
&& xPos < this.xOffset + this.ROCK_WIDTH / (this.Size*this.Size+1)
&& yPos + Height > this.yOffset
&& yPos < this.yOffset + this.ROCK_HEIGHT / (this.Size*this.Size+1)) == true)
{
++this.Size;
document.getElementById(this.Name).src = "AsteroidsJS/images/AstroRock_"
+ this.Size + ".gif"
do
{
this.xVelocity = Math.floor(Math.random()*6*(this.Size+1) - 3*(this.Size+1));
this.yVelocity = Math.floor(Math.random()*6*(this.Size+1) - 3*(this.Size+1));
} while(this.xVelocity == 0 || this.yVelocity == 0);
}
}
return Collision;
}
|
Though JavaScript does not appear to have a concept of private class variables, still handle requests to get and set member variables in a controlled fashion.
REF: 6.5
|
function AstroRockDestroy()
{
this.Size = this.ERASE;
this.xOffset = this.HYPERSPACE;
this.yOffset = this.HYPERSPACE;
}
function AstroRockGetSize()
{
return this.Size;
}
function AstroRockGetXOffset()
{
return this.xOffset;
}
function AstroRockGetYOffset()
{
return this.yOffset;
}
|
Arguments can not be passed into a constructor of a JavaScript class, so class initialization sometimes needs to be a two step process.
REF: 6.6
|
function AstroRockCreate(WindowID, NewName)
{
this.Name = NewName;
var NewElement = document.createElement("img");
document.getElementById(WindowID).appendChild(NewElement);
NewElement.id = this.Name;
NewElement.style.visibility = "visible";
NewElement.style.position = "absolute";
NewElement.style.left = this.xOffset;
NewElement.style.top = this.yOffset;
NewElement.src = "AsteroidsJS/images/AstroRock_1.gif";
}
|
|
This is the most complex class of the application. In JavaScript a class has a constructor in which the classes instance variables are created by referring to them for the first time and initializing them with values. Constant declarations are handled in the same way as variable declarations. Then the member functions of the class are initialized in the same way as the member variables, which isn't very tidy as the function names need to be kept unique across all classes in the application.
REF: 7.0
|
// Asteroids Clone JavaScript
// Copyright (C) 1997 Jason Birch
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
/**********************/
/* Object Constructor */
/**********************/
function AstroShip()
{
var Count;
this.FRAMES = 63;
this.SHIP_WIDTH = 25;
this.SHIP_HEIGHT = 25;
this.SHIP_START_ANGLE = 36;
this.MAX_EXPLODE_FRAME = 40;
this.LIFE_XGAP = 30;
this.LIFE_XOFFSET = 125;
this.LIFE_YOFFSET = 20;
this.LIVES_HEIGHT = 34;
this.LIVES_WIDTH = 100;
this.MAX_LIVES = 3;
this.SCORE_XOFFSET = 20;
this.SCORE_YOFFSET = 20;
this.SCORE_SCALE = 0.2;
this.MAX_SHOTS = 10;
this.HYPERSPACE = -500;
this.HYPER_FRAMES = 20;
this.Name = "";
this.Scale = 1;
this.OneDegree = 6.3 / 360;
this.FrameStep = 0.1;
this.Lives = this.MAX_LIVES;
this.Crash = false;
this.ThrustFlag = false;
this.Fade = 0;
this.xMax = 0;
this.yMax = 0;
this.Angle = this.SHIP_START_ANGLE;
this.xOffset = this.HYPERSPACE;
this.yOffset = this.HYPERSPACE;
this.ShotIndex = 0;
this.xVelocity = 0;
this.yVelocity = 0;
this.ExplodeFrame = 0;
this.HyperCount = false;
this.PlayerScore = new Digit();
this.Shots = new Array();
for (Count = 0; Count < this.MAX_SHOTS; ++Count)
this.Shots[Count] = new AstroShot();
this.AstroShipDraw = AstroShipDraw;
this.AstroShipIncAngle = AstroShipIncAngle;
this.AstroShipThrust = AstroShipThrust;
this.AstroShipShoot = AstroShipShoot;
this.AstroShipMove = AstroShipMove;
this.AstroShipReset = AstroShipReset;
this.AstroShipSetArea = AstroShipSetArea;
this.AstroShipHyperspace = AstroShipHyperspace;
this.AstroShipCollide = AstroShipCollide;
this.AstroShipSetCrash = AstroShipSetCrash;
this.AstroShipGetCrash = AstroShipGetCrash;
this.AstroShipGetXOffset = AstroShipGetXOffset;
this.AstroShipGetYOffset = AstroShipGetYOffset;
this.AstroShipGetWidth = AstroShipGetWidth;
this.AstroShipGetHeight = AstroShipGetHeight;
this.AstroShipSetScore = AstroShipSetScore;
this.AstroShipGetScore = AstroShipGetScore;
this.AstroShipGetLives = AstroShipGetLives;
this.AstroShipGetShot = AstroShipGetShot;
this.AstroShipGetShotCount = AstroShipGetShotCount;
}
|
Handle drawing the object of this class. JavaScript does not support drawing primitives so the object is defined as image files and then instances of the image files are created in the DIV area of the application. Displaying the object involves changing the IMG tag properties of the object. In addition an animated gif is used to show when the ship is thrusting and when the ship has collided. This function also displays the player score and number of remaining lives.
REF: 7.1
|
function AstroShipDraw()
{
var Count;
var LifeCount;
for (Count = 0; Count < this.MAX_SHOTS; ++Count)
this.Shots[Count].AstroShotDraw();
if (this.Lives != false || this.ExplodeFrame != this.MAX_EXPLODE_FRAME)
{
/*********************/
/* Draw intact ship. */
/*********************/
if (this.Crash == false)
{
/********************************/
/* Plot ships current position. */
/********************************/
document.getElementById(this.Name).style.visibility = "visible";
document.getElementById(this.Name).style.top = this.yOffset;
document.getElementById(this.Name).style.left = this.xOffset;
document.getElementById(this.Name).src = "AsteroidsJS/images/AstroShip_0"
+ this.Angle + ".gif";
document.getElementById(this.Name + "_Explode").style.visibility = "hidden";
if (this.ExplodeFrame >= this.ERASE_FRAME)
{
this.ExplodeFrame = false;
/************************************/
/* Erase previous position of ship. */
/************************************/
document.getElementById(this.Name).style.visibility = "hidden";
}
/*****************************************/
/* Add thrust point if currently active. */
/*****************************************/
if (this.ThrustFlag == true)
{
document.getElementById(this.Name + "_Thrust").style.top =
this.yOffset + (15 * Math.cos(this.FrameStep*this.Angle
+ this.OneDegree*160)) + 6;
document.getElementById(this.Name + "_Thrust").style.left =
this.xOffset
+ (15 * Math.sin(this.FrameStep*this.Angle + this.OneDegree*160));
document.getElementById(this.Name + "_Thrust").style.visibility = "visible";
this.ThrustFlag = false;
}
else
{
/******************/
/* Remove thrust. */
/******************/
document.getElementById(this.Name + "_Thrust").style.visibility = "hidden";
}
}
else
{
if (this.ExplodeFrame == false)
{
--this.Lives;
/************************************/
/* Erase previous position of ship. */
/************************************/
document.getElementById(this.Name).style.visibility = "hidden";
/******************/
/* Remove thrust. */
/******************/
document.getElementById(this.Name + "_Thrust").style.visibility = "hidden";
}
/******************************************/
/* Draw the ship in the current position. */
/******************************************/
if (this.ExplodeFrame < this.MAX_EXPLODE_FRAME -1)
{
/********************************************/
/* Split polygon shape into seperate lines. */
/********************************************/
document.getElementById(this.Name + "_Explode").style.visibility = "visible";
document.getElementById(this.Name + "_Explode").style.top = this.yOffset;
document.getElementById(this.Name + "_Explode").style.left = this.xOffset;
}
/************************/
/* Reset for next life. */
/************************/
if (++this.ExplodeFrame == this.MAX_EXPLODE_FRAME)
document.getElementById(this.Name + "_Explode").style.visibility = "hidden";
if (this.Lives != false && this.ExplodeFrame == this.MAX_EXPLODE_FRAME)
{
this.ExplodeFrame = false;
this.Crash = false;
this.ThrustFlag = false;
this.Angle = this.SHIP_START_ANGLE;
this.xOffset = this.xMax / 2;
this.yOffset = this.yMax / 2;
this.xVelocity = 0;
this.yVelocity = 0;
}
}
/****************************/
/* Display remaining lives. */
/****************************/
for (LifeCount = 0; LifeCount < this.MAX_LIVES; ++LifeCount)
{
/******************************************/
/* Draw the ship in the current position. */
/******************************************/
if (this.Lives > LifeCount)
document.getElementById(this.Name + "_"
+ LifeCount).style.visibility = "visible";
else
document.getElementById(this.Name + "_"
+ LifeCount).style.visibility = "hidden";
}
}
/******************/
/* Redraw scores. */
/******************/
this.PlayerScore.DigitDraw();
}
|
Allow the player to rotate the ship clockwise and anti-clockwise.
REF: 7.2
|
function AstroShipIncAngle(Direction)
{
if (this.Crash == false)
{
if (Direction == false)
{
++this.Angle;
if (this.Angle >= this.FRAMES)
this.Angle = 0;
}
else
{
--this.Angle;
if (this.Angle < 0)
this.Angle = this.FRAMES-1;
}
}
}
|
Allow the player to thrust the ship.
REF: 7.3
|
function AstroShipThrust()
{
this.ThrustFlag = true;
this.xVelocity += Math.sin(this.FrameStep*this.Angle + this.OneDegree*-30);
this.yVelocity += Math.cos(this.FrameStep*this.Angle + this.OneDegree*-30);
}
|
Allow the player to produce multiple simultaneous shots from the ship.
REF: 7.4
|
function AstroShipShoot()
{
if (this.Crash == false)
{
this.Shots[this.ShotIndex].AstroShotSetArea(
this.xMax, this.yMax,
this.xOffset + 8, this.yOffset + 10,
this.xVelocity + 10*Math.sin(this.FrameStep*this.Angle - this.OneDegree*30),
this.yVelocity + 10*Math.cos(this.FrameStep*this.Angle - this.OneDegree*30));
if (++this.ShotIndex == this.MAX_SHOTS)
this.ShotIndex = 0;
}
}
|
Animate the ship a single frame.
REF: 7.5
|
function AstroShipMove()
{
var Count;
if (this.HyperCount != false)
{
--this.HyperCount;
if (this.HyperCount == false)
{
this.xOffset = Math.random() * (this.xMax - 2 * this.SHIP_WIDTH);
this.yOffset = Math.random() * (this.yMax - 2 * this.SHIP_HEIGHT);
}
}
for (Count = 0; Count < this.MAX_SHOTS; ++Count)
this.Shots[Count].AstroShotMove();
if (this.Crash == false)
{
if (this.xOffset < 0 - this.SHIP_WIDTH)
this.xOffset = this.xMax + this.SHIP_WIDTH;
else if (this.xOffset > this.xMax + this.SHIP_WIDTH)
this.xOffset = 0 - this.SHIP_WIDTH;
if (this.yOffset < 0 - this.SHIP_HEIGHT)
this.yOffset = this.yMax + this.SHIP_HEIGHT;
else if (this.yOffset > this.yMax + this.SHIP_HEIGHT)
this.yOffset = 0 - this.SHIP_HEIGHT;
this.xOffset += this.xVelocity;
this.yOffset += this.yVelocity;
}
}
|
Reset this instance of a ship when a new game is started.
REF: 7.6
|
function AstroShipReset()
{
if (this.Lives == false)
{
this.ExplodeFrame = false;
this.Crash = false;
this.ThrustFlag = false;
this.Fade = 0;
this.Angle = this.SHIP_START_ANGLE;
this.xOffset = this.xMax / 2;
this.yOffset = this.yMax / 2;
this.xVelocity = 0;
this.yVelocity = 0;
this.Lives = this.MAX_LIVES;
this.PlayerScore.DigitSetNumber(0);
}
}
|
Initialise an instance of the class, add IMG tags to the DIV play area for the ship graphics. The following images are used:
REF: 7.7
|
function AstroShipSetArea(WindowID, NewName, Width, Height, NewScale)
{
var Count;
var ThisXOffset;
this.Name = NewName;
this.Scale = NewScale;
this.xMax = Width;
this.yMax = Height;
this.xOffset = this.xMax / 2;
this.yOffset = this.yMax / 2;
this.PlayerScore.DigitSetLocation(WindowID, "Score",
this.SCORE_XOFFSET, this.SCORE_YOFFSET,
this.SCORE_SCALE);
ThisXOffset = this.xMax - this.LIFE_XOFFSET;
for (Count = 0; Count < this.MAX_LIVES; ++Count)
{
var NewElement = document.createElement("img");
document.getElementById(WindowID).appendChild(NewElement);
NewElement.id = this.Name + "_" + Count;
NewElement.style.visibility = "visible";
NewElement.style.position = "absolute";
NewElement.style.left = ThisXOffset;
NewElement.style.top = this.LIFE_YOFFSET;
NewElement.style.width = 64 * this.Scale;
NewElement.style.height = 90 * this.Scale;
NewElement.src = "AsteroidsJS/images/AstroShip.gif";
ThisXOffset += 90 * this.Scale;
}
var NewElement = document.createElement("img");
document.getElementById(WindowID).appendChild(NewElement);
NewElement.id = this.Name;
NewElement.style.visibility = "visible";
NewElement.style.position = "absolute";
NewElement.style.left = this.xOffset;
NewElement.style.top = this.yOffset;
NewElement.style.width = 64 * this.Scale;
NewElement.style.height = 90 * this.Scale;
NewElement.src = "AsteroidsJS/images/AstroShip_0" + this.Angle + ".gif";
var NewElement = document.createElement("img");
document.getElementById(WindowID).appendChild(NewElement);
NewElement.id = this.Name + "_Explode";
NewElement.style.visibility = "hidden";
NewElement.style.position = "absolute";
NewElement.style.left = this.xOffset;
NewElement.style.top = this.yOffset;
NewElement.style.width = 64 * this.Scale;
NewElement.style.height = 64 * this.Scale;
NewElement.src = "AsteroidsJS/images/AstroShipExplode.gif";
var NewElement = document.createElement("img");
document.getElementById(WindowID).appendChild(NewElement);
NewElement.id = this.Name + "_Thrust";
NewElement.style.visibility = "hidden";
NewElement.style.position = "absolute";
NewElement.style.left = this.xOffset;
NewElement.style.top = this.yOffset;
NewElement.style.width = 48 * this.Scale;
NewElement.style.height = 48 * this.Scale;
NewElement.src = "AsteroidsJS/images/Thrust.gif";
for (Count = 0; Count < this.MAX_SHOTS; ++Count)
this.Shots[Count].AstroShotSetName(WindowID, "ShipShot_" + Count)
}
|
Allow the player to hyperspace the ship.
REF: 7.8
|
function AstroShipHyperspace()
{
if (this.Crash == false && this.HyperCount == false)
{
this.HyperCount = this.HYPER_FRAMES;
this.xOffset = this.HYPERSPACE;
this.yOffset = this.HYPERSPACE;
this.xVelocity = 0;
this.yVelocity = 0;
}
}
|
Check if this instance of the ship has collided with a specific location.
REF: 7.9
|
function AstroShipCollide(xPos, yPos, Width, Height)
{
var Collision = false;
if (this.Crash == false)
if ((Collision = xPos + Width > this.xOffset
&& xPos < this.xOffset + this.SHIP_WIDTH
&& yPos + Height > this.yOffset
&& yPos < this.yOffset + this.SHIP_HEIGHT) == true)
this.Crash = true;
return Collision;
}
|
Allow various properties of this instance of the ship to be set and read.
REF: 7.10
|
function AstroShipSetCrash(NewCrash)
{
this.Crash = NewCrash;
}
function AstroShipGetCrash()
{
return this.Crash;
}
function AstroShipGetXOffset()
{
return this.xOffset;
}
function AstroShipGetYOffset()
{
return this.yOffset;
}
function AstroShipGetWidth()
{
return this.SHIP_WIDTH;
}
function AstroShipGetHeight()
{
return this.SHIP_HEIGHT;
}
function AstroShipGetScore()
{
return this.PlayerScore.DigitGetNumber();
}
function AstroShipSetScore(NewScore)
{
this.PlayerScore.DigitSetNumber(NewScore);
}
function AstroShipGetLives()
{
return this.Lives;
}
function AstroShipGetShot(ShotCount)
{
return this.Shots[ShotCount];
}
function AstroShipGetShotCount()
{
return this.MAX_SHOTS;
}
|
|
In JavaScript a class has a constructor in which the classes instance variables are created by referring to them for the first time and initializing them with values. Constant declarations are handled in the same way as variable declarations. Then the member functions of the class are initialized in the same way as the member variables, which isn't very tidy as the function names need to be kept unique across all classes in the application.
REF: 8.0
|
// Asteroids Clone JavaScript
// Copyright (C) 1997 Jason Birch
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
/**********************/
/* Object Constructor */
/**********************/
function AstroShot()
{
this.SMALL_SHOT = 0;
this.LARGE_SHOT = 1;
this.SMALL_SHOT_FRAMES = 40;
this.LARGE_SHOT_FRAMES = 20;
this.ERASE_FRAME = 1;
this.HYPERSPACE = -500;
this.Name = "";
this.xMax = 0;
this.yMax = 0;
this.xOffset = this.HYPERSPACE;
this.yOffset = this.HYPERSPACE;
this.xVelocity = 0;
this.yVelocity = 0;
this.Size = this.SMALL_SHOT;
this.FrameCount = 0;
this.AstroShotDraw = AstroShotDraw;
this.AstroShotMove = AstroShotMove;
this.AstroShotSetArea = AstroShotSetArea;
this.AstroShotGetXOffset = AstroShotGetXOffset;
this.AstroShotGetYOffset = AstroShotGetYOffset;
this.AstroShotDestroy = AstroShotDestroy;
this.AstroShotActive = AstroShotActive;
this.AstroShotSetName = AstroShotSetName;
}
|
Handle drawing the object of this class. JavaScript does not support drawing primitives so the object is defined as image files and then instances of the image files are created in the DIV area of the application. Displaying the object involves changing the IMG tag properties of the object.
REF: 8.1
|
function AstroShotDraw()
{
if (this.FrameCount != false)
{
if (this.FrameCount == this.ERASE_FRAME)
{
this.FrameCount = false;
document.getElementById(this.Name).style.visibility = "hidden";
}
if (this.FrameCount != false)
{
document.getElementById(this.Name).style.visibility = "visible";
document.getElementById(this.Name).style.top = this.yOffset;
document.getElementById(this.Name).style.left = this.xOffset;
}
}
}
|
Animate this instance of the object by a single frame.
REF: 8.2
|
function AstroShotMove()
{
if (this.FrameCount > this.ERASE_FRAME)
{
if (this.xOffset < 0)
this.xOffset = this.xMax;
else if (this.xOffset > this.xMax)
this.xOffset = 0;
if (this.yOffset < 0)
this.yOffset = this.yMax;
else if (this.yOffset > this.yMax)
this.yOffset = 0;
this.xOffset += this.xVelocity;
this.yOffset += this.yVelocity;
--this.FrameCount;
}
}
|
Initialize this instance of the object.
REF: 8.3
|
function AstroShotSetArea(NewxMax, NewyMax,
NewxOffset, NewyOffset,
NewxVelocity, NewyVelocity,
NewSize)
{
this.xMax = NewxMax;
this.yMax = NewyMax;
this.xOffset = NewxOffset;
this.yOffset = NewyOffset;
this.xVelocity = NewxVelocity;
this.yVelocity = NewyVelocity;
this.Size = NewSize;
if (this.Size == this.LARGE_SHOT)
{
this.FrameCount = this.LARGE_SHOT_FRAMES;
document.getElementById(this.Name).style.width = 8;
document.getElementById(this.Name).style.height = 8;
}
else
{
this.FrameCount = this.SMALL_SHOT_FRAMES;
document.getElementById(this.Name).style.width = 4;
document.getElementById(this.Name).style.height = 4;
}
}
|
Allow various properties of the object to be set and read.
REF: 8.4
|
function AstroShotGetXOffset()
{
return this.xOffset;
}
function AstroShotGetYOffset()
{
return this.yOffset;
}
function AstroShotDestroy()
{
this.FrameCount = this.ERASE_FRAME;
this.xOffset = this.HYPERSPACE;
this.yOffset = this.HYPERSPACE;
}
function AstroShotActive()
{
return (this.FrameCount != false);
}
|
As JavaScript constructors can not have arguments passed in, creating an instance of a class can sometimes be a two step process. The following image is used:
REF: 8.5
|
function AstroShotSetName(WindowID, NewName)
{
this.Name = NewName;
var NewElement = document.createElement("img");
document.getElementById(WindowID).appendChild(NewElement);
NewElement.id = this.Name;
NewElement.style.visibility = "visible";
NewElement.style.position = "absolute";
NewElement.style.left = this.xOffset;
NewElement.style.top = this.yOffset;
NewElement.style.width = 2;
NewElement.style.height = 2;
NewElement.src = "AsteroidsJS/images/AstroShot.gif";
}
|
|
In JavaScript a class has a constructor in which the classes instance variables are created by referring to them for the first time and initializing them with values. Constant declarations are handled in the same way as variable declarations. Then the member functions of the class are initialized in the same way as the member variables, which isn't very tidy as the function names need to be kept unique across all classes in the application.
REF: 9.0
|
// Asteroids Clone JavaScript
// Copyright (C) 1997 Jason Birch
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
/**********************/
/* Object Constructor */
/**********************/
function AstroUFO()
{
this.FRAMES = 2;
this.UFO_WIDTH = 16;
this.UFO_HEIGHT = 6;
this.INACTIVE = 3;
this.ERASE = 4;
this.HYPERSPACE = -500;
this.Name = "";
this.Scale = 1;
this.xMax = 0;
this.yMax = 0;
this.Size = this.INACTIVE;
this.xOffset = this.HYPERSPACE;
this.yOffset = this.HYPERSPACE;
this.xVelocity = 0;
this.yVelocity = 0;
this.Shot = new AstroShot();
this.AstroUFODraw = AstroUFODraw;
this.AstroUFOMove = AstroUFOMove;
this.AstroUFOSetArea = AstroUFOSetArea;
this.AstroUFOCollide = AstroUFOCollide;
this.AstroUFODestroy = AstroUFODestroy;
this.AstroUFOGetSize = AstroUFOGetSize;
this.AstroUFOGetXOffset = AstroUFOGetXOffset;
this.AstroUFOGetYOffset = AstroUFOGetYOffset;
this.AstroUFOGetShot = AstroUFOGetShot;
}
|
Handle drawing the object of this class. JavaScript does not support drawing primitives so the object is defined as image files and then instances of the image files are created in the DIV area of the application. Displaying the object involves changing the IMG tag properties of the object.
REF: 9.1
|
function AstroUFODraw()
{
this.Shot.AstroShotDraw();
if (this.Size != this.INACTIVE)
{
if (this.Size == this.ERASE)
{
this.Size = this.INACTIVE;
document.getElementById(this.Name).style.visibility = "hidden";
}
if (this.Size < this.INACTIVE)
{
document.getElementById(this.Name).style.visibility = "visible";
document.getElementById(this.Name).style.top = this.yOffset;
document.getElementById(this.Name).style.left = this.xOffset;
}
}
}
|
Animate a single frame of this instance of the object.
REF: 9.2
|
function AstroUFOMove()
{
var ShotDirection;
if (this.Shot.AstroShotActive() == true)
this.Shot.AstroShotMove();
if (this.Size != this.INACTIVE)
{
if (this.Shot.AstroShotActive() == false)
{
ShotDirection = Math.floor(Math.random() * 4);
switch (ShotDirection)
{
case 0:
{
this.Shot.AstroShotSetArea(this.xMax, this.yMax,
this.xOffset, this.yOffset,
-8, -8, AstroShot.LARGE_SHOT);
break;
}
case 1:
{
this.Shot.AstroShotSetArea(this.xMax, this.yMax,
this.xOffset, this.yOffset,
-8, +8, AstroShot.LARGE_SHOT);
break;
}
case 2:
{
this.Shot.AstroShotSetArea(this.xMax, this.yMax,
this.xOffset, this.yOffset,
+8, -8, AstroShot.LARGE_SHOT);
break;
}
case 3:
{
this.Shot.AstroShotSetArea(this.xMax, this.yMax,
this.xOffset, this.yOffset,
+8, +8, AstroShot.LARGE_SHOT);
break;
}
}
}
if (Math.floor(Math.random() * 10) == 0)
this.yVelocity = Math.random() * (this.Size+2)*10 - (this.Size+2)*5;
if (this.xOffset < 0 - this.UFO_WIDTH)
this.Size = this.INACTIVE;
else if (this.xOffset > this.xMax + this.UFO_WIDTH)
this.Size = this.INACTIVE;
if (this.yOffset < 0)
this.yOffset = 0;
else if (this.yOffset > this.yMax)
this.yOffset = this.yMax;
this.xOffset += this.xVelocity;
this.yOffset += this.yVelocity;
}
else if (Math.floor(Math.random() * 1000) == 0)
{
this.Size = Math.floor(Math.random() * this.FRAMES);
this.yOffset = Math.random() * this.yMax;
if (Math.floor(Math.random() * 2) == 0)
{
this.xOffset = 0 - this.UFO_WIDTH;
this.xVelocity = 3 * (2 - this.Size+1);
}
else
{
this.xOffset = this.xMax + this.UFO_WIDTH;
this.xVelocity = -3 * (2 - this.Size+1);
}
this.yVelocity = Math.random() * 5 - 3;
}
}
|
Initialise an instance of the class, add IMG tags to the DIV play area for the UFO graphics. The following image is used:
REF: 9.3
|
function AstroUFOSetArea(WindowID, NewName, Width, Height, NewScale)
{
this.Name = NewName;
this.Scale = NewScale;
this.xMax = Width;
this.yMax = Height;
var NewElement = document.createElement("img");
document.getElementById(WindowID).appendChild(NewElement);
NewElement.id = this.Name;
NewElement.style.visibility = "visible";
NewElement.style.position = "absolute";
NewElement.style.left = this.xOffset;
NewElement.style.top = this.yOffset;
NewElement.style.width = 64 * this.Scale;
NewElement.style.height = 90 * this.Scale;
NewElement.src = "AsteroidsJS/images/AstroUFO.gif";
this.Shot.AstroShotSetName(WindowID, "UFOShot")
}
|
Check if this instance of the object has collided with a specific location.
REF: 9.4
|
function AstroUFOCollide(xPos, yPos, Width, Height)
{
var Collision = false;
if (this.Size < this.INACTIVE)
if ((Collision = xPos + Width > this.xOffset
&& xPos < this.xOffset + this.UFO_WIDTH / (this.Size*this.Size+1)
&& yPos + Height > this.yOffset
&& yPos < this.yOffset + this.UFO_HEIGHT / (this.Size*this.Size+1)) == true)
this.AstroUFODestroy();
return Collision;
}
|
Allow various properties of this instance of the object to be set and read.
REF: 9.5
|
function AstroUFODestroy()
{
this.Size = this.ERASE;
this.xOffset = this.HYPERSPACE;
this.yOffset = this.HYPERSPACE;
}
function AstroUFOGetSize()
{
return this.Size;
}
function AstroUFOGetXOffset()
{
return this.xOffset;
}
function AstroUFOGetYOffset()
{
return this.yOffset;
}
function AstroUFOGetShot()
{
return this.Shot;
}
|
|
|