ollow-engine
v5.6.5
Published
OLLOW Engine V5.6 — 2D/3D/vectoriel/rigging/physique pour le web moderne
Readme
OLLOW Engine V5.6 Ultra Web — Documentation complète
Moteur standalone 2D + 3D + vectoriel + rigging + physique avancée pour le web moderne. Script unique, détection automatique PC/mobile, aucune étape de build, aucune dépendance à un framework.
Version : 5.6.0
Profil : détecté automatiquement (pc ou mobile)
Dépendances : Three.js 0.160 (chargé via esm.sh)
Fonctionnalités énumérables : plus de 720 000
Table des matières
- Installation
- Démarrage rapide
- Profil et configuration
- Engine
- Scène 3D
- Géométries 3D
- Matériaux
- Lumières
- Physique avancée 3D
- Formes physiques
- Matériaux physiques
- Joints et contraintes
- Character Controller
- Véhicules
- Fluides
- Tissus et cordes
- Ragdolls
- Champs de force
- Customisation physique
- Requêtes physiques
- Debug physique
- Physique 2D
- Sprites 2D
- Animations 2D
- Tilemaps 2D
- Formes et texte 2D
- Camera 2D
- Scène 2D
- Vectoriel 3D
- Rigging squelettique
- Audio
- Contrôles
- Pathfinding A*
- ECS
- Plugins
- Bus d'événements
- Maths et bruit
- Easing
- Post-processing
- Manifeste
- PC vs Mobile
- Performance
- Dépannage
1. Installation
Copie ollow.js à côté de ton HTML, puis importe-le comme module ES.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<style>
html, body { margin: 0; height: 100%; overflow: hidden; background: #000; }
canvas { display: block; width: 100%; height: 100%; }
</style>
</head>
<body>
<canvas id="c"></canvas>
<script type="module">
import OLLOW from './ollow.js';
const engine = new OLLOW.Engine({ canvas3D: document.getElementById('c') });
const scene = engine.createScene3D();
engine.createCamera(70);
engine.createControls('orbit');
scene.addLight({ type: 'hemisphere', intensity: 0.7 });
scene.addLight({ type: 'directional', intensity: 1.2, position: [10, 20, 10] });
scene.addMesh({ geometry: 'box', position: [0, 0.5, 0] });
engine.start();
</script>
</body>
</html>Le CDN esm.sh doit être joignable au chargement. Pour un fonctionnement hors-ligne, héberge Three.js localement et remplace les imports en haut du fichier moteur.
2. Démarrage rapide
import OLLOW from './ollow.js';
const engine = new OLLOW.Engine({ canvas3D });
const scene = engine.createScene3D();
const camera = engine.createCamera(70);
const ctrl = engine.createControls('third-person');
scene.addLight({ type: 'hemisphere', color: 0x88aaff, groundColor: 0x222233, intensity: 0.7 });
scene.addLight({ type: 'directional', color: 0xfff0d0, intensity: 1.2, position: [30, 50, 20], shadowSize: 80 });
scene.setBackground(0x0a0a14);
scene.setFog(0x0a0a14, 40, 220);
scene.addMesh({
geometry: 'box',
size: [40, 0.5, 40],
position: [0, -0.25, 0],
material: { type: 'concrete' },
physics: { type: 'static', shape: OLLOW.PhysicsShape.aabb(40, 0.5, 40) },
});
const playerMesh = scene.addMesh({
geometry: 'capsule',
radius: 0.4, height: 1,
position: [0, 3, 0],
material: { type: 'standard', color: 0x60a5fa, emissive: 0x1e3a8a, emissiveIntensity: 0.3 },
});
const playerBody = new OLLOW.Rigidbody3D(playerMesh, {
shape: OLLOW.PhysicsShape.capsule(0.4, 1),
type: 'dynamic', mass: 1, fixedRotation: true,
});
scene.physics.add(playerBody);
engine.onUpdate((dt) => {
const axis = ctrl.getMoveAxis2D();
playerBody.velocity.x = axis.x * 8;
playerBody.velocity.z = axis.y * 8;
});
engine.start();3. Profil et configuration
Détection automatique :
console.log(OLLOW.BUILD); // 'pc' ou 'mobile'
console.log(OLLOW.CONFIG.profile); // idem| Réglage | PC | Mobile | |---|---|---| | Antialiasing | MSAA | désactivé | | Pixel ratio max | 2.0 | 1.5 | | Tone mapping | ACES | aucun | | Ombres | PCFSoft 2048 | désactivées | | Matériau défaut | standard (PBR) | lambert | | Post-processing | bloom + SSAO | désactivé | | Pas physique | 60 Hz | 30 Hz | | Substeps physiques | 4 | 2 | | Itérations solveur | 8 | 4 | | Particules max | 5 000 | 1 000 | | Plan lointain | 900 | 300 | | Contrôles | clavier + souris + manette | tactile + manette |
Tout est modifiable :
OLLOW.CONFIG.shadows = false;
OLLOW.CONFIG.physics.substeps = 6;
OLLOW.CONFIG.physics.integrator = 'rk4';
OLLOW.CONFIG.physics.gravity = { x: 0, y: -9.8, z: 0 };4. Engine
new OLLOW.Engine({ mode, canvas2D, canvas3D })
| Méthode | Retour | Description |
|---|---|---|
| createScene3D() | OllowScene3D | Crée une scène 3D. La première devient active. |
| createScene2D() | Scene2D | Crée une scène 2D. La première devient active. |
| createCamera(fov) | PerspectiveCamera | Caméra 3D. |
| createControls(type) | Controls | 'fps', 'orbit', 'third-person', '2d'. |
| setMode('2d'\|'3d') | this | Bascule entre canvas 2D et 3D. |
| enableBloom(strength) | UnrealBloomPass | PC uniquement. |
| shake(mag) | — | Secousse caméra 3D. |
| onUpdate(fn) | unsubscribe | Callback par frame. |
| start() / stop() | — | Boucle RAF. |
| loadGLTF(url) | Promise<GLTF> | Charge un glTF/glb. |
| loadImage(url) | Promise<Image> | Charge une image 2D. |
| stats | { fps } | FPS. |
| audio | AudioBus | Web Audio. |
| plugins | PluginHost | Système de plugins. |
| ecs | ECS | Entity-Component-System. |
| events | EventBus | Événements globaux. |
5. Scène 3D
scene.addMesh({
geometry: 'sphere',
radius: 0.5,
position: [0, 1, 0],
rotation: [0, Math.PI / 2, 0],
scale: [1, 1, 1],
material: { type: 'metal', color: 0xff6600 },
physics: { type: 'dynamic', mass: 1, restitution: 0.4, tag: 'ball' },
tag: 'enemy', name: 'sphereA', group: 'level1',
castShadow: true, receiveShadow: true,
});| Méthode | Description |
|---|---|
| addLight(spec) | Ajoute une lumière. |
| removeMesh(mesh) | Retire un mesh. |
| removeGroup(name) | Retire tous les meshes d'un groupe. |
| clear() | Vide la scène. |
| setBackground(couleur) | Fond uni. |
| setFog(couleur, near, far) | Brouillard linéaire. |
| setFogExp(couleur, densité) | Brouillard exponentiel. |
| raycast(origine, direction, dist, tag) | Raycast Three.js. |
| findByName(name) | Cherche un mesh par nom. |
| findByTag(tag) | Filtre par tag. |
| physics | Instance Physics3D de la scène. |
| particles | Particules 3D. |
| vector | Outils vectoriels 3D. |
6. Géométries 3D
spec.geometry accepte :
Primitives : box, sphere, plane, circle, cylinder, cone, torus, torusKnot, capsule, lathe, tube, extrude, ring, dodeca, icosa, octa, tetra.
Procédurales : terrain, rock, tree.
Utilitaires : crate, platform, wall, grid, merged.
7. Matériaux
spec.material.type accepte :
Base : standard, physical, lambert, phong, basic, toon, matcap, depth, normal, wireframe, points, line, dashed, sprite, shadow.
Presets : water, glass, metal, gold, silver, copper, iron, chrome, emissive, neon, glow, hologram, lava, ice, plasma, toxic, wood, marble, granite, concrete, brick, sand, grass, dirt, rock, rubber, plastic, ceramic, paper, bone, flesh, blood, oil, crystal, diamond, obsidian, amber.
Sur mobile, les matériaux PBR basculent automatiquement en Lambert.
8. Lumières
scene.addLight({ type: 'ambient', color: 0xffffff, intensity: 0.5 });
scene.addLight({ type: 'hemisphere', color: 0x88aaff, groundColor: 0x222233, intensity: 0.7 });
scene.addLight({ type: 'directional', color: 0xfff0d0, intensity: 1.2, position: [30, 50, 20], shadowSize: 80 });
scene.addLight({ type: 'point', color: 0xffaa00, intensity: 2, position: [0, 5, 0], distance: 30, decay: 2 });
scene.addLight({ type: 'spot', color: 0xffffff, intensity: 3, position: [0, 10, 0], target: [0, 0, 0], distance: 40, angle: Math.PI / 6, penumbra: 0.3 });Sur mobile, les ombres sont ignorées.
9. Physique avancée 3D
Le module Physics3D remplace le moteur AABB simple de la V5.5.
Caractéristiques
- Bodies : static, dynamic, kinematic.
- Formes : AABB, sphère, capsule, OBB, convex hull, triangle mesh, heightfield, compound.
- Intégrateurs : Euler, semi-implicite (défaut), Verlet, RK4, PBD, XPBD.
- Solveurs : impulsion (défaut), Gauss-Seidel, Jacobi, LCP.
- Broadphase : brute, grid, sweep-and-prune.
- Substeps configurables (4 par défaut sur PC).
- CCD (continuous collision detection).
- Sleep automatique.
- Events :
onBeginContact,onEndContact,onPreSolve,onPostSolve. - Filtres de collision par callback.
- Matériaux par paire.
Rigidbody3D
const rb = new OLLOW.Rigidbody3D(mesh, {
shape: OLLOW.PhysicsShape.aabb(1, 1, 1),
type: 'dynamic', // 'static' | 'dynamic' | 'kinematic'
mass: 2,
material: new OLLOW.PhysicsMaterial({ friction: 0.6, restitution: 0.4, density: 1 }),
gravityScale: 1,
useGravity: true,
ccd: true,
fixedRotation: false,
layer: 0, mask: 0xFFFF,
tag: 'enemy',
userData: { hp: 100 },
onBeginContact: (other, point, normal) => { /* ... */ },
onEndContact: (other) => { /* ... */ },
onPreSolve: (other, point, normal) => { /* ... */ },
onPostSolve: (other, point, normal, impulse) => { /* ... */ },
});
scene.physics.add(rb);Méthodes de Rigidbody3D
| Méthode | Description |
|---|---|
| applyForce(f) | Force au centre. |
| applyForceAt(f, point) | Force + couple. |
| applyImpulse(j) | Impulsion linéaire. |
| applyImpulseAt(j, point) | Impulsion + couple. |
| applyTorque(t) | Couple. |
| setVelocity(v) | Vitesse linéaire. |
| setAngularVelocity(v) | Vitesse angulaire. |
| wake() / sleep() | Force le réveil / sommeil. |
| worldAABB | Boîte englobante actuelle. |
| speed | Norme de la vélocité. |
| kineticEnergy | Énergie cinétique. |
Physics3D — API
scene.physics.add(body);
scene.physics.remove(body);
scene.physics.addJoint(joint);
scene.physics.addForceField(field);
scene.physics.addMaterial('glace', mat);
scene.physics.setPairMaterial('glace', 'caoutchouc', mat);
scene.physics.registerForce(name, fn);
scene.physics.registerBodyType(name, def);
scene.physics.registerIntegrator(name, fn);
scene.physics.registerFilter(fn);
scene.physics.applyForce(name, body, dt);
scene.physics.gravitySet(x, y, z);
scene.physics.pause() / resume();
scene.physics.step(dt); // appelé automatiquement par le moteur10. Formes physiques
OLLOW.PhysicsShape.aabb(w, h, d)
OLLOW.PhysicsShape.sphere(r)
OLLOW.PhysicsShape.capsule(r, h)
OLLOW.PhysicsShape.obb(w, h, d, quat)
OLLOW.PhysicsShape.convex(points)
OLLOW.PhysicsShape.mesh(vertices, indices)
OLLOW.PhysicsShape.heightfield(heights, sx, sz)
OLLOW.PhysicsShape.compound([shape1, shape2])11. Matériaux physiques
const glace = new OLLOW.PhysicsMaterial({
friction: 0.05,
restitution: 0.1,
density: 0.9,
linearDamping: 0.01,
angularDamping: 0.02,
});
scene.physics.addMaterial('glace', glace);
scene.physics.setPairMaterial('glace', 'metal', new OLLOW.PhysicsMaterial({ friction: 0.3, restitution: 0.4 }));12. Joints et contraintes
new OLLOW.Joint3D(bodyA, bodyB, {
type: 'spring', // 'distance', 'spring', 'hinge', 'ball', 'slider',
// 'wheel', 'gear', 'pulley', 'point', 'magnet',
// 'breakable', 'motor'
anchorA: new THREE.Vector3(0, 0, 0),
anchorB: new THREE.Vector3(0, 0, 0),
length: 2,
stiffness: 40,
damping: 4,
min: -Math.PI / 4,
max: Math.PI / 4,
motorSpeed: 0,
motorForce: 0,
breakForce: 500,
});
scene.physics.addJoint(joint);Types disponibles :
| Type | Description |
|---|---|
| distance | Distance fixe entre deux points. |
| spring | Ressort de Hooke (raideur + amortissement). |
| hinge | Charnière (1 axe libre). |
| ball | Rotule (3 axes libres). |
| slider | Glissière (1 axe libre). |
| wheel | Roue (rotation + suspension). |
| gear | Engrenage. |
| pulley | Poulie. |
| point | Point-à-point. |
| magnet | Ressort vers une cible. |
| breakable | Cassable au-delà d'une force. |
| motor | Moteur avec vitesse cible. |
13. Character Controller
const char = new OLLOW.CharacterController3D(body, {
maxSlope: Math.PI / 4,
stepHeight: 0.4,
snapToGround: true,
coyoteTime: 0.15,
jumpBuffer: 0.2,
airControl: 0.4,
wallSlide: 0.2,
ledgeGrab: true,
});
engine.onUpdate((dt) => {
char.update(dt, scene.physics);
if (controls.isDown('Space')) char.jump();
});14. Véhicules
const chassis = new OLLOW.Rigidbody3D(mesh, {
shape: OLLOW.PhysicsShape.aabb(2, 0.5, 4),
type: 'dynamic', mass: 500,
});
scene.physics.add(chassis);
const car = new OLLOW.Vehicle3D(chassis, {
wheelBase: 2.5, track: 1.8,
wheelRadius: 0.3,
suspensionRest: 0.4,
suspensionStiffness: 30,
suspensionDamping: 4,
maxEngineForce: 3000,
maxBrakeForce: 5000,
maxSteeringAngle: Math.PI / 6,
});
car.addWheel('FL', new THREE.Vector3(-0.9, 0, 1.2), { isSteering: true });
car.addWheel('FR', new THREE.Vector3(0.9, 0, 1.2), { isSteering: true });
car.addWheel('RL', new THREE.Vector3(-0.9, 0, -1.2), { isDriven: true });
car.addWheel('RR', new THREE.Vector3(0.9, 0, -1.2), { isDriven: true });
engine.onUpdate((dt) => {
car.throttle(controls.isDown('KeyW') ? 1 : 0);
car.brake(controls.isDown('Space') ? 1 : 0);
car.steer(controls.getMoveAxis2D().x * 0.5);
car.update(dt);
});15. Fluides
SPH (Smoothed Particle Hydrodynamics).
const fluid = new OLLOW.Fluid3D({
count: 800,
radius: 3,
restDensity: 1000,
gasConstant: 2000,
viscosity: 0.1,
color: 0x2266aa,
bounds: { minX: -10, maxX: 10, minY: 0, maxY: 20, minZ: -10, maxZ: 10 },
});
const points = fluid.toPoints();
scene.three.add(points);
engine.onUpdate((dt) => {
fluid.step(dt);
const pos = points.geometry.attributes.position;
for (let i = 0; i < fluid.particles.length; i++) {
pos.setXYZ(i, fluid.particles[i].position.x, fluid.particles[i].position.y, fluid.particles[i].position.z);
}
pos.needsUpdate = true;
});16. Tissus et cordes
const cloth = new OLLOW.Cloth3D({
cols: 20, rows: 20,
size: 4, mass: 0.1,
stiffness: 1, damping: 0.1,
anchors: ['0,0', '19,0'],
});
const clothMesh = cloth.toMesh();
scene.three.add(clothMesh);
engine.onUpdate((dt) => {
cloth.step(dt);
const pos = clothMesh.geometry.attributes.position;
for (let i = 0; i < cloth.particles.length; i++) pos.setXYZ(i, cloth.particles[i].position.x, cloth.particles[i].position.y, cloth.particles[i].position.z);
pos.needsUpdate = true;
clothMesh.geometry.computeVertexNormals();
});17. Ragdolls
const skel = OLLOW.buildHumanoidSkeleton();
const ragdoll = new OLLOW.Ragdoll3D(skel);
ragdoll.build(scene.physics);
// Activation
ragdoll.activate();
// Désactivation
ragdoll.deactivate();18. Champs de force
const wind = new OLLOW.ForceField3D({
type: 'wind', // 'wind', 'gravity', 'attract', 'repel',
// 'vortex'
position: new THREE.Vector3(0, 10, 0),
direction: new THREE.Vector3(1, 0, 0),
strength: 20,
radius: 30,
falloff: 1,
});
scene.physics.addForceField(wind);19. Customisation physique
Force custom
scene.physics.registerForce('tornade', (body, dt) => {
const toCenter = body.position.clone().negate();
const dist = toCenter.length();
if (dist > 20) return null;
const s = 50 / Math.max(dist, 1);
return { x: toCenter.x * s, y: 5, z: toCenter.z * s };
});
scene.physics.applyForce('tornade', body, dt);Intégrateur custom
scene.physics.registerIntegrator('verletMaison', (body, dt, world) => {
const prevPos = body.userData.prevPos || body.position.clone();
body.userData.prevPos = body.position.clone();
body.position.addScaledVector(body.velocity, dt);
body.velocity.addScaledVector(world.gravity, dt);
});
OLLOW.CONFIG.physics.integrator = 'verletMaison';Filtre de collision
scene.physics.registerFilter((a, b) => {
if (a.tag === 'projectile' && b.isTrigger) return false;
if (b.tag === 'projectile' && a.isTrigger) return false;
return true;
});Type de body custom
scene.physics.registerBodyType('monstre', {
integrate: (body, dt) => { /* ... */ },
collide: (body, other) => { /* ... */ },
solve: (body, dt) => { /* ... */ },
});20. Requêtes physiques
const hit = scene.physics.raycast(
new THREE.Vector3(0, 10, 0),
new THREE.Vector3(0, -1, 0),
100, 'enemy'
);
const inSphere = scene.physics.overlapSphere(new THREE.Vector3(0, 0, 0), 5);
const inBox = scene.physics.overlapAABB({
min: { x: -5, y: 0, z: -5 },
max: { x: 5, y: 10, z: 5 },
});21. Debug physique
scene.physics.debug.wireframe = true;
scene.physics.debug.contacts = true;
scene.physics.debug.aabb = true;
scene.physics.debug.joints = true;
scene.physics.debug.forces = true;
scene.physics.debug.sleep = true;
scene.physics.debug.velocity = true;
scene.physics.debug.mass = true;22. Physique 2D
const scene2 = engine.createScene2D();
const sprite = new OLLOW.Sprite2D({ image, x: 100, y: 100 });
scene2.addSprite(sprite);
const body = new OLLOW.Body2D(sprite, {
type: 'dynamic',
width: 32, height: 32,
mass: 1,
restitution: 0.2,
friction: 0.9,
useGravity: true,
isTrigger: false,
tag: 'player',
layer: 0, mask: 0xFFFF,
onCollision: (other) => { /* ... */ },
});
scene2.physics.add(body);
engine.onUpdate((dt) => {
const axis = engine.controls.getMoveAxis2D();
body.velocity.x = axis.x * 200;
});
// Requêtes
const hit = scene2.physics.raycast(x, y, dx, dy, maxDist);
const inAABB = scene2.physics.overlapAABB({ minX: 0, minY: 0, maxX: 100, maxY: 100 });
// Custom
scene2.physics.registerForce('vent', (body, dt) => ({ x: 100, y: 0 }));
scene2.physics.registerIntegrator('verlet2D', (b, dt) => { /* ... */ });
scene2.physics.registerFilter((a, b) => true);23. Sprites 2D
const sprite = new OLLOW.Sprite2D({
image: monImage,
x: 100, y: 100,
scaleX: 2, scaleY: 2,
rotation: 0,
anchorX: 0.5, anchorY: 0.5,
opacity: 1,
tint: null,
visible: true,
zIndex: 0,
blendMode: 'source-over',
tag: 'player',
});
sprite.setFrame(sx, sy, sw, sh);
sprite.setPosition(200, 300);
sprite.getBounds(); // { x, y, w, h, minX, minY, maxX, maxY }
sprite.contains(px, py); // bool
sprite.draw(ctx);24. Animations 2D
const anim = new OLLOW.AnimatedSprite2D({ image: spritesheet, x: 0, y: 0 });
anim.addAnimation('idle', [
{ x: 0, y: 0, w: 32, h: 32 },
{ x: 32, y: 0, w: 32, h: 32 },
], 8);
anim.addAnimation('run', [
{ x: 0, y: 32, w: 32, h: 32 },
{ x: 32, y: 32, w: 32, h: 32 },
], 12);
anim.play('idle', { loop: true });
anim.play('run', { loop: false });
engine.onUpdate((dt) => anim.update(dt));25. Tilemaps 2D
const map = new OLLOW.Tilemap2D({
tileSize: 32,
width: 100,
height: 100,
tileset: monTileset,
});
map.addLayer('background');
map.addLayer('solids');
map.setCollisionLayer('solids');
map.setTile('solids', x, y, tileIndex);
map.getTile('solids', x, y);
map.isSolid(x, y);
scene2.addTilemap(map);26. Formes et texte 2D
// Formes
const rect = new OLLOW.Shape2D('rect', { x: 0, y: 0, w: 100, h: 50, fill: 'red' });
const circle = new OLLOW.Shape2D('circle', { x: 100, y: 100, radius: 25, fill: 'blue' });
const poly = new OLLOW.Shape2D('polygon', { points: [[0,0],[50,0],[25,50]], fill: 'green' });
// Texte
const label = new OLLOW.Text2D('Score : 0', {
x: 20, y: 20,
font: 'bold 24px monospace',
fill: '#fff',
stroke: '#000', lineWidth: 3,
align: 'left', baseline: 'top',
});27. Camera 2D
const cam = scene2.camera;
cam.follow(player, 0.1);
cam.shake(15);
cam.setBounds(-1000, -1000, 1000, 1000);
cam.zoom = 2;
cam.update(dt);
cam.apply(ctx);
cam.screenToWorld(sx, sy);
cam.worldToScreen(wx, wy);28. Scène 2D
const scene2 = engine.createScene2D();
scene2.background = '#000';
scene2.addSprite(sprite);
scene2.addShape(shape);
scene2.addText(text);
scene2.addTilemap(tilemap);
scene2.addHud(element);
scene2.removeSprite(sprite);
scene2.removeGroup('enemies');
scene2.clear();29. Vectoriel 3D
VectorPath
const path = new OLLOW.VectorPath();
path.moveTo(0, 0);
path.lineTo(10, 0);
path.quadTo(15, 5, 20, 0);
path.bezierTo(25, -5, 30, 5, 35, 0);
path.close();
path.rect(0, 0, 10, 5);
path.circle(0, 0, 5);
path.polygon([[0,0],[10,0],[10,10],[0,10]]);
path.star(0, 0, 10, 4, 5);
path.toPoints();
path.toShape(); // THREE.ShapeVector3D
const v = scene.vector;
v.extrude(path, { depth: 0.5, bevel: true, material: { type: 'gold' } });
v.vectorPlane(drawFn, { width: 4, height: 2, resolution: 1024 });
v.tube([[0,0,0],[1,1,0],[2,0,0]], { radius: 0.1, material: { type: 'neon' } });
v.lines3D([[[0,0,0],[1,1,1]]], { color: 0xff0000 });
v.groundGrid(20, 20, 0x444444);vectorDraw (helpers canvas)
OLLOW.vectorDraw.clear(ctx, w, h);
OLLOW.vectorDraw.path(ctx, path, { fill: 'red', stroke: 'black' });
OLLOW.vectorDraw.circle(ctx, x, y, r, { fill: 'blue' });
OLLOW.vectorDraw.rect(ctx, x, y, w, h, { fill: 'green' });
OLLOW.vectorDraw.text(ctx, 'texte', x, y, { fill: 'white' });30. Rigging squelettique
const skel = OLLOW.buildHumanoidSkeleton();
const hips = skel.find('Hips');
const head = skel.find('Head');
head.setLocalEuler(0, 0, Math.PI / 4);
skel.update();Bone
const bone = new OLLOW.Bone('UpperArm', 0.3);
bone.setLocalPosition(0, 0, 0);
bone.setLocalEuler(0, 0, -Math.PI / 2);
bone.reset();
bone.captureBindPose();
bone.updateWorld();
bone.find('Hand');
bone.flatten();Skeleton
const skel = new OLLOW.Skeleton('perso');
const root = new OLLOW.Bone('Hips');
skel.addBone(root);
skel.addBone(new OLLOW.Bone('Spine'), root);
skel.captureBindPose();
skel.update();
skel.resetPose();31. Audio
await engine.audio.unlock();
await engine.audio.load('/sfx/hit.wav', 'hit');
engine.audio.play('hit', { volume: 1, loop: false, position: [x, y, z], rate: 1 });
engine.audio.setMasterVolume(0.8);
engine.audio.beep({ freq: 440, duration: 0.1, type: 'sine' });32. Contrôles
controls.setSensitivity(0.0022);
controls.setSpeed(8);
controls.setTarget(mesh);
controls.vibrate(30);
controls.isDown('Space');
controls.isPressed(0);
const axis = controls.getMoveAxis2D();Gestes tactiles :
controls.gestures.tap = (e) => {};
controls.gestures.doubleTap = (e) => {};
controls.gestures.longPress = (e) => {};
controls.gestures.swipe = ({ dx, dy, direction }) => {};
controls.gestures.pinch = (scale) => {};33. Pathfinding A*
const nav = new OLLOW.NavGrid({ width: 120, depth: 120, cell: 1, heightFn: (x, z) => 0 });
nav.block(5, 5);
nav.unblock(5, 5);
const path = nav.findPath([0, 0, 0], [10, 0, 10]);34. ECS
const id = engine.ecs.createEntity('player');
engine.ecs.addComponent(id, 'position', { x: 0, y: 0, z: 0 });
engine.ecs.addComponent(id, 'health', { hp: 100 });
engine.ecs.addSystem((ecs, dt) => {
for (const e of ecs.query('position', 'health')) {
const p = ecs.getComponent(e, 'position');
const h = ecs.getComponent(e, 'health');
}
});
engine.ecs.destroyEntity(id);35. Plugins
engine.plugins.use({
name: 'mon-plugin',
setup(api) {
api.on('update', dt => {});
api.on('render', () => {});
}
});36. Bus d'événements
const off = engine.events.on('update', dt => {});
engine.events.emit('custom', { data: 1 });
off();37. Maths et bruit
Math : clamp, lerp, invLerp, remap, rand, randI, pick, shuffle, damp, smoothstep, smootherstep, pingpong, wrap, angleLerp, deg2rad, rad2deg, dist, dist2, dist2D, uuid, sleep.
Bruit : noise1, noise2, fbm2, perlin2, gaussian.
38. Easing
30 fonctions : linear, quadIn/Out/InOut, cubicIn/Out/InOut, quartIn/Out, quintIn/Out, expoIn/Out, circIn/Out, backIn/Out, elasticIn/Out, bounceIn/Out, sineIn/Out, smoothstep, spring.
39. Post-processing
engine.enableBloom(0.6);PC uniquement.
40. Manifeste
OLLOW.features.count(); // > 720 000
OLLOW.features.list();
OLLOW.features.categories();
OLLOW.features.stats();
OLLOW.features.query(f => f.category === 'physicsShape3D');
OLLOW.features.find('physicsJoint3D', 'spring');Catégories principales :
primitive3D,material3D,light3D,postfx,particle3DphysicsShape3D,physicsJoint3D,physicsIntegrator,physicsSolver,physicsBroadphase,physicsMaterial,forceField,fluid,cloth,characterController,ragdoll,customPhysics,debugPhysicssprite2D,anim2D,tile2D,physics2D,shape2D,text2D,camera2D,hud2D,gesturevector,rig,input,event,camera3D,uiPreset,biome,easing,noisecombo3D-*,comboPhysics-*,combo2D-*,comboCross-*,bigInput3D,bigPhysics,bigPhysicsCore,hueVariant3D,physicsMatVariant
41. PC vs Mobile
| Réglage | PC | Mobile | |---|---|---| | Antialiasing | MSAA | désactivé | | Pixel ratio max | 2.0 | 1.5 | | Tone mapping | ACES | aucun | | Ombres | PCFSoft 2048 | désactivées | | Matériau défaut | standard (PBR) | lambert | | Post-processing | bloom + SSAO | désactivé | | Pas physique | 60 Hz | 30 Hz | | Substeps physiques | 4 | 2 | | Itérations solveur | 8 | 4 | | Particules max | 5 000 | 1 000 | | Plan lointain | 900 | 300 |
42. Performance
- Limite les draw calls < 100 mobile, < 200 PC.
- Réutilise
reuseGeometry/reuseMaterial. - Utilise
isInstanced: true+countpour les objets répétés. - Ne crée jamais de mesh dans
onUpdate. - Réduis
CONFIG.physics.substepssi la physique saccade. - Bascule en
physics.integrator = 'semi-implicit'sirk4coûte trop. - Utilise
physics.broadphase = 'sweep'au-delà de 100 bodies. - Active
ccduniquement sur les projectiles rapides. - Désactive les
onBeginContact/onPostSolvesur les bodies qui n'en ont pas besoin.
43. Dépannage
| Symptôme | Cause | Solution |
|---|---|---|
| Canvas noir | Pas de caméra ou de lumière | createCamera() + au moins une lumière. |
| Le joueur traverse le sol | Pas de body physique | Ajoute physics: { type: 'static' } au sol. |
| Objets qui s'enfoncent | Substeps trop bas | Augmente CONFIG.physics.substeps. |
| Tremblements | Solveur instable | Augmente les itérations solveur. |
| Meshes invisibles mobile | Matériau PBR | Utilise lambert / basic / toon. |
| Pointer lock refusé | Pas un geste utilisateur | Appelle depuis un clic. |
| Audio silencieux | Context verrouillé | engine.audio.unlock() depuis un clic. |
| THREE is not defined | Import séparé | Utilise OLLOW.THREE. |
| Joints qui explosent | Stiffness trop haut | Réduis stiffness et augmente les itérations. |
| Fluides très lents | Trop de particules | Réduis count ou passe à count: 300. |
| Ragdoll mou | Masses incohérentes | Aligne masses avec les os. |
| Character qui grimpe aux murs | maxSlope trop grand | Réduis maxSlope à π/6. |
Licence
Livré tel quel pour le projet du destinataire. Three.js est sous licence MIT ; conserve son en-tête si tu redistribues le moteur.
