19.11

THREE.Basic Third Person Gamestarter kit for 3D WebGL games

When I was starting the design for this website I also had the idea of doing a 3D game which would come in the same look as the whole page. This game is now available here and during the development, the idea for a small framework came up. I decided to release the final boilerplate as THREE.BasicThirdPersonGame. It’s finally an open-source project whose code is available on GitHub.

THREE.BasicThirdPersonGame - starter kit for 3D WebGL games

As you can guess, the micro-framework and the platforms game are based on THREE.js and also Cannon.js. While THREE.js is one of the most popular fully-featured WebGL 3D engines out there right now, Cannon.js may be less familiar to you: it’s a simple, fast and lightweight physics engine that provides you interesting features like rigid bodies, friction, restitution, a real-time collision detection and constraints. In this article I just want to give an rough overview of THREE.BasicThirdPersonGame but if you want to dive directly into the details, just head over to the documentation or the GitHub page.

So THREE.BasicThirdPersonGame is a set of JavaScript files which form the starting point for a 3D game using a third-person camera system. Each file is responsible for a specific part of the game logic whereas the heart of the game code is placed in game.core.js. A short overview of the module files:

  • game.three.js: scene, rendering, 3D models, Cannon.js helpers
  • game.cannon.js: world, rigid body management
  • game.core.js: game loop, player logic, level logic, module imports
  • game.events.js: input system for keyboard controls
  • game.helpers.js: math conversions, general helpers
  • game.ui.js: user interface
  • game.static.js: static constants (color values)

The game.core.js file is the central place for all the game logic. This file contains the whole player and level structure including all needed properties. The player’s acceleration and rotation as well as the camera movement are defined here. Before we look at a compressed version of this file, I will show how to create a new game. So the following lines will initialize the game core and finally start the game. They can be placed in your HTML file after the game script files or in a separate JavaScript file.

if (!Detector.webgl) {
	Detector.addGetWebGLMessage();
} else {
	window.gameInstance = window.game.core();
	window.gameInstance.init({
		domContainer: document.querySelector("#game"),
		rendererClearColor: 0xffffff
	});
}

The init function accepts three optional parameters: one for the target DOM container, one for the background color of THREE’s renderer and you could also pass cameraSizeConstraint to set the camera’s limits inside the window. If the domContainer option is omitted, THREE.BasicThirdPersonGame will append a div to the body element.

Let’s have a brief look at the _game.init function itself: it initializes the main components (THREE.js, Cannon.js, events, user interface), creates the player and level and starts the game loop.

init: function(options) {
	_game.initComponents(options);

	_game.player.create();
	_game.level.create();

	_game.loop();
}

Where there is a game start there also needs to be a game end or game over. This event is handled in the _game.destroy method: the game loop is temporarily stopped and the Cannon.js physics world plus THREE’s scene is being destroyed and recreated again. Afterwards a level reset mechanism comes into play – the player and level objects were cloned via cloneObject at the end of the game.core.js file and now they are used for the recreation of the player and level. After this reset, the create functions are called again and the game loop continues.

destroy: function() {
	window.cancelAnimationFrame(_animationFrameLoop);

	_cannon.destroy();
	_cannon.setup();
	_three.destroy();
	_three.setup();

	_game.player = window.game.helpers.cloneObject(_gameDefaults.player);
	_game.level = window.game.helpers.cloneObject(_gameDefaults.level);

	_game.player.create();
	_game.level.create();

	_game.loop();
}

Before heading to the contents of the game.core.js, let’s have a brief look at the game loop. The game loop or main loop of the game is the place where the information processing and scene rendering come together to generate a visual output. The function calls inside the loop look like this:

loop: function() {
	_animationFrameLoop = window.requestAnimationFrame(_game.loop);
	_cannon.updatePhysics();
	_game.player.update();
	_three.render();
}

The speed of the game loop will be determined by the browser’s requestAnimationFrame method and will usually result in a framerate of 60 fps. While updatePhysics does nothing special but synchronizing the bodies' and visuals' positions and quaternions, it’s worth having a quick look at _game.player.update.

update: function() {
	_game.player.processUserInput();
	_game.player.accelerate();
	_game.player.rotate();
	_game.player.updateCamera();
}

As you can see the methods are self explaining: we process the user input (pressed keys) and move the player by using acceleration and rotation. Finally the third-person camera is updated. Inside the update function you would include more game logic later for your final game like checking for game over or updating the user interface (UI).

So finally we can look at the main contents of game.core.js: some unimportant attributes have been left so you can see what’s mainly inside the file.

window.game.core = function () {
	var _game = {
		// Attributes
		player: {
			// Attributes
			speed: 2,
			speedMax: 65,
			rotationSpeed: 0.007,
			rotationSpeedMax: 0.040,
			damping: 0.9,
			rotationDamping: 0.8,
			cameraOffsetH: 280,
			cameraOffsetV: 180,

			// Methods
			create: function() {},
			update: function() {},
			updateCamera: function() {},
			updateAcceleration: function() {},
			processUserInput: function() {},
			accelerate: function() {},
			rotate: function() {},
			jump: function() {},
			updateOrientation: function() {}
		},
		level: {
			// Methods
			create: function() {}
		},

		// Methods
		init: function() {},
		destroy: function() {},
		loop: function() {},
		initComponents: function () {}
	};

	return _game;
};

This is the place where all the player attributes are stored. Beside the settings for acceleration and rotation, the horizontal and vertical offset of the camera to the player are set here. Finally the _game.level.create method is where your game development will usually start. Since the player and the camera get updated frequently, you may start writing procedural level code (like in the platforms demo game) or import level data to your game world. THREE.BasicThirdPersonGame is meant to be a basic boilerplate for your third-person WebGL game so if you want to add some features, just use GitHub and create a fork to do so.

Lastly you may have a look at the examples where you will find some common scenarios for a game. Have fun!