8 min read

(For more resources related to this topic, see here.)

A maze is a rather simple shape that consists of a number of walls and a floor. So, what we need is a way to create these shapes. Three.js, not very surprisingly, doesn’t have a standard geometry that will allow you to create a maze, so we need to create this maze by hand. To do this, we need to take two different steps:

  1. Find a way to generate the layout of the maze so that not all the mazes look the same.
  2. Convert that to a set of cubes (THREE.BoxGeometry) that we can use to render the maze in 3D.

There are many different algorithms that we can use to generate a maze, and luckily there are also a number of open source JavaScript libraries that implement such an algorithm. So, we don’t have to start from scratch. For the example in this book, I’ve used the following random-maze-generator project that you can find on GitHub at the following link:

https://github.com/felipecsl/random-maze-generator

Generating a maze layout

Without going into too much detail, this library allows you to generate a maze and render it on an HTML5 canvas. The result of this library looks something like the following screenshot:

You can generate this by just using the following JavaScript:

var maze = new Maze(document, 'maze'); maze.generate(); maze.draw();

Even though this is a nice looking maze, we can’t use this directly to create a 3D maze. What we need to do is change the code the library uses to write on the canvas, and change it to create Three.js objects. This library draws the lines on the canvas in a function called drawLine:

drawLine: function(x1, y1, x2, y2) { self.ctx.beginPath(); self.ctx.moveTo(x1, y1); self.ctx.lineTo(x2, y2); self.ctx.stroke(); }

If you’re familiar with the HTML5 canvas, you can see that this function draws lines based on the input arguments. Now that we’ve got this maze, we need to convert it to a number of 3D shapes so that we can render them in Three.js.

Converting the layout to a 3D set of objects

To change this library to create Three.js objects, all we have to do is change the drawLine function to the following code snippet:

drawLine: function(x1, y1, x2, y2) { var lengthX = Math.abs(x1 - x2); var lengthY = Math.abs(y1 - y2); // since only 90 degrees angles, so one of these is always 0 // to add a certain thickness to the wall, set to 0.5 if (lengthX === 0) lengthX = 0.5; if (lengthY === 0) lengthY = 0.5; // create a cube to represent the wall segment var wallGeom = new THREE.BoxGeometry(lengthX, 3, lengthY); var wallMaterial = new THREE.MeshPhongMaterial({ color: 0xff0000, opacity: 0.8, transparent: true }); // and create the complete wall segment var wallMesh = new THREE.Mesh(wallGeom, wallMaterial); // finally position it correctly wallMesh.position = new THREE.Vector3( x1 - ((x1 - x2) / 2) - (self.height / 2), wallGeom.height / 2, y1 - ((y1 - y2)) / 2 - (self.width / 2)); self.elements.push(wallMesh); scene.add(wallMesh); }

In this new drawLine function, instead of drawing on the canvas, we create a THREE.BoxGeometry object whose length and depth are based on the supplied arguments. Using this geometry, we create a THREE.Mesh object and use the position attribute to position the mesh on a specific points with the x, y, and z coordinates. Before we add the mesh to the scene, we add it to the self.elements array.

Now we can just use the following code snippet to create a 3D maze:

var maze = new Maze(scene,17, 100, 100); maze.generate(); maze.draw();

As you can see, we’ve also changed the input arguments. These properties now define the scene to which the maze should be added and the size of the maze. The result from these changes can be seen in the following screenshot:

Every time you refresh, you’ll see a newly generated random maze. Now that we’ve got our generated maze, the next step is to add the object that we’ll move through the maze.

Animating the cube

Before we dive into the code, let’s first look at the result as shown in the following screenshot:

Using the controls at the top-right corner, you can move the cube around. What you’ll see is that the cube rotates around its edges, not around its center. In this section, we’ll show you how to create that effect. Let’s first look at the default rotation, which is along an object’s central axis, and the translation behavior of Three.js.

The standard Three.js rotation behavior

Let’s first look at all the properties you can set on THREE.Mesh. They are as follows:

Function/property

Description

position

This property refers to the position of an object, which is relative to the position of its parent. In all our examples, so far the parent is THREE.Scene.

rotation

This property defines the rotation of THREE.Mesh around its own x, y, or z axis.

scale

With this property, you can scale the object along its own x, y, and z axes.

translateX(amount)

This property moves the object by a specified amount over the x axis.

translateY(amount)

This property moves the object by a specified amount over the y axis.

translateZ(amount)

This property moves the object by a specified amount over the z axis.

If we want to rotate a mesh around one of its own axes, we can just call the following line of code:

plane.rotation.x = -0.5 * Math.PI;

We’ve used this to rotate the ground area from a horizontal position to a vertical one. It is important to know that this rotation is done around its own internal axis, not the x, y, or z axis of the scene. So, if you first do a number of rotations one after another, you have to keep track at the orientation of your mesh to make sure you get the required effect. Another point to note is that rotation is done around the center of the object—in this case the center of the cube. If we look at the effect we want to accomplish, we run into the following two problems:

  • First, we don’t want to rotate around the center of the object; we want to rotate around one of its edges to create a walking-like animation
  • Second, if we use the default rotation behavior, we have to continuously keep track of our orientation since we’re rotating around our own internal axis

In the next section, we’ll explain how you can solve these problems by using matrix-based transformations.

Creating an edge rotation using matrix-based transformation

If we want to perform edge rotations, we have to take the following few steps:

  • If we want to rotate around the edge, we have to change the center point of the object to the edge we want to rotate around.
  • Since we don’t want to keep track of all the rotations we’ve done, we’ll need to make sure that after each rotation, the vertices of the cube represent the correct position.
  • Finally, after we’ve rotated around the edge, we have to do the inverse of the first step. This is to make sure the center point of the object is back in the center of the cube so that it is ready for the next step.

So, the first thing we need to do is change the center point of the cube. The approach we use is to offset the position of all individual vertices and then change the position of the cube in the opposite way. The following example will allow us to make a step to the right-hand side:

cubeGeometry.applyMatrix(new THREE.Matrix4().makeTranslation (0, width / 2, width / 2)); cube.position.y += -width / 2; cube.position.z += -width / 2;

With the cubeGeometry.applyMatrix function, we can change the position of the individual vertices of our geometry. In this example, we will create a translation (using makeTranslation), which offsets all the y and z coordinates by half the width of the cube. The result is that it will look like the cube moved a bit to the right-hand side and then up, but the actual center of the cube now is positioned at one of its lower edges. Next, we use the cube.position property to position the cube back at the ground plane since the individual vertices were offset by the makeTranslation function.

Now that the edge of the object is positioned correctly, we can rotate the object. For rotation, we could use the standard rotation property, but then, we will have to constantly keep track of the orientation of our cube. So, for rotations, we once again use a matrix transformation on the vertices of our cube:

cube.geometry.applyMatrix(new THREE.Matrix4().makeRotationX(amount);

As you can see, we use the makeRotationX function, which changes the position of our vertices. Now we can easily rotate our cube, without having to worry about its orientation. The final step we need to take is reset the cube to its original position; taking into account that we’ve moved a step to the right, we can take the next step:

cube.position.y += width/2; // is the inverse + width cube.position.z += -width/2; cubeGeometry.applyMatrix(new THREE.Matrix4().makeTranslation(0, - width / 2, width / 2));

As you can see, this is the inverse of the first step; we’ve added the width of the cube to position.y and subtracted the width from the second argument of the translation to compensate for the step to the right-hand side we’ve taken.

If we use the preceding code snippet, we will only see the result of the step to the right.

Summary

In this article, we have seen how to create a maze and animate a cube.

Resources for Article:


Further resources on this subject:


LEAVE A REPLY

Please enter your comment!
Please enter your name here