Creating a camera controller
A custom controller implements ICameraController and is registered with CameraSystem.
Contract
ts
import type { Camera } from '@babylonjs/core/Cameras/camera';
import type { Observer } from '@babylonjs/core/Misc/observable';
import type { Scene } from '@babylonjs/core/scene';
import type {
CameraControllerConfig,
ICameraController,
} from '../../../../src/engine/camera/controllers/ICameraController';
export class OrbitDebugController implements ICameraController {
readonly type = 'orbit-debug';
private scene: Scene | null = null;
private observer: Observer<Scene> | null = null;
private enabled = true;
private config: CameraControllerConfig = { speed: 1 };
private angle = 0;
attach(camera: Camera, scene: Scene, _canvas: HTMLCanvasElement): void {
this.scene = scene;
this.observer = scene.onBeforeRenderObservable.add(() => {
if (!this.enabled) return;
this.angle += (this.config.speed ?? 1) * scene.getEngine().getDeltaTime() / 1000;
camera.position.x = Math.cos(this.angle) * 200;
camera.position.z = Math.sin(this.angle) * 200;
});
}
detach(): void {
if (this.scene && this.observer) {
this.scene.onBeforeRenderObservable.remove(this.observer);
}
this.observer = null;
this.scene = null;
}
update(_deltaTime: number): void {}
configure(config: Partial<CameraControllerConfig>): void {
this.config = { ...this.config, ...config };
}
getConfig(): CameraControllerConfig {
return { ...this.config };
}
setEnabled(enabled: boolean): void {
this.enabled = enabled;
}
isEnabled(): boolean {
return this.enabled;
}
dispose(): void {
this.detach();
}
}The interface also declares optional setTarget, getTarget and setFollowTarget methods; implement them when the controller follows something.
Register and select
ts
const cameras = CameraSystem.getInstance();
cameras.registerController('orbit-debug', new OrbitDebugController());
cameras.setController('orbit-debug');registerController disposes an existing controller registered under the same name. setController returns false if the type is missing or the system has not been initialized with a scene, canvas, and camera.
Lifecycle rules
- remove every Babylon observer and DOM listener in
detach; - make
detachanddisposesafe to call more than once; - do not dispose the shared camera from
detach; - use
InputSystemactions rather than adding duplicate key listeners; - keep game-specific selection or combat behavior outside the controller.
camera mode accepts only the built-in controller names. To select a custom controller from the console, add a game-specific command that calls setController.
