UniversalCamera

UniversalCamera for First-Person Movement

UniversalCamera, Babylon 89,603 's default first-person camera, extends FreeCamera: arrow keys move it, the mouse turns it, and touch and gamepads work without extra code. Place it, then aim it with setTarget:

A UniversalCamera at eye height and its defaultsJavaScript
const camera = new BABYLON.UniversalCamera('walk', new BABYLON.Vector3(0, 1.6, -2.2), scene);
camera.setTarget(new BABYLON.Vector3(0, 1.4, 3));        // look toward the back wall
console.log(`keysUp ${camera.keysUp}, keysDown ${camera.keysDown}, speed ${camera.speed}`);
console.log(`angularSensibility ${camera.angularSensibility}, inertia ${camera.inertia}, ` +
  `minZ ${camera.minZ}`);
console.log(`inputs: ${Object.keys(camera.inputs.attached).join(', ')}`);
Output
BJS - [15:49:26]: Babylon.js v9.28.0 - WebGL2 - Parallel shader compilation
keysUp 38, keysDown 40, speed 2
angularSensibility 2000, inertia 0.9, minZ 1
inputs: keyboard, mouse, touch, gamepad

The defaults suit a large outdoor scene, not a room: holding the up arrow for one second moved the camera 64 m in headless Chrome 1 , ten times BookNest's room, angularSensibility is a divisor (a larger value turns more slowly), and the near plane minZ of 1 m cuts away anything closer, such as a shelf you walk up to. Keys are arrays of key codes, so adding W, A, S and D is a push (Walkthrough Camera). FlyCamera is the variant for free flight with banking.

A UniversalCamera at eye height in the bookstore, tuned from its outdoor defaultsHTMLLive
<!doctype html>
<style>
  body { margin: 0; background: #f7f3ec; font: 13px system-ui, sans-serif; color: #333; }
  canvas { width: 100%; max-width: 600px; height: 330px; display: block; touch-action: none; outline: none; }
  #info { max-width: 600px; padding: 4px 8px; font-family: monospace; white-space: pre; }
</style>
<canvas id="view" tabindex="0"></canvas>
<div id="info"></div>
<script src="https://cdn.jsdelivr.net/npm/babylonjs@9.28.0/babylon.js"></script>
<script>
const canvas = document.getElementById('view');
const engine = new BABYLON.Engine(canvas, true);
const scene = new BABYLON.Scene(engine);
scene.clearColor = BABYLON.Color4.FromHexString('#cfd8dcff');
new BABYLON.HemisphericLight('sky', new BABYLON.Vector3(0.3, 1, -0.4), scene)
  .groundColor = new BABYLON.Color3(0.35, 0.32, 0.3);

const camera = new BABYLON.UniversalCamera('walk', new BABYLON.Vector3(0, 1.6, -2.2), scene);
camera.setTarget(new BABYLON.Vector3(0, 1.4, 3));        // look toward the back wall
const defaults = `defaults: speed ${camera.speed}, angularSensibility ${camera.angularSensibility}, ` +
  `minZ ${camera.minZ}, inertia ${camera.inertia}`;
// The defaults suit a big outdoor scene; tune them for a 8 m x 6 m room
camera.speed = 0.05;                   // about walking pace
camera.angularSensibility = 4000;      // a divisor: larger turns more slowly
camera.minZ = 0.05;                    // the 1 m default clips shelves you walk up to
camera.keysUp.push(87); camera.keysDown.push(83);        // W and S beside the arrows
camera.keysLeft.push(65); camera.keysRight.push(68);     // A and D
camera.attachControl(canvas, true);

const paint = (mesh, hex) => {
  mesh.material = new BABYLON.StandardMaterial(hex, scene);
  mesh.material.diffuseColor = BABYLON.Color3.FromHexString(hex);
  mesh.material.specularColor = BABYLON.Color3.Black();
  return mesh;
};
paint(BABYLON.MeshBuilder.CreateGround('floor', { width: 8, height: 6 }, scene), '#b08d6a');
for (const [name, width, x, z] of [['back', 8, 0, 3], ['left', 6, -4, 0], ['right', 6, 4, 0]]) {
  const wall = paint(BABYLON.MeshBuilder.CreateBox(name, { width, height: 3, depth: 0.15 }, scene), '#fff3e0');
  wall.position.set(x, 1.5, z);
  wall.rotation.y = x ? Math.PI / 2 : 0;
}
// Two bookcases against the back wall, each with rows of colored spines
const COLORS = ['#1f5f8b', '#5b3f99', '#e09a10', '#3f7d3a', '#b5452f', '#2a9d8f'];
for (const cx of [-1.8, 1.8]) {
  const back = paint(BABYLON.MeshBuilder.CreateBox('case', { width: 3, height: 2.2, depth: 0.05 }, scene), '#6d4c33');
  back.position.set(cx, 1.1, 2.9);
  for (let shelf = 0; shelf < 4; shelf++) {
    const board = paint(BABYLON.MeshBuilder.CreateBox('board', { width: 3, height: 0.04, depth: 0.35 }, scene), '#6d4c33');
    board.position.set(cx, 0.1 + shelf * 0.5, 2.75);
    for (let i = 0; i < 14; i++) {
      const h = 0.26 + ((i * 7 + shelf * 3) % 5) * 0.03;
      const book = paint(BABYLON.MeshBuilder.CreateBox('book', { width: 0.16, height: h, depth: 0.26 }, scene),
        COLORS[(i + shelf) % 6]);
      book.position.set(cx - 1.3 + i * 0.2, 0.12 + shelf * 0.5 + h / 2, 2.75);
    }
  }
}

const info = document.getElementById('info');
scene.onAfterRenderObservable.add(() => {
  const p = camera.position;
  info.textContent = `${defaults}\ntuned:    speed ${camera.speed}, angularSensibility ${camera.angularSensibility}, ` +
    `minZ ${camera.minZ}\nclick, then WASD / arrows to walk, drag to look — position ` +
    `(${p.x.toFixed(2)}, ${p.y.toFixed(2)}, ${p.z.toFixed(2)})`;
});
engine.runRenderLoop(() => scene.render());
addEventListener('resize', () => engine.resize());
</script>