What I’m trying to achieve is closer to a third-person game camera.
The target object is moving, and I want the camera to follow that movement with a fixed offset, while still allowing the user to freely rotate the view using OrbitControls.
So it’s not just about updating controls.target.copy(...).
I tried that, but since the target moves every frame, OrbitControls keeps having its rotation center overridden, which makes the camera interaction feel unnatural — the view snaps or shifts instead of keeping the user’s current orientation.
In other words, I’m looking for a setup where:
the camera rig follows the moving target,
the user can still orbit and zoom naturally,
and both behaviors do not interfere with each other — similar to how you can move a character and freely adjust the camera in a typical 3D game.
If you need to track at some fixed offset in camera space..
let camSpaceOffset= new THREE.Vector3();
...
camera.position.sub(controls.target);
character.localToWorld(controls.target.set(0,1,0)); //Character space offset
camSpaceOffset.set(1,0,0).applyQuaternion(camera.quaternion);
controls.target.add( camSpaceOffset ); // Camera space offset
camera.position.add(controls.target);
Keep camera+controls locked on moving/rotating player:
character.worldToLocal(controls.target);
character.worldToLocal(camera.position);
UPDATE_YOUR_CHARACTER_HERE() // Rotate / move your character here..
//This can be the character itself, or for instance, the characters head bone...
//In which case, you would call your animationMixer.update() here as well to update the bone..
character.updateMatrixWorld(true); //Make sure its matrices are up to date.. (may not be neccesary)
character.localToWorld(controls.target);
character.localToWorld(camera.position)
(these operations should be done right before you call “controls.update()” in your render loop)
If the character has an initial rotation, how do you compute the camera offset so that the camera always stays at the character’s true back? I’ve made a small demo here: https://codesandbox.io/p/sandbox/wkjz6s
Could you help implement this behavior in the demo?