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.