Overview
In this exercise, we will place objects on a circular path. We’ll be extending this exercise further by adding animation to the cubes. If you are unfamiliar with the basics, please go through the Grid 1D exercise first. At the end, we’ll also look into creating a spiral of cubes.
Circle of Objects Codepen: https://codepen.io/xrclub/full/QWQKVLM
Getting Started: https://xr.club/exercise/getting-started/
Level I – Circle of Objects
The scene, camera, lighting, and renderer need to be set up beforehand, as in the Getting Started exercise. If you need help remembering how it all works, check that exercise out before starting. Here, we will just focus on creating the circle of objects in the 3D scene.
Here are the instructions:
- define radius and cube counts
- set up cube geometry and material
- generate cubes inside a for loop
- calculate angle of a circle
- set new X & Y positions for the cube
Step 1: define radius and cube counts
Start by defining the basic parameters necessary to create a circle of objects: the radius and the number of cubes.
// Set the radius of the circle
this.radius = 20;
// Set the number of cubes
this.numberOfCubes = 20;
Step 2: set up cube geometry and material
Create the geometry and material for the cube.
// Create cube geometry and material
const cubeGeometry = new THREE.BoxGeometry( 2, 2, 2 );
const cubeMaterial = new THREE.MeshStandardMaterial( {
color: 0xffffff
} );
Step 3: generate cubes inside a for loop
Add a for loop to generate the cubes based on the ‘numberOfCubes’ defined. Use the cube geometry and material created previously.
// Loop through each cube to be placed on the circle
for(let cubeIndex = 0; cubeIndex < this.numberOfCubes; cubeIndex++){
let cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
this.scene.add(cube);
}
Step 4: calculate angle of a circle
Determine an angle for positioning objects on a circular path. To do this, calculate the current cube progress by dividing the cube index by the number of objects. The cube progress ranges between 0 and 1 and it will increase incrementally. Multiplying this value by 2 PI will ensure the angle is measured in radians.
// Calculate the progress of the loop through the cubes
let cubeProgress = cubeIndex / this.numberOfCubes;
// Calculate angle in radians by multiplying the cube progress with 2 PI
let angleInRadians = (Math.PI * 2) * cubeProgress;
Step 5: set new X & Y positions for the cube
The general trigonometric equation for determining the X position of a circle can be evaluated by taking the cosine of the angle and multiplying this by the radius. To evaluate the y position, repeat this process but use the sine value of the angle instead. Set the new x & y positions for the object.
// Calculate the cube's X position by multiplying each circle's radius with the cosine of the angle. Alternatively, use the sine of the angle for calculating the Y position instead.
// Set cube's X & Y positions
cube.position.x = Math.cos(angleInRadians) * this.radius
cube.position.y = Math.sin(angleInRadians) * this.radius
Result
See the Pen Loops – 1D Circle by XR Club (@xrclub) on CodePen.
Level II – Circle of Objects Animated
Next, we’ll animate the Circle of Objects to oscillate over time.
Add a new cubes array and time variable
Create a new empty cubes array to store the cubes and a time variable to store the animation elapsed time.
// Create an empty array to store cubes
this.cubesArray = [];
// Set time to zero
this.time = 0;
Store the cube inside objects array
Inside the for loop that generates the cube objects, push the cube object into the empty array. Later, this will be referenced in the update loop for the animation.
this.cubesArray.push(cube);
Store time elapsed
In the update loop, continuously add dt to time. The accumulated total represents the elapsed time.
update(dt){
this.time += dt;
this.renderer.render(this.scene, this.camera);
}
}
Add animation logic
Obtain the current cube object by looping through each element in the array. Update the object’s Z position based on the sine function of time with the cube index value added. This will add phase differences to the object’s motion, try removing the cube index value, the circle will now animate in sync!
for(let cubeIndex = 0; cubeIndex < this.cubesArray.length; cubeIndex++){
// Get the current object
let cube = this.cubesArray[cubeIndex];
// Update cube's z-position to oscillate over time
cube.position.z = Math.sin( this.time + cubeIndex ) * 3;
}
Result
See the Pen Loops – 1D Circle – Ani by XR Club (@xrclub) on CodePen.
Level III – Spiral
For the last challenge, let’s make the cubes follow a spiral path.
Add new parameters for the radius and spiral
Expand on the original parameters to add more control by creating the start, end and current radius values. Do the same for the spiral depth values.
// Set the radius parameters for the spiral
this.startRadius = 20;
this.endRadius = 50;
this.currentRadius = 0;
// Set the number of cubes
this.numberOfObjects = 50;
// Set the spiral parameters
this.startSpiralDepth = 0;
this.endSpiralDepth = 60;
this.currentSpiralDepth = 0;
// Set the number of spirals
this.numberOfSpirals = 2;
Calculate step angle
The step angle will be defined as the number of fully rotated spirals in radians based on the previously defined spiral count.
// Calculate step angle based on spiral counts
const stepAngle = 2 * Math.PI * this.numberOfSpirals;
Update values relative to the cube progress inside for loop
To calculate angle in radians multiply the step angle by the cube progress, this will now take into account the number of spirals defined as opposed to just a single circular path. For the current radius and spiral depth, the equation used are similar in principle. By taking the differences between the start and end values, multiply by the cube progress. This will calculate the incremental steps, add this back to the start value, to create a spiral. Finally, set the cube’s position to the the new X, Y values and the current spiral depth for Z.
// Calculate angle in radians by multiplying the cube's progress with the step angle
let angleInRadians = stepAngle * cubeProgress;
// Update the currentRadius and currentSpiralDepth based on the cube's progress
this.currentRadius = this.startRadius + ( this.endRadius - this.startRadius) * cubeProgress;
this.currentSpiralDepth = this.startSpiralDepth + ( this.endSpiralDepth - this.startSpiralDepth) * cubeProgress;
// Calculate and set the cube's X, Y positions based on the currentRadius and angleInRadians. Set Z position to be the currentSpiralDepth
cube.position.x = Math.cos(angleInRadians) * this.currentRadius;
cube.position.y = Math.sin(angleInRadians) * this.currentRadius;
cube.position.z = this.currentSpiralDepth;
Result
See the Pen Loops – 1D Spiral by XR Club (@xrclub) on CodePen.