Next page | Contents page |

Outline for video games

Using the information from the previous few pages (events onwards), here is a skeletal outline for making video games using only plain JavaScript in an HTML page.

index.html


<!DOCTYPE html>
<html>
<head>
  <title>EXAMPLE</title>
  <meta charset="UTF-8">
  <meta name="viewport" 
           content="width=device-width, initial-scale=1.0">
  <style type="text/css">
  <!-- Use separate .css file if many styles -->
body {font-family:sans-serif;font-size:medium;padding:0}
#buttons, #title {float:left}
#title {background:#00f;color:#ff0;font-weight:bold;width:200px;
        padding:6px 16px;text-align:center}
  </style>
</head>
<body onload="run()">
  <canvas id="view"></canvas>
  <div id="title">EXAMPLE v0.1</div>
  <div id="buttons"></div>
  <div id="message"><strong>LOADING...</strong></div>
<script type="text/javascript" src="js/utilities.js"></script>
<script type="text/javascript" src="js/type1.js"></script>
<script type="text/javascript" src="js/type2.js"></script>
<!-- ... as many object types as needed -->
<script type="text/javascript" src="js/main.js"></script>
</body>
</html>

main.js


const WORLD = {}; // 1 global object

function run () // onload
{
  // Clear "loading":
  document.getElementById ("message").innerHTML = "";
  WORLD.canvas = document.getElementById ("view");
  WORLD.canvas.width = window.innerWidth - 20; // No scroll bar
  WORLD.canvas.height = window.innerHeight - 120; // Button space
  WORLD.canvas.context = canvas.getContext ("2d"); // For drawing
  WORLD.canvas.addEventListener ("click", handleClick);
  createButtons (); // With click listeners. In utilities.js
  document.addEventListener ("keypress", handleKey);
  buildWorld ();
  animate ();
}

function handleClick (event)
{
  var rect = WORLD.canvas.getBoundingClientRect ();
  var mouseX = event.clientX - rect.left;
  var mouseY = event.clientY - rect.top;
  // ... whatever actions needed
}

function handleKey (event)
{
  switch (event.key)
  {
  // ... whatever actions for each key
  }
}

function buildWorld ()
{
  WORLD = new World (); // In world.js - one of the typeX scripts
  WORLD.eye = new Viewer (10, -20, 5, 0);
  // Another type: Viewer = eye or camera at (x, y, z, facing)
  WORLD.animIds = []; // See animate (), below
  // ... Add objects of various types to the WORLD object
}

function animate () // This is the "game loop"
{
  // In case there are many requests outstanding because this
  // function takes longer than screen refresh rate (eg, 16.7ms)
  // Without this, mouse or key actions become jerky
  for (var i = 0; i < WORLD.animIds.length; i++)
  { cancelAnimationFrame (WORLD.animIds [i]);
    WORLD.animIds = [];
  }

  WORLD.update (); // In world.js - objects move/act
  WORLD.draw (); // In world.js - as seen by WORLD.eye
  WORLD.animIds.push (requestAnimationFrame (animate));
  // Causes display. NB: Execution ends here until
  // requestAnimationFrame calls animate() again (like 
  // setTimeout but time determined by screen hardware)
}

The following is an example of what I mean by the scripts for various object types. This is a very simple type that you are likely to need when making 3D games. Having a separate script file for each object type makes for a manageable structure when the program becomes large. It also makes it possible to control browser caching so that if you only change a couple of files the client does not have to download everything again.

point3D.js


function Point3D (x, y, z) // Constructor
{ this.x = x; this.y = y; this.z = z; }

Point3D.prototype.offset = function (dx, dy, dz) 
{ this.x += dx; this.y += dy; this.z += dz; };

Point3D.prototype.distance = function (otherPt)
{ var dx = otherPt.x - this.x;
  var dy = otherPt.y - this.y;
  var dz = otherPt.z - this.z;
  return Math.sqrt (dx * dx + dy * dy + dz * dz);
};

Point3D.prototype.distanceAndAngles = function (otherPt)
{ var dx = otherPt.x - this.x;
  var dy = otherPt.y - this.y;
  var dz = otherPt.z - this.z;
  var dx2dy2 = dx * dx + dy * dy;
  var distance = Math.sqrt (dx2dy2 + dz * dz);
  var bearingRad = Math.atan2 (dx, dy); // clockwise from N
  var altitudeRad = Math.atan2 (dz, Math.sqrt (dx2dy2));
  return {d:distance, b:bearingRad, a:altitudeRad};
};

/* Get vector from otherPt to this */
Point3D.prototype.difference = function (otherPt)
{ return new Vector3D (this.x - otherPt.x,
    this.y - otherPt.y, this.z - otherPt.z); };

Point3D.prototype.toString = function ()
{ return '(' + this.x.toFixed (1) + ', ' + this.y.toFixed (1) + 
    ', ' + this.z.toFixed (1) + ')'; };

Point3D.prototype.clone = function ()
{ return new Point3D (this.x, this.y, this.z); };

I have indicated some of the other object types you will need, such as

If you would like to see what is possible in a fully developed version of all this, without using any other frameworks, have a look at my 3D demonstration game, The Green.

When using multiple script files, the JavaScript standard now includes a concept of modules. In my humble opinion they are an unnecessary complication but you can read about them on Mozilla's site.

Next page | Contents page |