Getting Started - Chapter 3 - A Walk Around The Village
A Walk Around The Village
There is a useful property of a mesh, movePOV, which allows us to move a mesh relative to its point of view. Generally, a newly created mesh is considered to face the negative z direction, and this is the direction of its point of view. To move a mesh forward 6 units in the direction of its point of view, you use
mesh.movePOV(0, 0, -6)The parameters are, in order, the distance to move to the right, up, and forward. In the mesh's local space, these are generally the negative x axis, the positive y axis, and the negative z axis.
In Babylon.js you can write code that will be executed before the rendering of the next frame using
scene.onBeforeRenderObservable.add(() => { //code to execute});In this way, the properties of objects can be changed frame by frame as the scene renders.
Let us take the simple case of a sphere moving around the edges of a triangle. We want the sphere to appear to slide along one side, turn to slide along the next, then turn and slide along the last side before repeating.
This is also an opportunity to introduce two types of mesh you can create: a sphere and a series of lines. Take a sphere sliding around an isosceles right-angled triangle as an example.
import { Vector3 } from "@babylonjs/core/Maths/math.vector";import { MeshBuilder } from "@babylonjs/core/Meshes/meshBuilder";const sphere = MeshBuilder.CreateSphere("sphere", { diameter: 0.25 });//end points for the line sequence in an array//y component can be non zeroconst points = [];points.push(new Vector3(2, 0, 2));points.push(new Vector3(2, 0, -2));points.push(new Vector3(-2, 0, -2));points.push(points[0]); //close the triangle;MeshBuilder.CreateLines("triangle", { points: points });Also you can see another method, rotate, of rotating. This method rotates the mesh about the given axis by the given angle in radians. It adds to the current rotation.
import { Space } from "@babylonjs/core/Maths/math.axis";mesh.rotate(axis, angle, Space.LOCAL);To produce the animation, the sphere moves a distance of 0.05 before each render frame. When the distance it has travelled is greater than 4, the sphere makes a turn. When it is greater than 8, it turns again, and when it is greater than the perimeter, it resets and starts again.
We set up a track array of objects with the properties turn and distance. After travelling the given total distance, the sphere rotates by the given turn value.
const slide = function (turn, dist) { //after covering dist apply turn this.turn = turn; this.dist = dist;}const track = [];track.push(new slide(Math.PI / 2, 4)); //first side length 4track.push(new slide(3 * Math.PI / 4, 8)); //at finish of second side distance covered is 4 + 4track.push(new slide(3 * Math.PI / 4, 8 + 4 * Math.sqrt(2))); //all three sides cover the distance 4 + 4 + 4 * sqrt(2)Whenever the required distance is reached, a turn is made and the array index pointer, p, is increased by 1. The modulo operator % is used to reset the pointer to zero at the end of the array.
import { Axis, Space } from "@babylonjs/core/Maths/math.axis";if (distance > track[p].dist) { sphere.rotate(Axis.Y, track[p].turn, Space.LOCAL); p += 1; p %= track.length;}To prevent floating-point errors from accumulating, whenever the index pointer is reset to 0, the position and rotation of the sphere are also reset.
import { Vector3 } from "@babylonjs/core/Maths/math.vector";if (p === 0) { distance = 0; sphere.position = new Vector3(2, 0, 2); //reset to initial conditions sphere.rotation = Vector3.Zero(); //prevents error accumulation }This is a little trickier, and by using a bit of trial and error for the turns and distances, we can achieve a more complicated walk for the character around the village. One reason for using degrees and converting them to radians for the rotate method is that it is easier to adjust by adding one or two degrees.
Since the character, dude, imported from the .babylon file has had its rotation set using a rotationQuaternion rather than rotation, we use the rotate method to reset the character's orientation.
import { Axis, Space } from "@babylonjs/core/Maths/math.axis";import { Vector3 } from "@babylonjs/core/Maths/math.vector";import { Tools } from "@babylonjs/core/Misc/tools";dude.position = new Vector3(-6, 0, 0);dude.rotate(Axis.Y, Tools.ToRadians(-95), Space.LOCAL);const startRotation = dude.rotationQuaternion.clone(); //use clone so that variables are independent not linked copiesimport { Vector3 } from "@babylonjs/core/Maths/math.vector";if (p === 0) { distance = 0; dude.position = new Vector3(-6, 0, 0); dude.rotationQuaternion = startRotation.clone();}We now have two things moving around the village: a car and a character. How can we avoid them colliding?