← All Baltor Engineering Posts · Post 04 of 08 · 2026-10-18 · Game Engines & Physics · 8 min read · Raw Markdown (.md) · Open Live Studio ↗

Building Playable Godot 4 and WebGL Games That Survive Revision

Why single-file AI games break on the second edit, and how decoupling scene trees, swept-AABB collision controllers, and barycentric terrain samplers keeps creative projects editable.


SWEPT-AABB SLIDING CONTROLLER + BARYCENTRIC TERRAIN NORMAL ALIGNMENT 1. SWEPT-AABB TOI SOLVER t_entry = (B_min - A_max) / v t_exit = (B_max - A_min) / v Project residual velocity onto contact tangent plane (up to 3x) 0.0% wall tunneling at 60 Hz 2. BARYCENTRIC HEIGHT & NORMAL y(x,z) = u*y0 + v*y1 + w*y2 N = normalize((P1-P0) x (P2-P0)) Pitch & roll root bone to match hillside slope + 4-paw IK plant Paw-to-ground error < 0.4 cm 3. REVISION SURVIVAL Edit biome or enemy AI without regenerating physics or rig Clean reopen in Godot 4 / WebGL 10/10 Specs Preserved

Most AI game demos fail on two fronts: either the character controller clips through sloped terrain and tunnels through walls on the first run, or the second prompt ("add a companion firefly and a suspension bridge over the pond") overwrites the working movement code and breaks the scene. On fox.baltor.ai and parkour.baltor.ai, Baltor structures playable WebGL2 and Godot 4 projects around modular scene components and verified physics primitives so that unchanged subsystems remain locked across revisions.

Why naive discrete collision checks fail

When an unassisted model writes a 3D platformer or woodland exploration game, it almost always checks collision after moving the character (position.addScaledVector(velocity, dt)). If the player jumps toward a thin wall or lands from a high ledge during a 28 ms frame, the bounding box steps completely across the obstacle boundary in a single tick.

Baltor's movement component (physics.controller.swept_aabb.v1) uses continuous slab-method Time-of-Impact (TOI) queries with three-iteration slide projection:

// Continuous Swept-AABB Collision & Slide Solver
export function moveAndSlideSweptAABB(box, velocity, colliders, dt) {
  let remainingDt = Math.min(dt, 0.0333);
  let vel = { ...velocity };
  let grounded = false;

  for (let iter = 0; iter < 3 && remainingDt > 1e-5; iter++) {
    let earliestToi = 1.0;
    let hitNormal = null;

    for (const obstacle of colliders) {
      const hit = intersectMovingAABB(box, vel, obstacle, remainingDt);
      if (hit && hit.toi < earliestToi) {
        earliestToi = hit.toi;
        hitNormal = hit.normal;
      }
    }

    const safeAdvance = Math.max(0.0, earliestToi - 1e-4) * remainingDt;
    box.x += vel.x * safeAdvance;
    box.y += vel.y * safeAdvance;
    box.z += vel.z * safeAdvance;

    if (!hitNormal) break;
    if (hitNormal.y > 0.7) grounded = true;

    const vn = vel.x * hitNormal.x + vel.y * hitNormal.y + vel.z * hitNormal.z;
    if (vn < 0) {
      vel.x -= vn * hitNormal.x;
      vel.y -= vn * hitNormal.y;
      vel.z -= vn * hitNormal.z;
    }
    remainingDt *= (1.0 - earliestToi);
  }
  return { position: box, velocity: vel, grounded };
}

Barycentric terrain sampling and quadruped gait alignment

In Foxglade: The Living Wood (fox.baltor.ai), the red fox traverses rolling hills, a catenary wooden bridge, and shoreline rocks. Sampling terrain height at the character origin alone leaves the front paws floating in mid-air on downhill slopes and buried inside the mesh on uphill climbs.

The Baltor terrain adapter queries exact barycentric triangle heights (u * y0 + v * y1 + w * y2) at four independent paw raycast sockets (FL, FR, HL, HR), pitches the pelvis and chest bones along the filtered terrain normal, and blends a 17-joint RTMPose rotary-gallop keyframe cycle (fox.baltor.ai/motion-study.html). When a user later revises the lighting or adds a quest marker, the rig, terrain sampler, and swept-AABB controller remain untouched in their component slots.


Navigation Post Title Direct Links
Previous Post Eliminating Non-Manifold LLM Meshes in Blender bpy and Three.js r170+
Next Post 60 FPS Motion Graphics: Why Spring Physics and DFT Epicycles Beat CSS Keyframes
Blog Index & Feeds Return to Baltor Engineering Blog Index (8 Posts)