How to smoothly rotate a three-dimensional object in Three.js until it reaches a predetermined angle

Recently, I delved into the world of Three.JS, only to encounter a puzzling roadblock. My goal is to create a drivable car controlled by the Arrow Keys. Thus far, I've managed to make it move forwards and backwards, with the wheels turning appropriately when pressing Up or Down. Naturally, the next challenge is turning. However, this is where I hit a snag.

I'm stumped on how to implement a gradual increase in wheel rotation when I press the left or right arrow keys. I aim to reach specific values, like 45 for left and -45 for right turns.

My setup involves a cube shaped like a flat rectangle, dubbed "chassis," serving as the base. Attached are four wheels - Wheel_FL, FR, RL, and RR - strategically placed for navigation.

Wheel_FL and Wheel_FR each have a parent group for rotation, acting as pivots. Despite my best efforts and extensive research, I'm unable to crack this puzzle. Any insights or solutions would be greatly appreciated!

Furthermore, once the steering issue is resolved, I hope to simulate the chassis's movement realistically during turns.

Any guidance or suggestions would be invaluable. Thank you in advance!

For a clearer picture, an example can be found here.

Answer №1

Allow me to assist you with the initial segment of your inquiry:

Let's assign monikers to the variables:

Wheel_FL, Wheel_FR, Group_FL, Group_FR;
. Each individual wheel is enclosed within its corresponding group using Group_FL.add(Wheel_FL);

To set the wheels in motion, you can utilize

Wheel_FL.rotation.x += spinAngle;
or possibly rotation.z depending on the orientation of your asset. For steering left/right, adjust the container Group accordingly so it doesn't disrupt the spinning angle:
Group_FL.rotation.y = steerAngle;

To facilitate animation or tweens for the steering angle, you may want to explore the MathUtils.lerp() function (lerp denotes Linear-interpolation), which aids in easing a variable towards its target.

var Wheel_FL, Wheel_FR;
var Group_FL = new THREE.Group();
var Group_FR = new THREE.Group();
Group_FL.add(Wheel_FL);
Group_FR.add(Wheel_FR);

var spinAngle = 0;
var steerAngle = 0;
var steerAngleTarget = 0;

function update() {
    // Update wheel spin
    Wheel_FL.rotation.x += spinAngle;
    Wheel_FR.rotation.x += spinAngle;

    // Tween steering angle towards target
    steerAngle = MathUtils.lerp(steerAngle, steerAngleTarget, 0.1);

    // Rotate parent group around y-axis
    Group_FL.rotation.y = steerAngle;
    Group_FR.rotation.y = steerAngle;
}

function steerLeft() {
    steerAngleTarget = Math.PI / 4; // 45 deg in radians
}

function steerRight() {
    steerAngleTarget = - Math.PI / 4; // -45 deg in radians
}

I've prepared a fully functioning demo for your reference below. Employ the w-a-s-d keys to steer, accelerate, and decelerate:

var camera, scene, renderer, clock, container;

container = document.getElementById( 'container' );

camera = new THREE.PerspectiveCamera( 45, container.offsetWidth / container.offsetHeight, 1, 100 );
camera.position.y = 10;
camera.position.z = 5;
camera.lookAt(0, 0, 0);

scene = new THREE.Scene();
scene.background = new THREE.Color( 0xeeeeee );
scene.fog = new THREE.Fog( 0xcccccc, 100, 1500 );

clock = new THREE.Clock();

var hemiLight = new THREE.HemisphereLight( 0xffffff, 0x222222, 1.5 );
hemiLight.position.set( 1, 1, 1 );
scene.add( hemiLight );

var floor = new THREE.Mesh(new THREE.PlaneBufferGeometry(10, 10), new THREE.MeshBasicMaterial({color: 0x999999}));
floor.rotation.x = -Math.PI/2;
scene.add(floor);

renderer = new THREE.WebGLRenderer();
renderer.setSize( container.offsetWidth, container.offsetHeight );
container.appendChild( renderer.domElement );

window.addEventListener( 'resize', onWindowResize, false );
window.addEventListener("keydown", onKeyPress);
window.addEventListener("keyup", onKeyRelease);

// Create wheels
var Wheel_FL = new THREE.Mesh(
    new THREE.CylinderBufferGeometry(1, 1, 1, 8),
  new THREE.MeshBasicMaterial({color: 0xffff00, wireframe: true})
);
var Wheel_FR = new THREE.Mesh(
    new THREE.CylinderBufferGeometry(1, 1, 1, 8),
  new THREE.MeshBasicMaterial({color: 0xffff00, wireframe: true})
);
var Group_FL = new THREE.Group();
var Group_FR = new THREE.Group();

// Set initial positions and rotations
Wheel_FL.position.y = 1;
Wheel_FR.position.y = 1;
Wheel_FL.rotation.z = Math.PI / 2;
Wheel_FR.rotation.z = Math.PI / 2;

// Add wheels to group, and position groups
Group_FL.add(Wheel_FL);
Group_FR.add(Wheel_FR);
Group_FL.position.x = -2;
Group_FR.position.x = 2;
scene.add(Group_FL);
scene.add(Group_FR);

var spinAngle = 0;
var steerAngle = 0;
var steerAngleTarget = 0;

// WASD to turn, accelerate, and decelerate
function onKeyPress(evt) {
    switch(evt.key) {
    case 'a':
        steerAngleTarget = Math.PI / 6; // 45 deg in radians
    break;
    case 'd':
        steerAngleTarget = - Math.PI / 6; // -45 deg in radians
    break;
    case 'w':
        spinAngle += 0.01; // accelerate
    break;
    case 's':
        spinAngle -= 0.01; // decelerate
    break;
  }
}

// Returns wheels to center
function onKeyRelease() {
    steerAngleTarget = 0;
}

function onWindowResize() {
    camera.aspect = container.offsetWidth / container.offsetHeight;
    camera.updateProjectionMatrix();
    renderer.setSize( container.offsetWidth, container.offsetHeight );
}

function update() {
  // Update wheel spin
  Wheel_FL.rotation.x -= spinAngle;
  Wheel_FR.rotation.x -= spinAngle;

  // Tween steering angle towards target
  steerAngle = THREE.MathUtils.lerp(steerAngle, steerAngleTarget, 0.1);

  // Rotate parent group around y-axis
  Group_FL.rotation.y = steerAngle;
  Group_FR.rotation.y = steerAngle;
    requestAnimationFrame( update );

    renderer.render(scene, camera);
}

update();
body, html {
  background-color: #fff;
  color: #222;
  width: 100%;
  height: 100%;
  margin: 0;
  padding: 0;
  overflow: hidden;
}

#container {
  position: absolute;
  top: 0;
  width: 100%;
  bottom: 0px;
}
<script src="https://cdn.rawgit.com/mrdoob/three.js/dev/build/three.js"></script>
<div id="container">
</div>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Exploring the method to retrieve data on the server side through Express when it is shared by the client within a put request

Here is the angular http put request I am working with: sendPutRequest(data) : Observable<any>{ return this.http.put("http://localhost:5050", data).pipe(map(this.handleData)); } After making this call, the server side method being invoked is ...

Set the height of a div based on the height of another div

My challenge involves a textarea that dynamically expands as content is added to it. The structure of the textarea within a div element is illustrated below: <div id='sendMes' class='container-fluid'> <form action='#&apos ...

Selecting a dropdown value dynamically after a form submission in ColdFusion using jQuery

I created a basic form with the use of an onload function to populate market values by default. However, I encountered an issue where after selecting a market value from the drop-down list and submitting the form, the selected value does not stay selected ...

Drop draggable items on top of each other

I'm wondering if there's a way to drag jQuery elements into each other. To illustrate my question, I've duplicated this code (link) and made some style changes. Here is the updated version: Fiddle Link. In the current setup, you can drag e ...

Switching the phone formatting from JavaScript to TypeScript

Below is the JavaScript code that I am attempting to convert to TypeScript: /** * @param {string} value The value to be formatted into a phone number * @returns {string} */ export const formatPhoneString = (value) => { const areaCode = value.substr(0 ...

Looking for some guidance on grasping the concept of strict mode in React and determining what actions can be considered side effects

The other day, I came across a strange bug in React and here is a simplified version of it. let count = 0; export default function App() { const [countState, setCountState] = useState(count); const [countState2, setCountState2] = useState(count); con ...

Developing a TypeScript NodeJS module

I've been working on creating a Node module using TypeScript, and here is my progress so far: MysqlMapper.ts export class MysqlMapper{ private _config: Mysql.IConnectionConfig; private openConnection(): Mysql.IConnection{ ... } ...

How to Fix Items Being Pushed Down by 'Particleground' Jquery Plugin Due to Z-Index and Positioning

I'm grappling with understanding z-index and positioning, despite reading various questions and articles on the topic. Currently, I'm attempting to incorporate a Jquery Plugin called 'Particleground': https://github.com/jnicol/particle ...

Trouble encountered when attempting to override styles in Material UI's TableRow

I am attempting to customize Material UI's default styles in order to create a margin between each TableRow. Despite my efforts, it appears that the override is not being reflected in the user interface. ...

Tips for ensuring the CSRF token functions properly on the browser when utilizing Django and React

Apologies in advance if this question seems beginner-friendly, but I have developed an application with Django backend and React frontend. I am currently working on implementing the CSRF token for the post request on the create endpoint using the code snip ...

Unable to render a rectangle with React's canvas context.fillRect

Could anyone help me with drawing a rectangle using React? I'm having trouble getting it to work. I'm confused as to why the code below isn't showing a rectangle on the screen. class DrawingView{ constructor(props) { this.canva ...

Unusual patterns observed when employing the splice method in AngularJS for ordering

Take a look at this Plunker demo link I have encountered an issue after implementing the orderby feature (line 24) in my application. When I try to add an item without priority and then add another one with priority, followed by deleting the first item, t ...

Breaking down an object containing an internal array and omitting specific keys

I've got this specific object structure: const objBefore: { "id": "3pa99f64-5717-4562-b3fc-2c963f66afa1", "number": "5000", "enabled": true, "classes": [ { ...

Ways to verify that a javascript function generates an object and executes a method within that object

Currently, I am in the process of updating server code written in nodejs and incorporating unit tests into the mix. However, I have encountered a challenge that I need assistance with: classX.prototype.methodX = function () { // Create new session ...

The importance of incorporating React into the scope of functional component development

While discussing class components, it's clear that they are part of the global React object. But why is it necessary to import them with every functional component? And do bundlers play a role in this requirement? I've been coding for 5 months n ...

What is the most popular method for namespacing AngularJS modules?

I am new to AngularJS and currently exploring different ways to namespace modules in my application. One challenge I face is the need to integrate my Angular app into a designated placeholder div within a third-party website (which may also use Angular), ...

Execute a function upon the invocation of a different function

I am exploring how to trigger a function when another function is executed. While addEventListener is typically used for events like "click" or "mouseover", I am looking to detect when a function is called. For instance: Function 1 is invoked, an ...

ng-bind stops updating after entering text into input field

I am a newcomer to AngularJS and I have encountered an issue that I am struggling to resolve. Although I found a similar question on stackoverflow, the solution provided did not work for me. The problem I am facing is that when I input text into any of the ...

Mastering the art of properly connecting Angular HttpPromise

Recently, I encountered an angular Service containing a crucial function: service.getItemByID = function(id) { var hp = $http({method: "GET", url: "service/open/item/id", headers: {"token": $rootScope.user.token}, para ...

A guide on implementing gradient color fill for scatter plot points within Google Charts

I am interested in creating a scatter plot using a data set that includes three series: (1) for the x-axis, (2) for the y-axis, and (3) a third series that contains only 0 and 1 values. For this plot, I would like the points with a third series value of 0 ...