var CameraViewHandler = pc.createScript('cameraViewHandler');
CameraViewHandler.attributes.add('rearViewBtn', { type: 'entity' });
CameraViewHandler.attributes.add('glassBtn', { type: 'entity' });
CameraViewHandler.attributes.add('bumperBtn', { type: 'entity' });
CameraViewHandler.attributes.add('bodyBtn', { type: 'entity' });
CameraViewHandler.attributes.add('camera', { type: 'entity' });
CameraViewHandler.prototype.initialize = function() {
this._orbitCamera = this.camera.script.orbitCamera;
this._transitioning = false;
this._transitionDuration = 1;
this._elapsedTime = 0;
// Store initial camera transform when transition starts
this._startPosition = new pc.Vec3();
this._startRotation = new pc.Quat();
this._setupButtonListeners();
};
CameraViewHandler.prototype._setupButtonListeners = function() {
const buttons = [
{ btn: this.rearViewBtn, fn: this.moveToRearView },
{ btn: this.glassBtn, fn: this.moveToGlassView },
{ btn: this.bumperBtn, fn: this.moveToBumperView },
{ btn: this.bodyBtn, fn: this.moveToBodyView }
];
buttons.forEach(({ btn, fn }) => {
if (btn && btn.button) {
btn.button.on('click', () => {
if (!this._transitioning) fn.call(this);
});
}
});
};
// View configuration objects
const VIEWS = {
rear: {
pos: new pc.Vec3(-16, 3, 2.5),
rot: new pc.Vec3(-0.6, -82, 0)
},
glass: {
pos: new pc.Vec3(-19, 4, 1),
rot: new pc.Vec3(357, -90, 0)
},
bumper: {
pos: new pc.Vec3(0.56, 0.575, -35),
rot: new pc.Vec3(-180, 0, -180)
},
body: {
pos: new pc.Vec3(-20, 3.5, 17.7),
rot: new pc.Vec3(-5.8, -27.2, 0)
}
};
CameraViewHandler.prototype.moveToRearView = function() { this._startTransition(VIEWS.rear); };
CameraViewHandler.prototype.moveToGlassView = function() { this._startTransition(VIEWS.glass); };
CameraViewHandler.prototype.moveToBumperView = function() { this._startTransition(VIEWS.bumper); };
CameraViewHandler.prototype.moveToBodyView = function() { this._startTransition(VIEWS.body); };
CameraViewHandler.prototype._startTransition = function(viewConfig) {
if (this._transitioning) return;
// Store initial transform at transition start
this._startPosition.copy(this.camera.getLocalPosition());
this._startRotation.copy(this.camera.getRotation());
// Convert target rotation to quaternion
this._targetRotation = new pc.Quat().setFromEulerAngles(
viewConfig.rot.x,
viewConfig.rot.y,
viewConfig.rot.z
);
this._targetPosition = viewConfig.pos.clone();
this._orbitCamera.enabled = false; // Disable orbit camera to control transition manually
this._transitioning = true;
this._elapsedTime = 0;
};
CameraViewHandler.prototype.update = function(dt) {
if (!this._transitioning) return;
this._elapsedTime += dt;
const t = Math.min(this._elapsedTime / this._transitionDuration, 1);
// Smoothly interpolate position and rotation
const newPos = new pc.Vec3().lerp(this._startPosition, this._targetPosition, t);
const newRot = new pc.Quat().slerp(this._startRotation, this._targetRotation, t);
// Apply new transforms
this.camera.setLocalPosition(newPos);
this.camera.setRotation(newRot);
// Finalize transition when complete
if (t >= 1) {
this._transitioning = false;
// Ensure exact final position/rotation and apply
this.camera.setLocalPosition(this._targetPosition);
this.camera.setRotation(this._targetRotation);
// After the transition, lock the camera in place at the final position and rotation
this._orbitCamera.enabled = true; // Enable orbit camera for rotation only (position is fixed)
// Optionally, you can use resetAndLookAtPoint here to ensure the camera stays locked at the final position
this._resetAndLookAtFinalPosition();
}
};
// This function resets the camera and makes sure it stays at the desired position and rotation
CameraViewHandler.prototype._resetAndLookAtFinalPosition = function() {
if (this._targetPosition && this._targetRotation) {
const targetPos = new pc.Vec3(this._targetPosition.x, this._targetPosition.y, this._targetPosition.z);
const targetRot = new pc.Quat(this._targetRotation.x, this._targetRotation.y, this._targetRotation.z, this._targetRotation.w);
// Reset the orbit camera and make it look at the final position
this._orbitCamera.resetAndLookAtPoint(targetPos, targetRot);
}
};
Hey i got this code for my playcanvas project
Project Overview: I have a PlayCanvas project with a 3D car model and UI buttons that let users switch between different views (e.g., Rear View, Glass View, etc.). When a button is clicked, the camera animates smoothly to the selected view. I also have an Orbit Camera script that allows the user to rotate the car, which gets disabled during the animation and re-enabled once it finishes.
Problem: While the camera transitions smoothly to the target view, the issue occurs after the animation finishes:
- The camera feels like it jerks or snaps when it reaches the target position, instead of smoothly locking into place.
- There’s a discrepancy between the intended final position and where the camera actually settles.
Expected Behavior:
- Camera smoothly locks in place at the target position after the animation.
- Orbit camera is re-enabled once the animation completes without any jerking or snapping.
Attempts to Fix:
- I’ve tried using
lerp
andslerp
for smooth transitions, locking the camera after the animation, and disabling/enabling the Orbit Camera during the transition. Despite these fixes, the camera still doesn’t settle smoothly at the target position.
Request: Any suggestions on how to fix the smooth transition and ensure the camera locks in place without snapping after the animation?