JSFiddle - React, Tailwind, and code Playground
by Gwyn Milcote
HTML
<h2>Creating a Spyro-like platform game in three.js</h2>
<p>The original <b>Spyro the Dragon</b> is my favourite game ever. The controls are very simple, the big open-world environments are beautiful, there are no time limits or annoying minigames... who doesn't like gliding and flying around a fantasy world?</p>
<p>Until recently, making 3D games required fancy software like Unity, and programming languages I don't know and can't be arsed to learn. But then a JavaScript library called <a href='https://threejs.org/'><b>three.js</b></a> appeared, and you can do some pretty cool stuff with it, in the browser - no downloads required, and if you have basic knowledge of JS it's easy to start playing with. I'm not sure if it's powerful enough to run a full game with big detailed environments, but making a simple Spyro-style demo would be a fun learning project, and satisfy my curiosity: "how difficult is this 3D stuff?"</p>
<p>The code is public and I'll write about each part. Maybe some three.js newbies will find it helpful? If you have more experience and see any crappy code that could be improved, please do let me know.</p>
<p>One problem with three.js is the huge number of old versions, and websites with outdated information and code that won't work any more (looking at you, ancient StackOverflow answers). Finding correct info can be a bloody pain!<br>This project started in February 2021 and uses the <a href='https://github.com/mrdoob/three.js/tree/dev/build'>latest version</a> directly from github, so you can be sure it's up-to-date.</p>
<h2>Chapters</h2>
<ul>
<li>
<a href='https://jsfiddle.net/bufforpington/3bmtf24o/latest'><b>Basics</b> - setting up a scene<br></a>
Lights, camera... not much action yet! Just a visible cube and a variety of scenery objects.
</li>
<li>
<b>Movement</b> - move cube with arrow keys<br>
Camera follows the cube. Use the S key for faster 'sprinting mode'.
</li>
<li>
...
JavaScript
/*
* This object acts a bit like a database.
* All level data, gems, enemies are looked up here.
*/
let game = {
/*
* Types of enemy that can be encountered,
* and how they can be defeated.
*
* Can reuse same mesh but different textures (palette swapping).
*
* Ouch 1 means they can hurt you, take 1 hitpoint.
* Ouch 2 means insta-death if they hit you.
*/
enemyTypes: [
{name: "HarmlessGnorc", mesh: 1, texture: 1, weakness: 1, ouch: 0},
{name: "PunchGnorc", mesh: 1, texture: 2, weakness: 1, ouch: 1},
{name: "BigGnorc", mesh: 2, texture: 1, weakness: 2, ouch: 1},
{name: "ArmourGnorc", mesh: 2, texture: 2, weakness: 3, ouch: 0},
{name: "TankGnorc", mesh: 2, texture: 3, weakness: 4, ouch: 0},
{name: "PowerGnorc", mesh: 3, texture: 1, weakness: 5, ouch: 1},
{name: "DangerGnorc", mesh: 3, texture: 2, weakness: 1, ouch: 2}
],
/*
* Just for reference.
*/
enemyWeakness: [
"This enemy can be flamed or charged once.",
"This enemy can't be charged, but can be flamed.",
"This enemy is fireproof, but can be charged.",
"This enemy needs to be charged to remove armour, and then flamed.",
"This enemy is fireproof and chargeproof. Might need a powerup or something to damage them.",
"There's no way to get rid of this enemy at all. How annoying. A cutscene might kill them."
],
levels: [
{
name: "Sunny Field",
music: 2,
startX: 5, startY: 9, startZ: 10,
/*
* ID numbers of individual enemies in this level.
* Details are found in 'enemies' table.
*/
enemies: [],
/*
* ID numbers of gems scattered around.
* Details are found in 'gems' table.
*/
gems: []
},{
name: "Icy Mountain",
music: 3,
startX: 5, startY: 9, startZ: 10
}
],
/*
* Collectables. Some can be inside containers.
* Ignore those which don't have the 'vessel' property.
*/
gems: [
{level: 1, x: 5, y: 6, z: 9, value: 1},
{level: 1, x: 15, y: 16, z: 9, value: 5},
...