I’ve to custom an AutoRotationBehavior, and here’s the code
Here is the PG playground
import {
ArcRotateCamera,
AutoRotationBehavior,
PointerEventTypes,
Scene,
Animation,
AbstractMesh,
Mesh,
ActionManager,
ExecuteCodeAction,
CubicEase,
EasingFunction
} from '@babylonjs/core';
interface TEventListeners {
idleTimeout: any[];
resetComplete: any[];
}
const easingFunction = new CubicEase();
easingFunction.setEasingMode(EasingFunction.EASINGMODE_EASEINOUT);
class CameraController {
public camera: ArcRotateCamera;
public scene: Scene;
public autoRotationBehavior: AutoRotationBehavior;
private lastInteractionTime: number = Date.now();
private resetInterval: NodeJS.Timer | number | undefined = undefined;
private eventListeners: TEventListeners = {
idleTimeout: [],
resetComplete: []
};
private setupTimer: NodeJS.Timer | number | undefined = undefined;
private once: boolean = false;
public OVER_TIME = 50 * 1000;
private triggerEvents = new Set();
/**
*
* @param camera
* @param scene
* @param mesh mesh
* @param delay delay time
* @param once Whether to execute the command only once
*/
constructor(camera: ArcRotateCamera, scene: Scene, mesh: AbstractMesh | Mesh, delay?: number, once = false) {
this.camera = camera;
this.scene = scene;
this.once = once;
this.autoRotationBehavior = new AutoRotationBehavior();
const isDelay = Boolean(delay);
if (isDelay) {
this.setupTimer = setTimeout(() => {
//camera.useAutoRotationBehavior =true
this.autoRotationBehavior.attach(this.camera);
}, delay);
} else {
this.autoRotationBehavior.attach(this.camera);
}
this.autoRotationBehavior.idleRotationSpeed = -0.2;
this.autoRotationBehavior.idleRotationWaitTime = 5 * 1000;
this.autoRotationBehavior.idleRotationSpinupTime = 5000;
// this.SetupListeners();
this.SetMeshAction(mesh);
this.StartResetCheck();
}
SetupListeners() {
this.scene.onPointerObservable.add((pointerInfo) => {
switch (pointerInfo.type) {
case PointerEventTypes.POINTERDOWN:
this.autoRotationBehavior.idleRotationSpeed = 0;
this.lastInteractionTime = Date.now();
break;
case PointerEventTypes.POINTERUP:
this.autoRotationBehavior.idleRotationSpeed = -0.1;
this.lastInteractionTime = Date.now();
break;
case PointerEventTypes.POINTERMOVE:
if (pointerInfo.event.button !== 0) {
this.lastInteractionTime = Date.now();
}
break;
}
});
}
SetMeshAction(mesh: AbstractMesh | Mesh) {
mesh.actionManager = new ActionManager(this.scene);
mesh.actionManager.registerAction(
new ExecuteCodeAction(
{
trigger: ActionManager.OnPickTrigger
},
() => {
this.autoRotationBehavior.idleRotationSpeed = 0;
this.lastInteractionTime = Date.now();
}
)
);
mesh.actionManager.registerAction(
new ExecuteCodeAction(
{
trigger: ActionManager.OnPickUpTrigger
},
() => {
this.autoRotationBehavior.idleRotationSpeed = 0.1;
this.lastInteractionTime = Date.now();
}
)
);
this.scene.onPointerObservable.add((pointerInfo) => {
if (pointerInfo.type === PointerEventTypes.POINTERMOVE) {
if (pointerInfo.pickInfo?.hit && pointerInfo.pickInfo.pickedMesh === mesh) {
this.lastInteractionTime = Date.now();
}
}
});
}
StartResetCheck() {
this.resetInterval = setInterval(() => {
const currentTime = Date.now();
const timeDifference = currentTime - this.lastInteractionTime;
if (timeDifference > this.OVER_TIME) {
if (!this.once || !this.triggerEvents.has('idleTimeout')) {
this.TriggerEvent('idleTimeout');
this.ResetCameraRotaion();
this.lastInteractionTime = currentTime;
}
}
}, 1000);
}
StopResetCheck() {
if (this.resetInterval) {
clearInterval(this.resetInterval as number);
}
}
ResetCameraRotaion = (initAplha = 1.57, duration = 1000) => {
let currentAlpha = this.camera.alpha;
currentAlpha %= Math.PI * 2;
if (currentAlpha < 0) {
currentAlpha += Math.PI * 2;
}
let deltaAlpha = initAplha - currentAlpha;
if (Math.abs(deltaAlpha) > Math.PI) {
deltaAlpha = deltaAlpha > 0 ? deltaAlpha - Math.PI * 2 : deltaAlpha + Math.PI * 2;
}
const ani = Animation.CreateAndStartAnimation(
'resetCameraRotaion',
this.camera,
'alpha',
60,
duration / (1000 / 60),
this.camera.alpha,
this.camera.alpha + deltaAlpha,
Animation.ANIMATIONLOOPMODE_CONSTANT
);
ani!.onAnimationEnd = () => {
if (!this.once || !this.triggerEvents.has('resetComplete')) {
this.autoRotationBehavior.idleRotationSpeed = 0;
this.TriggerEvent('resetComplete');
}
};
};
addEventListener(eventName: keyof TEventListeners, callback: () => void) {
if (!this.eventListeners[eventName]) {
this.eventListeners[eventName] = [];
}
this.eventListeners[eventName].push(callback);
}
removeEventListener(eventName: keyof TEventListeners, callback: () => void) {
if (this.eventListeners[eventName]) {
this.eventListeners[eventName] = this.eventListeners[eventName].filter((cb) => cb !== callback);
}
}
TriggerEvent(eventName: keyof TEventListeners) {
if (this.eventListeners[eventName]) {
this.eventListeners[eventName].forEach((callback) => callback());
this.triggerEvents.add(eventName);
}
}
Dispose() {
this.StopResetCheck();
this.autoRotationBehavior.detach();
this.eventListeners = null;
this.setupTimer = undefined;
this.resetInterval = undefined;
this.triggerEvents.clear();
}
}
export { CameraController };
I have done this:
scene.getMeshByName('Earth_Clound').isPickable = false;
scene.getMeshByName('Earth_Hightlight').isPickable = false;
The function I want to implement is:
- Use the autoRotationBehavior of ArcRotateCamera, but need to reset some of its properties, such as idleRotationSpeed and idleRotationWaitTime
- Monitor whether there is interaction with the mesh. If there is no interaction, then after OVER_TIME seconds, reset the camera position and send events so that I can do my own things.
…
…
…
The question is:
I don’t want to add listening events to the scene, I just want to add them to the mesh, but no matter what I do, the mesh can’t add OnPickTrigger or anything else, but I can easily add them to the scene, I think I’ve set the mesh that blocks the mesh isPickable=false,such as Earth_Clound,Earth_Hightlight, I’ve even set them all to xxx.isenabled (false)
I’m not very good at bjs, is there another better way? Or can you help me figure out what’s wrong? Also, can the resetLastInteractionTime attribute help me?