Skip to content

Repository files navigation

🎮 3D Character Animation Controller V2 for Babylon.js

An advanced third-person character locomotion and physics framework built with Babylon.js. This framework provides a fluid, powerful, and easy-to-use Character Controller with integrated physics, animations, and high-end visual features.

🎮 Live Demo: https://viseni.com/demos/bjs_character_controller_v2/

BJS Character Controller V2 Screenshot

☕ If this controller saves you time, consider supporting its development!

Buy Me A Coffee


🚀 Key Features

  • Dual-Movement Modes (Physics vs Kinematic): Toggle dynamically between Havok Physics (dynamic simulation with body bodies) and standard Kinematic Collisions (ellipsoid-based movement) directly from the HUD.
  • Locomotion Blend Tree: Smoothly blends weight and speed between Idle, Walk, and Sprint.
  • Dual-State Toggle Coexistence: Crouch and Sprint operate as persistent toggles and can co-exist (allowing crouch-running).
  • Dynamic Zoom & Camera Follow: Smooth camera tracking with automated user-zoom sync (mouse wheel, trackpad, pinch) and double-tap recentering.
  • Dynamic FOV & Camera Shake: Camera Field of View expands with speed. Rotational camera shake is triggered on landing impacts relative to fall height.
  • Camera Follow Lock (Direct Steering): Locks the camera directly behind the character for tank-style direct controls.
  • Visual Enhancements: Procedural dust/smoke trails at the feet, procedural leaning/banking on turns, slope-incline alignment, and squash & stretch scaling.
  • Collision height adjustments & Ceiling protection: Shrinks the capsule automatically when crouching/rolling, prevents standing up or rolling under low ceilings, and expands width when sprinting to prevent wall clipping.
  • Ledge & Stairs Snapping: Keeps the character grounded on sloped surfaces and stairs to prevent airborne jitter.
  • Slope-Aligned Kinematic Traversal: Kinematic collisions mode projects movement directly onto the ground normal to ensure butter-smooth ascent/descent on ramps and slopes.
  • Smart Snap-Down Controls: Dynamically disables downward snap forces when ascending stairs or steep slopes to eliminate physics/collision jitter.
  • Implicit Self-Collision Prevention: Prevents parent-capsule jitter by automatically disabling collision checks (checkCollisions = false) on imported character visual meshes.
  • Mobile Touch Support: Responsive virtual joystick and customizable glassmorphism action buttons.
  • Gamepad Support: Analog movement with configurable dead zone and edge-triggered jump, roll, sprint, crouch and action buttons.
  • Production Rigging Pipeline: Human and quadruped analysis, body-mesh selection, editable canonical bone assignment, topology-aware auto-rigging, quality diagnostics and deterministic animation retargeting.
  • Air Dash (Mid-Air Roll): Perform a responsive dodge roll in mid-air with a horizontal speed boost and a 55% jump-power vertical hop (available if Double Jump is enabled, works even after double jumping).
  • Action Interrupt Roll: Pressing Roll immediately interrupts active attack combos or spell casts for instant responsiveness.
  • Roll Cooldown & HUD Feedback: A 1.1s cooldown prevents roll spamming, displaying a "DODGE COOLDOWN" HUD warning when pressed too early.
  • Toggleable Action HUD Texts: Toggle on-screen action text alerts (like "AIR DASH", "JAB", "CROSS!") directly from the System & UI settings drawer.

⚖️ Physics vs. Kinematic Modes

character-controller.js is a unified single-file engine that runs in two distinct physics regimes. Both modes live in the same class — a single usePhysics flag switches the internal code paths at initialization time.

  • Havok Physics (Default): Leverages the WASM-powered Havok Physics engine. The character capsule is created as a dynamic PhysicsBody with defined mass and inertia properties, interacting naturally with other dynamic aggregates (like boxes, cylinders, and triggers).
  • Kinematic Collisions: Runs entirely within Babylon's native collision engine using kinematic ellipsoids (moveWithCollisions). Havok initialization is skipped entirely, providing maximum performance and deterministic locomotion.

Explicit configuration (default behaviour)

Runtime options are authoritative. initPhysics(scene) tries Havok and falls back to kinematic collisions without reading or modifying browser storage. Pass usePhysics: false when kinematic mode is required.

Overriding the mode

Preference persistence is deliberately opt-in. The bundled HUD examples use persistPreferences: true; embedded applications remain deterministic by default:

constusePhysics=awaitinitPhysics(scene,{usePhysics: true,persistPreferences: true});

The same policy is available on the controller constructor:

constcharCtrl=newCharCtrl(playerCapsule,charRoot,camera,animCtrl,scene,{usePhysics: true,// or falsepersistPreferences: false,config: {SPEED_MULTIPLIER: 1.5// Multiplies walking, running and jogging speeds}});

⚙️ Configuration Parameters

The config object in the constructor accepts a wide range of physics, camera, and gameplay properties to fine-tune character behavior:

ParameterDefaultTypeDescription
GRAV22numberGravity force pulling the character down
JUMP_PWR9.5numberVertical takeoff impulse force for jumping
SPD_WALK2.5numberMaximum physical walking speed
SPD_JOG3.0numberMaximum physical jogging speed (blend speed threshold)
SPD_SPRINT5.0numberMaximum physical sprinting speed
SPD_CROUCH2.0numberMaximum physical crouching walk speed
SPD_CROUCH_RUN3.2numberMaximum physical crouching run speed
ACCEL14numberMovement acceleration rate (speed-up responsiveness)
DECEL16numberMovement deceleration rate (braking/stopping responsiveness)
ROT_SPD40numberCharacter yaw rotation speed responsiveness
AIR_CONTROLfalsebooleanSteering control in mid-air (true = full control, false = no control)
DYNAMIC_FOVtruebooleanDynamically adjust camera Field of View based on speed
DYNAMIC_FOV_MAX0.10numberMaximum camera FOV expansion amount at full sprint speed
CAM_FOLLOW_LOCKtruebooleanIf true, the camera is locked behind the character's facing direction
CAM_FOLLOW_PITCH1.047numberCamera follow lock pitch (beta angle in radians, approx 60 degrees)
CAM_FOLLOW_DIST8.0numberCamera follow lock distance (radius in meters)
CAM_LOCK_PITCHfalsebooleanIf true, drag input only rotates camera horizontally (locks pitch axis)
JOYSTICK_LOCK_XfalsebooleanIf true, joystick input is locked to vertical axis only (no strafing)
DOUBLE_JUMP_ENABLEDtruebooleanIf true, the character can perform a double jump in mid-air
SPEED_MULTIPLIER1.0numberSpeed multiplier for walking, running, and jogging
PLAY_PARTICLEStruebooleanPlay procedural dust/smoke particles under the character's feet

🔄 Dynamic Animation Remapping

You can dynamically change any animation on the character controller or adjust keyframe ranges at runtime using the AnimCtrl instance (accessed via charCtrl.anim):

1. Reassigning Animations (Setters)

Pass a new Babylon AnimationGroup to dynamically swap any of the pre-mapped animations:

// Remap basic locomotioncharCtrl.anim.setWalkAnim(newWalkAnimGroup);charCtrl.anim.setRunAnim(newRunAnimGroup);charCtrl.anim.setIdleAnim(newIdleAnimGroup);// Remap crouch statescharCtrl.anim.setCrouchIdleAnim(newCrouchIdle);charCtrl.anim.setCrouchFwdAnim(newCrouchWalk);// Remap jumps and actionscharCtrl.anim.setJumpStartAnim(newJumpStart);charCtrl.anim.setJumpLoopAnim(newJumpLoop);charCtrl.anim.setJumpLandAnim(newJumpLand);charCtrl.anim.setRollAnim(newRoll);charCtrl.anim.setPunchJabAnim(newPunchJab);charCtrl.anim.setPunchCrossAnim(newPunchCross);charCtrl.anim.setSpellEnterAnim(newSpellEnter);charCtrl.anim.setSpellShootAnim(newSpellShoot);charCtrl.anim.setSpellExitAnim(newSpellExit);charCtrl.anim.setInteractAnim(newInteract);// Remap any custom animation keycharCtrl.anim.setAnimation('Custom_State_Name',myAnimGroup);

2. Modifying Playback Keyframe Ranges

Change the start/end frames of an animation without replacing the group:

// setAnimationRanges(animKey, startFrame, endFrame)charCtrl.anim.setAnimationRanges('Walk_Loop',10,45);

🕹️ Controls Layout

Keyboard (PC):

  • W, A, S, D / Arrow Keys: Movement.
  • Shift: Sprint (Toggle).
  • Ctrl: Crouch (Toggle).
  • Space: Jump / Double Jump.
  • R: Dodge roll / Air Dash:
    • Action Interrupt: Instantly cancels active attack combos or spell casts.
    • Roll Cooldown: 1.1s cooldown between rolls (triggers a "DODGE COOLDOWN" HUD alert).
    • Air Dash: If Double Jump is enabled in settings, performs a mid-air roll with a horizontal boost and a 55% jump-power vertical hop (usable even after double jumping).
  • Q: Punch combo.
  • E: Spell casting.
  • F: Interaction.
  • Mouse Drag: Orbit camera / Double-click to recenter.

Mobile Touch:

  • Left Hand: Floating Analog Joystick.
  • Right Hand (Buttons): SPELL, ACT, CROUCH, ROLL, SPRINT, JUMP.
  • Canvas Double-Tap: Recenter camera.

🛠️ Implementation Quickstart

The js/ directory is organized into subfolders by role:

  • js/character-controller.js — Unified core engine. Handles Havok Physics and Kinematic modes, locomotion state machines, and animation blending. Exports initPhysics and setupCharacter helpers.
  • js/ui/custom-hud.js — Tactile settings overlay (Camera Lock, Physics toggle, Dynamic FOV, Hide Cursor, Double Jump, Air Control, sliders). Optional.
  • js/ui/custom-pointer.js — Spring-damper trailing cursor ring. Optional.
  • js/examples/ — Ready-to-run setup templates (app.js, app-minimal.js, app-complex.js).
  • js/core/builder.js — Powers builder.html, the visual configuration tool (see below).

⚡ High-Level Setup (Recommended)

You can initialize physics and load the character in just a few lines of code using the shared helper functions: initPhysics and setupCharacter (wrapped in a clean loadCharacter helper function across the app templates). This helper supports configuring model paths, spawn locations, bounding ellipsoids, controls, and animations:

// 1. Define character initialization helperasyncfunctionloadCharacter(scene,shadow,camera,usePhysics){returnsetupCharacter(scene,camera,usePhysics,{
shadow,// Optional: shadow generator to add character meshes topersistPreferences: true,// Optional: allow HUD/browser preference persistenceassetsPath: 'assets/',// Optional: path to GLB assets folder (defaults to 'assets/')filename: 'character_animated.glb',// Optional: GLB file name (defaults to 'character_animated.glb')spawnPosition: newBABYLON.Vector3(0,2,0),// Optional: starting position overrideellipsoid: newBABYLON.Vector3(0.35,0.96,0.35),// Optional: collision ellipsoid overridekeys: {JUMP: ['KeyK']},// Optional: remap keyboard controls directlyconfig: {JUMP_PWR: 12},// Optional: override physical and camera parametersconfigure: ({ animCtrl, filteredGroups })=>{// Optional: callback to remap animations or customize keyframe rangesanimCtrl.setWalkAnim(filteredGroups[15]);}});}// 2. Initialize physics (Havok or Kinematic fallback)constusePhysics=awaitinitPhysics(scene,{persistPreferences: true});// 3. Load the character using the helperconst{ playerCapsule, animCtrl, charCtrl }=awaitloadCharacter(scene,shadow,camera,usePhysics);// 4. Hook up HUD setting toggles dynamically via custom-hud.jsif(typeofbindHUDControls==='function'){bindHUDControls(charCtrl,camera,usePhysics);}

We have provided three setup examples to guide your implementation:

  • js/examples/app-minimal.js: A bare-minimum integration template/guide to quickly see how to set up the Babylon.js engine, scene, capsule collider, parent the mesh, and initialize the controllers.
  • js/examples/app-complex.js: A full-featured setup designed to demonstrate how the character controller functions with a highly complex 3D scenery model (assets/backyard_demo.glb) containing many intricate, complex collisions and polygon-heavy geometry.
  • js/examples/app.js: A fully featured production loading example including advanced lighting, shadows, skyboxes, procedural environment shapes (boxes, ramp, stairs), post-processing, and HUD settings synchronization.

🔧 Visual Builder (builder.html)

BJS Character Controller V2 Builder

builder is an interactive GUI tool for visually configuring and exporting a custom character controller — no code editing required. You can use it as a static page, or run it with the local NodeJS development server to enable full backend-powered retargeting and GLB merges.

🌐 Running with NodeJS / npm (Recommended)

To run the local server which powers advanced skeletal retargeting, GLB animation merges, and asset optimizations via the local backend API:

  1. Install dependencies:

    npm install
  2. Start the local server:

    npm start
  3. Open the builder: Navigate to http://localhost:3000/builder in your browser.

  4. Run the complete verification suite before publishing changes:

    npm run check
    npm audit

Tabs

TabWhat it does
Import & RigImport GLB/FBX, choose the deforming body meshes, adjust transforms and bind pose, inspect skeleton health, edit canonical bone assignments and generate or rebuild rigs
AnimateAuto-match animation names, preview clips, define gameplay frame markers and add custom triggered actions
Input MappingRemap keyboard/gameplay actions and restore individual defaults
ControllerApply presets, use the live test lab, and tune movement, camera and feel
PhysicsConfigure collision, gravity, jumping, grounding, slopes and Havok/kinematic behaviour
Validate & ExportReview diagnostics and generated code, save/restore schema-validated configuration, or export a merged GLB and standalone controller

Builder preferences auto-save locally for editing convenience. Exported runtime controllers do not inherit that storage unless the application explicitly enables persistPreferences.

💀 FBX Direct Import & Bind-Pose Posture Tuning

When running the NodeJS backend, the Import & Rig tab offers advanced rigging, conversion, and alignment utilities:

  • Direct FBX Support: Drag-and-drop .fbx character models and animation files. The server auto-converts them to .glb under-the-hood (using fbx_api.mjs), fixing materials and flattening the RootNode transformation to avoid rotation/scale offset issues.
  • Scale & Pivot Offsets: Fine-tune character sizing using uniform scaling or independent X, Y, and Z scaling. Adjust the pivot offset (X, Y, Z) and use the Pivot to Ground helper to easily snap a character's feet to the ground level.
  • Skeletal Posture Adjustments: Straighten or adjust character postures (e.g., matching A-poses to T-poses) using bind-pose angle sliders for Arm Spread, Arm Splay, Shoulder Raise, Leg Spread, Hips Tilt, and Spine Straightening.
  • Skeleton Tree & Health Report: View the hierarchy, humanoid/quadruped body plan, coverage, duplicate and unresolved roles, and confidence/reason for every canonical mapping. Every role can be reassigned to an exact node without renaming the source asset.

💀 Auto-Rig (skeleton generation for skinless meshes)

If you import a mesh-only GLB (no skeleton/skin), Import & Rig → Skeleton offers Generate Skeleton (Auto-Rig):

  1. Choose exactly which meshes form the deforming body. Automatic selection excludes likely floors, props and accessories; manual selection is available for ambiguous assets. The server then analyzes the selected vertex cloud — not just the bounding box — and selects a humanoid or quadruped body plan.
  2. For humanoids it proposes Mixamo-named joint positions: it detects the crotch (where the body splits into legs), shoulder height, hand positions (works for both T-pose and A-pose meshes), per-leg offsets, and follows hunched spines. For meshes in non-standard poses (crouching, sitting, action poses) a pose-independent topology pass kicks in automatically: the mesh is voxelized, the interior is filled (works on non-watertight meshes), and the five extremities (head, hands, feet) are found on the geodesic graph and classified by body topology — legs merge far from the head, arms merge near it. Joints are placed along the detected limb centerlines.
  3. The builder enters a dedicated rig viewport mode: the character is isolated, draggable yellow joint markers appear, with Front/Side/Top camera presets (keys 1/2/3) and optional symmetric editing (left ↔ right mirroring).
  4. Apply Rig builds the skeleton, computes bounded topology-aware skin weights server-side, preserves unselected rigid meshes and morph deltas, and re-merges the animation set automatically. The final quality report records selection, coverage, warnings and compatibility.

Already-rigged characters get Re-Rig / Adjust Skeleton instead: markers seed from the current bind pose, and applying moves the existing joints while preserving the hierarchy, extra bones (fingers/twist) and the original artist skin weights.

🎭 Custom Actions & Animations

In Animate → Custom Animations, you can extend the controller by registering completely new character actions (e.g., TAUNT, DANCE, WAVE):

  • Map a custom action name to any animation group in the library.
  • Assign key triggers directly to the custom action.
  • In the exported snippet, these actions are configured and bound automatically.
  • You can trigger custom actions through the complete controller state machine using charCtrl.triggerAction('CUSTOM_ACTION_NAME').

🎯 Animation Events (gameplay frame markers)

In Animate → Animation Events you can attach typed markers (footstep, hit, cast, sound, particle, camera, custom) to any mapped animation at a specific frame:

  • Markers fire live in the builder viewport (toast + console) while previewing or playing animations — including during crossfades and inside the Locomotion blend tree (footsteps fire on Walk/Sprint loops).
  • Markers survive character swaps: they are kept as long as the slot maps to the same clip, and a Clear All button removes every marker at once.
  • The Export tab emits them as charCtrl.animationEvents. Consume them in your game:
charCtrl.animationEvents={Punch: [{type: 'hit',frame: 12,label: 'impact'}],Walk_Loop: [{type: 'footstep',frame: 5},{type: 'footstep',frame: 19}],};charCtrl.onAnimationEvent=(evt,animName)=>{if(evt.type==='hit')applyDamage();if(evt.type==='footstep')playFootstepSound();};// or listen globally:window.addEventListener('charanimevent',(e)=>console.log(e.detail));

🧪 Controller Presets & Test Lab

The Controller tab includes four one-click controller presets (Balanced Adventure, Action Combat, Arcade Platformer, Cinematic Walkthrough) and a Controller Test Lab: scenario camera chips (Studio / Motion / Air / Close Cam), action buttons (Idle, Walk, Sprint, Jump, Roll, Crouch — locomotion buttons drive the real blend tree, exactly like in-game), and a live metrics panel (state, speed, grounded, active animation, camera framing).

↺ Parameter Reset Buttons

Beside every slider, toggle, or control mapping under Controller, Physics and Input Mapping, there is an reset button. Clicking it restores that single parameter without clearing the rest of the setup.

🔄 Retargeting & Animation Merging (merge_api.mjs)

The Visual Builder uses the canonical server-side module merge_api.mjs (via server.mjs) to analyze, retarget and combine characters and animations deterministically.

When using the builder, you can import assets in different ways:

  • Separate Import: Load a character in Import & Rig, then one or more external animation GLBs/FBXs in Animate.
  • Embedded Animations: A character's own animation groups are available immediately after import; no duplicate upload is required.

📥 Exporting & Downloading Options

Validate & Export provides four distinct ways to output your configuration and assets for production:

🔀 Integration Modes (Pre-merged GLB vs. Runtime Retargeting)

When exporting your setup, you can choose between two integration architectures depending on your project needs:

  • Pre-merged GLB (Baked Merge):
    • How it works: Merges character meshes and animation channels into a single character_animated.glb file.
    • Best for: Single character games, simple setups, or engines where loading multiple separate files is not desired.
    • Drawback: Duplicate data. If you have 10 characters sharing the same locomotion set, you will be downloading those animation frames 10 times.
  • Runtime Retargeting (Client-Side Dynamic Retargeting):
    • How it works: Keeps character meshes (character.glb) and animation libraries (animations.glb) separate. setupCharacter requests the same canonical server merge used by the builder and falls back to client retargeting when the service is unavailable. Manual boneMapOverrides travel with the exported setup.
    • Best for: Multi-character games, RPGs, or modular projects. Reuses one shared animation file across dozens of characters, drastically reducing download size and memory footprint.
    • Note: Requires Babylon.js 9+. Cross-convention rigs are most accurate with the local merge service available.

1. 📋 Export Code Snippet (Preview & Copy)

This provides a complete, custom loadCharacter helper function matching your settings. Copy and paste it directly into your app.js entry file to replace the default loader. It automatically bakes in:

  • Mesh Transform Scaling (capsuleScale).
  • Custom Key Bindings (keys mappings).
  • Physics Config Parameters (config defaults).
  • Mapped Animations & Custom Actions (configure callback).
  • Animation Events (animationEvents markers).

2. 💾 Saving & Restoring Builder Config (builder-config.json)

Allows you to save/load your visual builder configuration presets:

  • Download builder-config.json: Saves schema-versioned transforms, exact bone assignments, key bindings, physics settings, animation mappings, custom actions and events. It intentionally does not embed model or animation binaries (includesAssets: false).
  • Import builder-config.json: Restore your saved configuration at any time to resume working in the builder without losing your adjustments.

3. 📦 Exporting the Character as GLB (with animations)

Click Download character_animated.glb to download a single, self-contained GLB file that merges your character mesh with the active animations retargeted and merged directly into the skeletal structures on the server. Ready for drag-and-drop into your assets folder.

⚡ 4. Downloading Baked Controller (custom-character-controller.js)

Generates a tailored standalone character-controller.js file with your settings pre-baked:

  • Replaces the default configurations (DEFAULT_CHAR_CONFIG) inside the script with your custom physics, keys, and touch layouts.
  • Keeps baked defaults authoritative and does not inject or mutate localStorage; persistence remains an explicit application choice.
  • Bakes all standard and custom animation remappings, frame ranges, and event markers directly into the controller's setup hooks, acting as a complete drop-in replacement with zero extra code required in your loader scripts.
<!-- Use the downloaded file in place of the original: --><scriptsrc="js/character-controller.js"></script><!-- or, if using the builder export: --><scriptsrc="js/custom-character-controller.js"></script>

📚 Credits & License

  • Rig: Customized Mixamo skeletal rig.
  • Animations: Universal Animation Library by Quaternius.
  • License: Licensed under the MIT License - see LICENSE for details. Keep the copyright notice and attribute the authorship of the Character Controller to Diego Ramirez in all copies.

About

No description, website, or topics provided.

Resources

Stars

8 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
GitHub - crazyramirez/BJS_Character_Controller_V2 · GitHub
Skip to content

Repository files navigation

🎮 3D Character Animation Controller V2 for Babylon.js

An advanced third-person character locomotion and physics framework built with Babylon.js. This framework provides a fluid, powerful, and easy-to-use Character Controller with integrated physics, animations, and high-end visual features.

🎮 Live Demo: https://viseni.com/demos/bjs_character_controller_v2/

BJS Character Controller V2 Screenshot

☕ If this controller saves you time, consider supporting its development!

Buy Me A Coffee


🚀 Key Features

  • Dual-Movement Modes (Physics vs Kinematic): Toggle dynamically between Havok Physics (dynamic simulation with body bodies) and standard Kinematic Collisions (ellipsoid-based movement) directly from the HUD.
  • Locomotion Blend Tree: Smoothly blends weight and speed between Idle, Walk, and Sprint.
  • Dual-State Toggle Coexistence: Crouch and Sprint operate as persistent toggles and can co-exist (allowing crouch-running).
  • Dynamic Zoom & Camera Follow: Smooth camera tracking with automated user-zoom sync (mouse wheel, trackpad, pinch) and double-tap recentering.
  • Dynamic FOV & Camera Shake: Camera Field of View expands with speed. Rotational camera shake is triggered on landing impacts relative to fall height.
  • Camera Follow Lock (Direct Steering): Locks the camera directly behind the character for tank-style direct controls.
  • Visual Enhancements: Procedural dust/smoke trails at the feet, procedural leaning/banking on turns, slope-incline alignment, and squash & stretch scaling.
  • Collision height adjustments & Ceiling protection: Shrinks the capsule automatically when crouching/rolling, prevents standing up or rolling under low ceilings, and expands width when sprinting to prevent wall clipping.
  • Ledge & Stairs Snapping: Keeps the character grounded on sloped surfaces and stairs to prevent airborne jitter.
  • Slope-Aligned Kinematic Traversal: Kinematic collisions mode projects movement directly onto the ground normal to ensure butter-smooth ascent/descent on ramps and slopes.
  • Smart Snap-Down Controls: Dynamically disables downward snap forces when ascending stairs or steep slopes to eliminate physics/collision jitter.
  • Implicit Self-Collision Prevention: Prevents parent-capsule jitter by automatically disabling collision checks (checkCollisions = false) on imported character visual meshes.
  • Mobile Touch Support: Responsive virtual joystick and customizable glassmorphism action buttons.
  • Gamepad Support: Analog movement with configurable dead zone and edge-triggered jump, roll, sprint, crouch and action buttons.
  • Production Rigging Pipeline: Human and quadruped analysis, body-mesh selection, editable canonical bone assignment, topology-aware auto-rigging, quality diagnostics and deterministic animation retargeting.
  • Air Dash (Mid-Air Roll): Perform a responsive dodge roll in mid-air with a horizontal speed boost and a 55% jump-power vertical hop (available if Double Jump is enabled, works even after double jumping).
  • Action Interrupt Roll: Pressing Roll immediately interrupts active attack combos or spell casts for instant responsiveness.
  • Roll Cooldown & HUD Feedback: A 1.1s cooldown prevents roll spamming, displaying a "DODGE COOLDOWN" HUD warning when pressed too early.
  • Toggleable Action HUD Texts: Toggle on-screen action text alerts (like "AIR DASH", "JAB", "CROSS!") directly from the System & UI settings drawer.

⚖️ Physics vs. Kinematic Modes

character-controller.js is a unified single-file engine that runs in two distinct physics regimes. Both modes live in the same class — a single usePhysics flag switches the internal code paths at initialization time.

  • Havok Physics (Default): Leverages the WASM-powered Havok Physics engine. The character capsule is created as a dynamic PhysicsBody with defined mass and inertia properties, interacting naturally with other dynamic aggregates (like boxes, cylinders, and triggers).
  • Kinematic Collisions: Runs entirely within Babylon's native collision engine using kinematic ellipsoids (moveWithCollisions). Havok initialization is skipped entirely, providing maximum performance and deterministic locomotion.

Explicit configuration (default behaviour)

Runtime options are authoritative. initPhysics(scene) tries Havok and falls back to kinematic collisions without reading or modifying browser storage. Pass usePhysics: false when kinematic mode is required.

Overriding the mode

Preference persistence is deliberately opt-in. The bundled HUD examples use persistPreferences: true; embedded applications remain deterministic by default:

constusePhysics=awaitinitPhysics(scene,{usePhysics: true,persistPreferences: true});

The same policy is available on the controller constructor:

constcharCtrl=newCharCtrl(playerCapsule,charRoot,camera,animCtrl,scene,{usePhysics: true,// or falsepersistPreferences: false,config: {SPEED_MULTIPLIER: 1.5// Multiplies walking, running and jogging speeds}});

⚙️ Configuration Parameters

The config object in the constructor accepts a wide range of physics, camera, and gameplay properties to fine-tune character behavior:

ParameterDefaultTypeDescription
GRAV22numberGravity force pulling the character down
JUMP_PWR9.5numberVertical takeoff impulse force for jumping
SPD_WALK2.5numberMaximum physical walking speed
SPD_JOG3.0numberMaximum physical jogging speed (blend speed threshold)
SPD_SPRINT5.0numberMaximum physical sprinting speed
SPD_CROUCH2.0numberMaximum physical crouching walk speed
SPD_CROUCH_RUN3.2numberMaximum physical crouching run speed
ACCEL14numberMovement acceleration rate (speed-up responsiveness)
DECEL16numberMovement deceleration rate (braking/stopping responsiveness)
ROT_SPD40numberCharacter yaw rotation speed responsiveness
AIR_CONTROLfalsebooleanSteering control in mid-air (true = full control, false = no control)
DYNAMIC_FOVtruebooleanDynamically adjust camera Field of View based on speed
DYNAMIC_FOV_MAX0.10numberMaximum camera FOV expansion amount at full sprint speed
CAM_FOLLOW_LOCKtruebooleanIf true, the camera is locked behind the character's facing direction
CAM_FOLLOW_PITCH1.047numberCamera follow lock pitch (beta angle in radians, approx 60 degrees)
CAM_FOLLOW_DIST8.0numberCamera follow lock distance (radius in meters)
CAM_LOCK_PITCHfalsebooleanIf true, drag input only rotates camera horizontally (locks pitch axis)
JOYSTICK_LOCK_XfalsebooleanIf true, joystick input is locked to vertical axis only (no strafing)
DOUBLE_JUMP_ENABLEDtruebooleanIf true, the character can perform a double jump in mid-air
SPEED_MULTIPLIER1.0numberSpeed multiplier for walking, running, and jogging
PLAY_PARTICLEStruebooleanPlay procedural dust/smoke particles under the character's feet

🔄 Dynamic Animation Remapping

You can dynamically change any animation on the character controller or adjust keyframe ranges at runtime using the AnimCtrl instance (accessed via charCtrl.anim):

1. Reassigning Animations (Setters)

Pass a new Babylon AnimationGroup to dynamically swap any of the pre-mapped animations:

// Remap basic locomotioncharCtrl.anim.setWalkAnim(newWalkAnimGroup);charCtrl.anim.setRunAnim(newRunAnimGroup);charCtrl.anim.setIdleAnim(newIdleAnimGroup);// Remap crouch statescharCtrl.anim.setCrouchIdleAnim(newCrouchIdle);charCtrl.anim.setCrouchFwdAnim(newCrouchWalk);// Remap jumps and actionscharCtrl.anim.setJumpStartAnim(newJumpStart);charCtrl.anim.setJumpLoopAnim(newJumpLoop);charCtrl.anim.setJumpLandAnim(newJumpLand);charCtrl.anim.setRollAnim(newRoll);charCtrl.anim.setPunchJabAnim(newPunchJab);charCtrl.anim.setPunchCrossAnim(newPunchCross);charCtrl.anim.setSpellEnterAnim(newSpellEnter);charCtrl.anim.setSpellShootAnim(newSpellShoot);charCtrl.anim.setSpellExitAnim(newSpellExit);charCtrl.anim.setInteractAnim(newInteract);// Remap any custom animation keycharCtrl.anim.setAnimation('Custom_State_Name',myAnimGroup);

2. Modifying Playback Keyframe Ranges

Change the start/end frames of an animation without replacing the group:

// setAnimationRanges(animKey, startFrame, endFrame)charCtrl.anim.setAnimationRanges('Walk_Loop',10,45);

🕹️ Controls Layout

Keyboard (PC):

  • W, A, S, D / Arrow Keys: Movement.
  • Shift: Sprint (Toggle).
  • Ctrl: Crouch (Toggle).
  • Space: Jump / Double Jump.
  • R: Dodge roll / Air Dash:
    • Action Interrupt: Instantly cancels active attack combos or spell casts.
    • Roll Cooldown: 1.1s cooldown between rolls (triggers a "DODGE COOLDOWN" HUD alert).
    • Air Dash: If Double Jump is enabled in settings, performs a mid-air roll with a horizontal boost and a 55% jump-power vertical hop (usable even after double jumping).
  • Q: Punch combo.
  • E: Spell casting.
  • F: Interaction.
  • Mouse Drag: Orbit camera / Double-click to recenter.

Mobile Touch:

  • Left Hand: Floating Analog Joystick.
  • Right Hand (Buttons): SPELL, ACT, CROUCH, ROLL, SPRINT, JUMP.
  • Canvas Double-Tap: Recenter camera.

🛠️ Implementation Quickstart

The js/ directory is organized into subfolders by role:

  • js/character-controller.js — Unified core engine. Handles Havok Physics and Kinematic modes, locomotion state machines, and animation blending. Exports initPhysics and setupCharacter helpers.
  • js/ui/custom-hud.js — Tactile settings overlay (Camera Lock, Physics toggle, Dynamic FOV, Hide Cursor, Double Jump, Air Control, sliders). Optional.
  • js/ui/custom-pointer.js — Spring-damper trailing cursor ring. Optional.
  • js/examples/ — Ready-to-run setup templates (app.js, app-minimal.js, app-complex.js).
  • js/core/builder.js — Powers builder.html, the visual configuration tool (see below).

⚡ High-Level Setup (Recommended)

You can initialize physics and load the character in just a few lines of code using the shared helper functions: initPhysics and setupCharacter (wrapped in a clean loadCharacter helper function across the app templates). This helper supports configuring model paths, spawn locations, bounding ellipsoids, controls, and animations:

// 1. Define character initialization helperasyncfunctionloadCharacter(scene,shadow,camera,usePhysics){returnsetupCharacter(scene,camera,usePhysics,{
shadow,// Optional: shadow generator to add character meshes topersistPreferences: true,// Optional: allow HUD/browser preference persistenceassetsPath: 'assets/',// Optional: path to GLB assets folder (defaults to 'assets/')filename: 'character_animated.glb',// Optional: GLB file name (defaults to 'character_animated.glb')spawnPosition: newBABYLON.Vector3(0,2,0),// Optional: starting position overrideellipsoid: newBABYLON.Vector3(0.35,0.96,0.35),// Optional: collision ellipsoid overridekeys: {JUMP: ['KeyK']},// Optional: remap keyboard controls directlyconfig: {JUMP_PWR: 12},// Optional: override physical and camera parametersconfigure: ({ animCtrl, filteredGroups })=>{// Optional: callback to remap animations or customize keyframe rangesanimCtrl.setWalkAnim(filteredGroups[15]);}});}// 2. Initialize physics (Havok or Kinematic fallback)constusePhysics=awaitinitPhysics(scene,{persistPreferences: true});// 3. Load the character using the helperconst{ playerCapsule, animCtrl, charCtrl }=awaitloadCharacter(scene,shadow,camera,usePhysics);// 4. Hook up HUD setting toggles dynamically via custom-hud.jsif(typeofbindHUDControls==='function'){bindHUDControls(charCtrl,camera,usePhysics);}

We have provided three setup examples to guide your implementation:

  • js/examples/app-minimal.js: A bare-minimum integration template/guide to quickly see how to set up the Babylon.js engine, scene, capsule collider, parent the mesh, and initialize the controllers.
  • js/examples/app-complex.js: A full-featured setup designed to demonstrate how the character controller functions with a highly complex 3D scenery model (assets/backyard_demo.glb) containing many intricate, complex collisions and polygon-heavy geometry.
  • js/examples/app.js: A fully featured production loading example including advanced lighting, shadows, skyboxes, procedural environment shapes (boxes, ramp, stairs), post-processing, and HUD settings synchronization.

🔧 Visual Builder (builder.html)

BJS Character Controller V2 Builder

builder is an interactive GUI tool for visually configuring and exporting a custom character controller — no code editing required. You can use it as a static page, or run it with the local NodeJS development server to enable full backend-powered retargeting and GLB merges.

🌐 Running with NodeJS / npm (Recommended)

To run the local server which powers advanced skeletal retargeting, GLB animation merges, and asset optimizations via the local backend API:

  1. Install dependencies:

    npm install
  2. Start the local server:

    npm start
  3. Open the builder: Navigate to http://localhost:3000/builder in your browser.

  4. Run the complete verification suite before publishing changes:

    npm run check
    npm audit

Tabs

TabWhat it does
Import & RigImport GLB/FBX, choose the deforming body meshes, adjust transforms and bind pose, inspect skeleton health, edit canonical bone assignments and generate or rebuild rigs
AnimateAuto-match animation names, preview clips, define gameplay frame markers and add custom triggered actions
Input MappingRemap keyboard/gameplay actions and restore individual defaults
ControllerApply presets, use the live test lab, and tune movement, camera and feel
PhysicsConfigure collision, gravity, jumping, grounding, slopes and Havok/kinematic behaviour
Validate & ExportReview diagnostics and generated code, save/restore schema-validated configuration, or export a merged GLB and standalone controller

Builder preferences auto-save locally for editing convenience. Exported runtime controllers do not inherit that storage unless the application explicitly enables persistPreferences.

💀 FBX Direct Import & Bind-Pose Posture Tuning

When running the NodeJS backend, the Import & Rig tab offers advanced rigging, conversion, and alignment utilities:

  • Direct FBX Support: Drag-and-drop .fbx character models and animation files. The server auto-converts them to .glb under-the-hood (using fbx_api.mjs), fixing materials and flattening the RootNode transformation to avoid rotation/scale offset issues.
  • Scale & Pivot Offsets: Fine-tune character sizing using uniform scaling or independent X, Y, and Z scaling. Adjust the pivot offset (X, Y, Z) and use the Pivot to Ground helper to easily snap a character's feet to the ground level.
  • Skeletal Posture Adjustments: Straighten or adjust character postures (e.g., matching A-poses to T-poses) using bind-pose angle sliders for Arm Spread, Arm Splay, Shoulder Raise, Leg Spread, Hips Tilt, and Spine Straightening.
  • Skeleton Tree & Health Report: View the hierarchy, humanoid/quadruped body plan, coverage, duplicate and unresolved roles, and confidence/reason for every canonical mapping. Every role can be reassigned to an exact node without renaming the source asset.

💀 Auto-Rig (skeleton generation for skinless meshes)

If you import a mesh-only GLB (no skeleton/skin), Import & Rig → Skeleton offers Generate Skeleton (Auto-Rig):

  1. Choose exactly which meshes form the deforming body. Automatic selection excludes likely floors, props and accessories; manual selection is available for ambiguous assets. The server then analyzes the selected vertex cloud — not just the bounding box — and selects a humanoid or quadruped body plan.
  2. For humanoids it proposes Mixamo-named joint positions: it detects the crotch (where the body splits into legs), shoulder height, hand positions (works for both T-pose and A-pose meshes), per-leg offsets, and follows hunched spines. For meshes in non-standard poses (crouching, sitting, action poses) a pose-independent topology pass kicks in automatically: the mesh is voxelized, the interior is filled (works on non-watertight meshes), and the five extremities (head, hands, feet) are found on the geodesic graph and classified by body topology — legs merge far from the head, arms merge near it. Joints are placed along the detected limb centerlines.
  3. The builder enters a dedicated rig viewport mode: the character is isolated, draggable yellow joint markers appear, with Front/Side/Top camera presets (keys 1/2/3) and optional symmetric editing (left ↔ right mirroring).
  4. Apply Rig builds the skeleton, computes bounded topology-aware skin weights server-side, preserves unselected rigid meshes and morph deltas, and re-merges the animation set automatically. The final quality report records selection, coverage, warnings and compatibility.

Already-rigged characters get Re-Rig / Adjust Skeleton instead: markers seed from the current bind pose, and applying moves the existing joints while preserving the hierarchy, extra bones (fingers/twist) and the original artist skin weights.

🎭 Custom Actions & Animations

In Animate → Custom Animations, you can extend the controller by registering completely new character actions (e.g., TAUNT, DANCE, WAVE):

  • Map a custom action name to any animation group in the library.
  • Assign key triggers directly to the custom action.
  • In the exported snippet, these actions are configured and bound automatically.
  • You can trigger custom actions through the complete controller state machine using charCtrl.triggerAction('CUSTOM_ACTION_NAME').

🎯 Animation Events (gameplay frame markers)

In Animate → Animation Events you can attach typed markers (footstep, hit, cast, sound, particle, camera, custom) to any mapped animation at a specific frame:

  • Markers fire live in the builder viewport (toast + console) while previewing or playing animations — including during crossfades and inside the Locomotion blend tree (footsteps fire on Walk/Sprint loops).
  • Markers survive character swaps: they are kept as long as the slot maps to the same clip, and a Clear All button removes every marker at once.
  • The Export tab emits them as charCtrl.animationEvents. Consume them in your game:
charCtrl.animationEvents={Punch: [{type: 'hit',frame: 12,label: 'impact'}],Walk_Loop: [{type: 'footstep',frame: 5},{type: 'footstep',frame: 19}],};charCtrl.onAnimationEvent=(evt,animName)=>{if(evt.type==='hit')applyDamage();if(evt.type==='footstep')playFootstepSound();};// or listen globally:window.addEventListener('charanimevent',(e)=>console.log(e.detail));

🧪 Controller Presets & Test Lab

The Controller tab includes four one-click controller presets (Balanced Adventure, Action Combat, Arcade Platformer, Cinematic Walkthrough) and a Controller Test Lab: scenario camera chips (Studio / Motion / Air / Close Cam), action buttons (Idle, Walk, Sprint, Jump, Roll, Crouch — locomotion buttons drive the real blend tree, exactly like in-game), and a live metrics panel (state, speed, grounded, active animation, camera framing).

↺ Parameter Reset Buttons

Beside every slider, toggle, or control mapping under Controller, Physics and Input Mapping, there is an reset button. Clicking it restores that single parameter without clearing the rest of the setup.

🔄 Retargeting & Animation Merging (merge_api.mjs)

The Visual Builder uses the canonical server-side module merge_api.mjs (via server.mjs) to analyze, retarget and combine characters and animations deterministically.

When using the builder, you can import assets in different ways:

  • Separate Import: Load a character in Import & Rig, then one or more external animation GLBs/FBXs in Animate.
  • Embedded Animations: A character's own animation groups are available immediately after import; no duplicate upload is required.

📥 Exporting & Downloading Options

Validate & Export provides four distinct ways to output your configuration and assets for production:

🔀 Integration Modes (Pre-merged GLB vs. Runtime Retargeting)

When exporting your setup, you can choose between two integration architectures depending on your project needs:

  • Pre-merged GLB (Baked Merge):
    • How it works: Merges character meshes and animation channels into a single character_animated.glb file.
    • Best for: Single character games, simple setups, or engines where loading multiple separate files is not desired.
    • Drawback: Duplicate data. If you have 10 characters sharing the same locomotion set, you will be downloading those animation frames 10 times.
  • Runtime Retargeting (Client-Side Dynamic Retargeting):
    • How it works: Keeps character meshes (character.glb) and animation libraries (animations.glb) separate. setupCharacter requests the same canonical server merge used by the builder and falls back to client retargeting when the service is unavailable. Manual boneMapOverrides travel with the exported setup.
    • Best for: Multi-character games, RPGs, or modular projects. Reuses one shared animation file across dozens of characters, drastically reducing download size and memory footprint.
    • Note: Requires Babylon.js 9+. Cross-convention rigs are most accurate with the local merge service available.

1. 📋 Export Code Snippet (Preview & Copy)

This provides a complete, custom loadCharacter helper function matching your settings. Copy and paste it directly into your app.js entry file to replace the default loader. It automatically bakes in:

  • Mesh Transform Scaling (capsuleScale).
  • Custom Key Bindings (keys mappings).
  • Physics Config Parameters (config defaults).
  • Mapped Animations & Custom Actions (configure callback).
  • Animation Events (animationEvents markers).

2. 💾 Saving & Restoring Builder Config (builder-config.json)

Allows you to save/load your visual builder configuration presets:

  • Download builder-config.json: Saves schema-versioned transforms, exact bone assignments, key bindings, physics settings, animation mappings, custom actions and events. It intentionally does not embed model or animation binaries (includesAssets: false).
  • Import builder-config.json: Restore your saved configuration at any time to resume working in the builder without losing your adjustments.

3. 📦 Exporting the Character as GLB (with animations)

Click Download character_animated.glb to download a single, self-contained GLB file that merges your character mesh with the active animations retargeted and merged directly into the skeletal structures on the server. Ready for drag-and-drop into your assets folder.

⚡ 4. Downloading Baked Controller (custom-character-controller.js)

Generates a tailored standalone character-controller.js file with your settings pre-baked:

  • Replaces the default configurations (DEFAULT_CHAR_CONFIG) inside the script with your custom physics, keys, and touch layouts.
  • Keeps baked defaults authoritative and does not inject or mutate localStorage; persistence remains an explicit application choice.
  • Bakes all standard and custom animation remappings, frame ranges, and event markers directly into the controller's setup hooks, acting as a complete drop-in replacement with zero extra code required in your loader scripts.
<!-- Use the downloaded file in place of the original: --><scriptsrc="js/character-controller.js"></script><!-- or, if using the builder export: --><scriptsrc="js/custom-character-controller.js"></script>

📚 Credits & License

  • Rig: Customized Mixamo skeletal rig.
  • Animations: Universal Animation Library by Quaternius.
  • License: Licensed under the MIT License - see LICENSE for details. Keep the copyright notice and attribute the authorship of the Character Controller to Diego Ramirez in all copies.

About

No description, website, or topics provided.

Resources

Stars

8 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' GitHub - crazyramirez/BJS_Character_Controller_V2 · GitHub
Skip to content

Repository files navigation

🎮 3D Character Animation Controller V2 for Babylon.js

An advanced third-person character locomotion and physics framework built with Babylon.js. This framework provides a fluid, powerful, and easy-to-use Character Controller with integrated physics, animations, and high-end visual features.

🎮 Live Demo: https://viseni.com/demos/bjs_character_controller_v2/

BJS Character Controller V2 Screenshot

☕ If this controller saves you time, consider supporting its development!

Buy Me A Coffee


🚀 Key Features

  • Dual-Movement Modes (Physics vs Kinematic): Toggle dynamically between Havok Physics (dynamic simulation with body bodies) and standard Kinematic Collisions (ellipsoid-based movement) directly from the HUD.
  • Locomotion Blend Tree: Smoothly blends weight and speed between Idle, Walk, and Sprint.
  • Dual-State Toggle Coexistence: Crouch and Sprint operate as persistent toggles and can co-exist (allowing crouch-running).
  • Dynamic Zoom & Camera Follow: Smooth camera tracking with automated user-zoom sync (mouse wheel, trackpad, pinch) and double-tap recentering.
  • Dynamic FOV & Camera Shake: Camera Field of View expands with speed. Rotational camera shake is triggered on landing impacts relative to fall height.
  • Camera Follow Lock (Direct Steering): Locks the camera directly behind the character for tank-style direct controls.
  • Visual Enhancements: Procedural dust/smoke trails at the feet, procedural leaning/banking on turns, slope-incline alignment, and squash & stretch scaling.
  • Collision height adjustments & Ceiling protection: Shrinks the capsule automatically when crouching/rolling, prevents standing up or rolling under low ceilings, and expands width when sprinting to prevent wall clipping.
  • Ledge & Stairs Snapping: Keeps the character grounded on sloped surfaces and stairs to prevent airborne jitter.
  • Slope-Aligned Kinematic Traversal: Kinematic collisions mode projects movement directly onto the ground normal to ensure butter-smooth ascent/descent on ramps and slopes.
  • Smart Snap-Down Controls: Dynamically disables downward snap forces when ascending stairs or steep slopes to eliminate physics/collision jitter.
  • Implicit Self-Collision Prevention: Prevents parent-capsule jitter by automatically disabling collision checks (checkCollisions = false) on imported character visual meshes.
  • Mobile Touch Support: Responsive virtual joystick and customizable glassmorphism action buttons.
  • Gamepad Support: Analog movement with configurable dead zone and edge-triggered jump, roll, sprint, crouch and action buttons.
  • Production Rigging Pipeline: Human and quadruped analysis, body-mesh selection, editable canonical bone assignment, topology-aware auto-rigging, quality diagnostics and deterministic animation retargeting.
  • Air Dash (Mid-Air Roll): Perform a responsive dodge roll in mid-air with a horizontal speed boost and a 55% jump-power vertical hop (available if Double Jump is enabled, works even after double jumping).
  • Action Interrupt Roll: Pressing Roll immediately interrupts active attack combos or spell casts for instant responsiveness.
  • Roll Cooldown & HUD Feedback: A 1.1s cooldown prevents roll spamming, displaying a "DODGE COOLDOWN" HUD warning when pressed too early.
  • Toggleable Action HUD Texts: Toggle on-screen action text alerts (like "AIR DASH", "JAB", "CROSS!") directly from the System & UI settings drawer.

⚖️ Physics vs. Kinematic Modes

character-controller.js is a unified single-file engine that runs in two distinct physics regimes. Both modes live in the same class — a single usePhysics flag switches the internal code paths at initialization time.

  • Havok Physics (Default): Leverages the WASM-powered Havok Physics engine. The character capsule is created as a dynamic PhysicsBody with defined mass and inertia properties, interacting naturally with other dynamic aggregates (like boxes, cylinders, and triggers).
  • Kinematic Collisions: Runs entirely within Babylon's native collision engine using kinematic ellipsoids (moveWithCollisions). Havok initialization is skipped entirely, providing maximum performance and deterministic locomotion.

Explicit configuration (default behaviour)

Runtime options are authoritative. initPhysics(scene) tries Havok and falls back to kinematic collisions without reading or modifying browser storage. Pass usePhysics: false when kinematic mode is required.

Overriding the mode

Preference persistence is deliberately opt-in. The bundled HUD examples use persistPreferences: true; embedded applications remain deterministic by default:

constusePhysics=awaitinitPhysics(scene,{usePhysics: true,persistPreferences: true});

The same policy is available on the controller constructor:

constcharCtrl=newCharCtrl(playerCapsule,charRoot,camera,animCtrl,scene,{usePhysics: true,// or falsepersistPreferences: false,config: {SPEED_MULTIPLIER: 1.5// Multiplies walking, running and jogging speeds}});

⚙️ Configuration Parameters

The config object in the constructor accepts a wide range of physics, camera, and gameplay properties to fine-tune character behavior:

ParameterDefaultTypeDescription
GRAV22numberGravity force pulling the character down
JUMP_PWR9.5numberVertical takeoff impulse force for jumping
SPD_WALK2.5numberMaximum physical walking speed
SPD_JOG3.0numberMaximum physical jogging speed (blend speed threshold)
SPD_SPRINT5.0numberMaximum physical sprinting speed
SPD_CROUCH2.0numberMaximum physical crouching walk speed
SPD_CROUCH_RUN3.2numberMaximum physical crouching run speed
ACCEL14numberMovement acceleration rate (speed-up responsiveness)
DECEL16numberMovement deceleration rate (braking/stopping responsiveness)
ROT_SPD40numberCharacter yaw rotation speed responsiveness
AIR_CONTROLfalsebooleanSteering control in mid-air (true = full control, false = no control)
DYNAMIC_FOVtruebooleanDynamically adjust camera Field of View based on speed
DYNAMIC_FOV_MAX0.10numberMaximum camera FOV expansion amount at full sprint speed
CAM_FOLLOW_LOCKtruebooleanIf true, the camera is locked behind the character's facing direction
CAM_FOLLOW_PITCH1.047numberCamera follow lock pitch (beta angle in radians, approx 60 degrees)
CAM_FOLLOW_DIST8.0numberCamera follow lock distance (radius in meters)
CAM_LOCK_PITCHfalsebooleanIf true, drag input only rotates camera horizontally (locks pitch axis)
JOYSTICK_LOCK_XfalsebooleanIf true, joystick input is locked to vertical axis only (no strafing)
DOUBLE_JUMP_ENABLEDtruebooleanIf true, the character can perform a double jump in mid-air
SPEED_MULTIPLIER1.0numberSpeed multiplier for walking, running, and jogging
PLAY_PARTICLEStruebooleanPlay procedural dust/smoke particles under the character's feet

🔄 Dynamic Animation Remapping

You can dynamically change any animation on the character controller or adjust keyframe ranges at runtime using the AnimCtrl instance (accessed via charCtrl.anim):

1. Reassigning Animations (Setters)

Pass a new Babylon AnimationGroup to dynamically swap any of the pre-mapped animations:

// Remap basic locomotioncharCtrl.anim.setWalkAnim(newWalkAnimGroup);charCtrl.anim.setRunAnim(newRunAnimGroup);charCtrl.anim.setIdleAnim(newIdleAnimGroup);// Remap crouch statescharCtrl.anim.setCrouchIdleAnim(newCrouchIdle);charCtrl.anim.setCrouchFwdAnim(newCrouchWalk);// Remap jumps and actionscharCtrl.anim.setJumpStartAnim(newJumpStart);charCtrl.anim.setJumpLoopAnim(newJumpLoop);charCtrl.anim.setJumpLandAnim(newJumpLand);charCtrl.anim.setRollAnim(newRoll);charCtrl.anim.setPunchJabAnim(newPunchJab);charCtrl.anim.setPunchCrossAnim(newPunchCross);charCtrl.anim.setSpellEnterAnim(newSpellEnter);charCtrl.anim.setSpellShootAnim(newSpellShoot);charCtrl.anim.setSpellExitAnim(newSpellExit);charCtrl.anim.setInteractAnim(newInteract);// Remap any custom animation keycharCtrl.anim.setAnimation('Custom_State_Name',myAnimGroup);

2. Modifying Playback Keyframe Ranges

Change the start/end frames of an animation without replacing the group:

// setAnimationRanges(animKey, startFrame, endFrame)charCtrl.anim.setAnimationRanges('Walk_Loop',10,45);

🕹️ Controls Layout

Keyboard (PC):

  • W, A, S, D / Arrow Keys: Movement.
  • Shift: Sprint (Toggle).
  • Ctrl: Crouch (Toggle).
  • Space: Jump / Double Jump.
  • R: Dodge roll / Air Dash:
    • Action Interrupt: Instantly cancels active attack combos or spell casts.
    • Roll Cooldown: 1.1s cooldown between rolls (triggers a "DODGE COOLDOWN" HUD alert).
    • Air Dash: If Double Jump is enabled in settings, performs a mid-air roll with a horizontal boost and a 55% jump-power vertical hop (usable even after double jumping).
  • Q: Punch combo.
  • E: Spell casting.
  • F: Interaction.
  • Mouse Drag: Orbit camera / Double-click to recenter.

Mobile Touch:

  • Left Hand: Floating Analog Joystick.
  • Right Hand (Buttons): SPELL, ACT, CROUCH, ROLL, SPRINT, JUMP.
  • Canvas Double-Tap: Recenter camera.

🛠️ Implementation Quickstart

The js/ directory is organized into subfolders by role:

  • js/character-controller.js — Unified core engine. Handles Havok Physics and Kinematic modes, locomotion state machines, and animation blending. Exports initPhysics and setupCharacter helpers.
  • js/ui/custom-hud.js — Tactile settings overlay (Camera Lock, Physics toggle, Dynamic FOV, Hide Cursor, Double Jump, Air Control, sliders). Optional.
  • js/ui/custom-pointer.js — Spring-damper trailing cursor ring. Optional.
  • js/examples/ — Ready-to-run setup templates (app.js, app-minimal.js, app-complex.js).
  • js/core/builder.js — Powers builder.html, the visual configuration tool (see below).

⚡ High-Level Setup (Recommended)

You can initialize physics and load the character in just a few lines of code using the shared helper functions: initPhysics and setupCharacter (wrapped in a clean loadCharacter helper function across the app templates). This helper supports configuring model paths, spawn locations, bounding ellipsoids, controls, and animations:

// 1. Define character initialization helperasyncfunctionloadCharacter(scene,shadow,camera,usePhysics){returnsetupCharacter(scene,camera,usePhysics,{
shadow,// Optional: shadow generator to add character meshes topersistPreferences: true,// Optional: allow HUD/browser preference persistenceassetsPath: 'assets/',// Optional: path to GLB assets folder (defaults to 'assets/')filename: 'character_animated.glb',// Optional: GLB file name (defaults to 'character_animated.glb')spawnPosition: newBABYLON.Vector3(0,2,0),// Optional: starting position overrideellipsoid: newBABYLON.Vector3(0.35,0.96,0.35),// Optional: collision ellipsoid overridekeys: {JUMP: ['KeyK']},// Optional: remap keyboard controls directlyconfig: {JUMP_PWR: 12},// Optional: override physical and camera parametersconfigure: ({ animCtrl, filteredGroups })=>{// Optional: callback to remap animations or customize keyframe rangesanimCtrl.setWalkAnim(filteredGroups[15]);}});}// 2. Initialize physics (Havok or Kinematic fallback)constusePhysics=awaitinitPhysics(scene,{persistPreferences: true});// 3. Load the character using the helperconst{ playerCapsule, animCtrl, charCtrl }=awaitloadCharacter(scene,shadow,camera,usePhysics);// 4. Hook up HUD setting toggles dynamically via custom-hud.jsif(typeofbindHUDControls==='function'){bindHUDControls(charCtrl,camera,usePhysics);}

We have provided three setup examples to guide your implementation:

  • js/examples/app-minimal.js: A bare-minimum integration template/guide to quickly see how to set up the Babylon.js engine, scene, capsule collider, parent the mesh, and initialize the controllers.
  • js/examples/app-complex.js: A full-featured setup designed to demonstrate how the character controller functions with a highly complex 3D scenery model (assets/backyard_demo.glb) containing many intricate, complex collisions and polygon-heavy geometry.
  • js/examples/app.js: A fully featured production loading example including advanced lighting, shadows, skyboxes, procedural environment shapes (boxes, ramp, stairs), post-processing, and HUD settings synchronization.

🔧 Visual Builder (builder.html)

BJS Character Controller V2 Builder

builder is an interactive GUI tool for visually configuring and exporting a custom character controller — no code editing required. You can use it as a static page, or run it with the local NodeJS development server to enable full backend-powered retargeting and GLB merges.

🌐 Running with NodeJS / npm (Recommended)

To run the local server which powers advanced skeletal retargeting, GLB animation merges, and asset optimizations via the local backend API:

  1. Install dependencies:

    npm install
  2. Start the local server:

    npm start
  3. Open the builder: Navigate to http://localhost:3000/builder in your browser.

  4. Run the complete verification suite before publishing changes:

    npm run check
    npm audit

Tabs

TabWhat it does
Import & RigImport GLB/FBX, choose the deforming body meshes, adjust transforms and bind pose, inspect skeleton health, edit canonical bone assignments and generate or rebuild rigs
AnimateAuto-match animation names, preview clips, define gameplay frame markers and add custom triggered actions
Input MappingRemap keyboard/gameplay actions and restore individual defaults
ControllerApply presets, use the live test lab, and tune movement, camera and feel
PhysicsConfigure collision, gravity, jumping, grounding, slopes and Havok/kinematic behaviour
Validate & ExportReview diagnostics and generated code, save/restore schema-validated configuration, or export a merged GLB and standalone controller

Builder preferences auto-save locally for editing convenience. Exported runtime controllers do not inherit that storage unless the application explicitly enables persistPreferences.

💀 FBX Direct Import & Bind-Pose Posture Tuning

When running the NodeJS backend, the Import & Rig tab offers advanced rigging, conversion, and alignment utilities:

  • Direct FBX Support: Drag-and-drop .fbx character models and animation files. The server auto-converts them to .glb under-the-hood (using fbx_api.mjs), fixing materials and flattening the RootNode transformation to avoid rotation/scale offset issues.
  • Scale & Pivot Offsets: Fine-tune character sizing using uniform scaling or independent X, Y, and Z scaling. Adjust the pivot offset (X, Y, Z) and use the Pivot to Ground helper to easily snap a character's feet to the ground level.
  • Skeletal Posture Adjustments: Straighten or adjust character postures (e.g., matching A-poses to T-poses) using bind-pose angle sliders for Arm Spread, Arm Splay, Shoulder Raise, Leg Spread, Hips Tilt, and Spine Straightening.
  • Skeleton Tree & Health Report: View the hierarchy, humanoid/quadruped body plan, coverage, duplicate and unresolved roles, and confidence/reason for every canonical mapping. Every role can be reassigned to an exact node without renaming the source asset.

💀 Auto-Rig (skeleton generation for skinless meshes)

If you import a mesh-only GLB (no skeleton/skin), Import & Rig → Skeleton offers Generate Skeleton (Auto-Rig):

  1. Choose exactly which meshes form the deforming body. Automatic selection excludes likely floors, props and accessories; manual selection is available for ambiguous assets. The server then analyzes the selected vertex cloud — not just the bounding box — and selects a humanoid or quadruped body plan.
  2. For humanoids it proposes Mixamo-named joint positions: it detects the crotch (where the body splits into legs), shoulder height, hand positions (works for both T-pose and A-pose meshes), per-leg offsets, and follows hunched spines. For meshes in non-standard poses (crouching, sitting, action poses) a pose-independent topology pass kicks in automatically: the mesh is voxelized, the interior is filled (works on non-watertight meshes), and the five extremities (head, hands, feet) are found on the geodesic graph and classified by body topology — legs merge far from the head, arms merge near it. Joints are placed along the detected limb centerlines.
  3. The builder enters a dedicated rig viewport mode: the character is isolated, draggable yellow joint markers appear, with Front/Side/Top camera presets (keys 1/2/3) and optional symmetric editing (left ↔ right mirroring).
  4. Apply Rig builds the skeleton, computes bounded topology-aware skin weights server-side, preserves unselected rigid meshes and morph deltas, and re-merges the animation set automatically. The final quality report records selection, coverage, warnings and compatibility.

Already-rigged characters get Re-Rig / Adjust Skeleton instead: markers seed from the current bind pose, and applying moves the existing joints while preserving the hierarchy, extra bones (fingers/twist) and the original artist skin weights.

🎭 Custom Actions & Animations

In Animate → Custom Animations, you can extend the controller by registering completely new character actions (e.g., TAUNT, DANCE, WAVE):

  • Map a custom action name to any animation group in the library.
  • Assign key triggers directly to the custom action.
  • In the exported snippet, these actions are configured and bound automatically.
  • You can trigger custom actions through the complete controller state machine using charCtrl.triggerAction('CUSTOM_ACTION_NAME').

🎯 Animation Events (gameplay frame markers)

In Animate → Animation Events you can attach typed markers (footstep, hit, cast, sound, particle, camera, custom) to any mapped animation at a specific frame:

  • Markers fire live in the builder viewport (toast + console) while previewing or playing animations — including during crossfades and inside the Locomotion blend tree (footsteps fire on Walk/Sprint loops).
  • Markers survive character swaps: they are kept as long as the slot maps to the same clip, and a Clear All button removes every marker at once.
  • The Export tab emits them as charCtrl.animationEvents. Consume them in your game:
charCtrl.animationEvents={Punch: [{type: 'hit',frame: 12,label: 'impact'}],Walk_Loop: [{type: 'footstep',frame: 5},{type: 'footstep',frame: 19}],};charCtrl.onAnimationEvent=(evt,animName)=>{if(evt.type==='hit')applyDamage();if(evt.type==='footstep')playFootstepSound();};// or listen globally:window.addEventListener('charanimevent',(e)=>console.log(e.detail));

🧪 Controller Presets & Test Lab

The Controller tab includes four one-click controller presets (Balanced Adventure, Action Combat, Arcade Platformer, Cinematic Walkthrough) and a Controller Test Lab: scenario camera chips (Studio / Motion / Air / Close Cam), action buttons (Idle, Walk, Sprint, Jump, Roll, Crouch — locomotion buttons drive the real blend tree, exactly like in-game), and a live metrics panel (state, speed, grounded, active animation, camera framing).

↺ Parameter Reset Buttons

Beside every slider, toggle, or control mapping under Controller, Physics and Input Mapping, there is an reset button. Clicking it restores that single parameter without clearing the rest of the setup.

🔄 Retargeting & Animation Merging (merge_api.mjs)

The Visual Builder uses the canonical server-side module merge_api.mjs (via server.mjs) to analyze, retarget and combine characters and animations deterministically.

When using the builder, you can import assets in different ways:

  • Separate Import: Load a character in Import & Rig, then one or more external animation GLBs/FBXs in Animate.
  • Embedded Animations: A character's own animation groups are available immediately after import; no duplicate upload is required.

📥 Exporting & Downloading Options

Validate & Export provides four distinct ways to output your configuration and assets for production:

🔀 Integration Modes (Pre-merged GLB vs. Runtime Retargeting)

When exporting your setup, you can choose between two integration architectures depending on your project needs:

  • Pre-merged GLB (Baked Merge):
    • How it works: Merges character meshes and animation channels into a single character_animated.glb file.
    • Best for: Single character games, simple setups, or engines where loading multiple separate files is not desired.
    • Drawback: Duplicate data. If you have 10 characters sharing the same locomotion set, you will be downloading those animation frames 10 times.
  • Runtime Retargeting (Client-Side Dynamic Retargeting):
    • How it works: Keeps character meshes (character.glb) and animation libraries (animations.glb) separate. setupCharacter requests the same canonical server merge used by the builder and falls back to client retargeting when the service is unavailable. Manual boneMapOverrides travel with the exported setup.
    • Best for: Multi-character games, RPGs, or modular projects. Reuses one shared animation file across dozens of characters, drastically reducing download size and memory footprint.
    • Note: Requires Babylon.js 9+. Cross-convention rigs are most accurate with the local merge service available.

1. 📋 Export Code Snippet (Preview & Copy)

This provides a complete, custom loadCharacter helper function matching your settings. Copy and paste it directly into your app.js entry file to replace the default loader. It automatically bakes in:

  • Mesh Transform Scaling (capsuleScale).
  • Custom Key Bindings (keys mappings).
  • Physics Config Parameters (config defaults).
  • Mapped Animations & Custom Actions (configure callback).
  • Animation Events (animationEvents markers).

2. 💾 Saving & Restoring Builder Config (builder-config.json)

Allows you to save/load your visual builder configuration presets:

  • Download builder-config.json: Saves schema-versioned transforms, exact bone assignments, key bindings, physics settings, animation mappings, custom actions and events. It intentionally does not embed model or animation binaries (includesAssets: false).
  • Import builder-config.json: Restore your saved configuration at any time to resume working in the builder without losing your adjustments.

3. 📦 Exporting the Character as GLB (with animations)

Click Download character_animated.glb to download a single, self-contained GLB file that merges your character mesh with the active animations retargeted and merged directly into the skeletal structures on the server. Ready for drag-and-drop into your assets folder.

⚡ 4. Downloading Baked Controller (custom-character-controller.js)

Generates a tailored standalone character-controller.js file with your settings pre-baked:

  • Replaces the default configurations (DEFAULT_CHAR_CONFIG) inside the script with your custom physics, keys, and touch layouts.
  • Keeps baked defaults authoritative and does not inject or mutate localStorage; persistence remains an explicit application choice.
  • Bakes all standard and custom animation remappings, frame ranges, and event markers directly into the controller's setup hooks, acting as a complete drop-in replacement with zero extra code required in your loader scripts.
<!-- Use the downloaded file in place of the original: --><scriptsrc="js/character-controller.js"></script><!-- or, if using the builder export: --><scriptsrc="js/custom-character-controller.js"></script>

📚 Credits & License

  • Rig: Customized Mixamo skeletal rig.
  • Animations: Universal Animation Library by Quaternius.
  • License: Licensed under the MIT License - see LICENSE for details. Keep the copyright notice and attribute the authorship of the Character Controller to Diego Ramirez in all copies.

About

No description, website, or topics provided.

Resources

Stars

8 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' GitHub - crazyramirez/BJS_Character_Controller_V2 · GitHub
Skip to content

Repository files navigation

🎮 3D Character Animation Controller V2 for Babylon.js

An advanced third-person character locomotion and physics framework built with Babylon.js. This framework provides a fluid, powerful, and easy-to-use Character Controller with integrated physics, animations, and high-end visual features.

🎮 Live Demo: https://viseni.com/demos/bjs_character_controller_v2/

BJS Character Controller V2 Screenshot

☕ If this controller saves you time, consider supporting its development!

Buy Me A Coffee


🚀 Key Features

  • Dual-Movement Modes (Physics vs Kinematic): Toggle dynamically between Havok Physics (dynamic simulation with body bodies) and standard Kinematic Collisions (ellipsoid-based movement) directly from the HUD.
  • Locomotion Blend Tree: Smoothly blends weight and speed between Idle, Walk, and Sprint.
  • Dual-State Toggle Coexistence: Crouch and Sprint operate as persistent toggles and can co-exist (allowing crouch-running).
  • Dynamic Zoom & Camera Follow: Smooth camera tracking with automated user-zoom sync (mouse wheel, trackpad, pinch) and double-tap recentering.
  • Dynamic FOV & Camera Shake: Camera Field of View expands with speed. Rotational camera shake is triggered on landing impacts relative to fall height.
  • Camera Follow Lock (Direct Steering): Locks the camera directly behind the character for tank-style direct controls.
  • Visual Enhancements: Procedural dust/smoke trails at the feet, procedural leaning/banking on turns, slope-incline alignment, and squash & stretch scaling.
  • Collision height adjustments & Ceiling protection: Shrinks the capsule automatically when crouching/rolling, prevents standing up or rolling under low ceilings, and expands width when sprinting to prevent wall clipping.
  • Ledge & Stairs Snapping: Keeps the character grounded on sloped surfaces and stairs to prevent airborne jitter.
  • Slope-Aligned Kinematic Traversal: Kinematic collisions mode projects movement directly onto the ground normal to ensure butter-smooth ascent/descent on ramps and slopes.
  • Smart Snap-Down Controls: Dynamically disables downward snap forces when ascending stairs or steep slopes to eliminate physics/collision jitter.
  • Implicit Self-Collision Prevention: Prevents parent-capsule jitter by automatically disabling collision checks (checkCollisions = false) on imported character visual meshes.
  • Mobile Touch Support: Responsive virtual joystick and customizable glassmorphism action buttons.
  • Gamepad Support: Analog movement with configurable dead zone and edge-triggered jump, roll, sprint, crouch and action buttons.
  • Production Rigging Pipeline: Human and quadruped analysis, body-mesh selection, editable canonical bone assignment, topology-aware auto-rigging, quality diagnostics and deterministic animation retargeting.
  • Air Dash (Mid-Air Roll): Perform a responsive dodge roll in mid-air with a horizontal speed boost and a 55% jump-power vertical hop (available if Double Jump is enabled, works even after double jumping).
  • Action Interrupt Roll: Pressing Roll immediately interrupts active attack combos or spell casts for instant responsiveness.
  • Roll Cooldown & HUD Feedback: A 1.1s cooldown prevents roll spamming, displaying a "DODGE COOLDOWN" HUD warning when pressed too early.
  • Toggleable Action HUD Texts: Toggle on-screen action text alerts (like "AIR DASH", "JAB", "CROSS!") directly from the System & UI settings drawer.

⚖️ Physics vs. Kinematic Modes

character-controller.js is a unified single-file engine that runs in two distinct physics regimes. Both modes live in the same class — a single usePhysics flag switches the internal code paths at initialization time.

  • Havok Physics (Default): Leverages the WASM-powered Havok Physics engine. The character capsule is created as a dynamic PhysicsBody with defined mass and inertia properties, interacting naturally with other dynamic aggregates (like boxes, cylinders, and triggers).
  • Kinematic Collisions: Runs entirely within Babylon's native collision engine using kinematic ellipsoids (moveWithCollisions). Havok initialization is skipped entirely, providing maximum performance and deterministic locomotion.

Explicit configuration (default behaviour)

Runtime options are authoritative. initPhysics(scene) tries Havok and falls back to kinematic collisions without reading or modifying browser storage. Pass usePhysics: false when kinematic mode is required.

Overriding the mode

Preference persistence is deliberately opt-in. The bundled HUD examples use persistPreferences: true; embedded applications remain deterministic by default:

constusePhysics=awaitinitPhysics(scene,{usePhysics: true,persistPreferences: true});

The same policy is available on the controller constructor:

constcharCtrl=newCharCtrl(playerCapsule,charRoot,camera,animCtrl,scene,{usePhysics: true,// or falsepersistPreferences: false,config: {SPEED_MULTIPLIER: 1.5// Multiplies walking, running and jogging speeds}});

⚙️ Configuration Parameters

The config object in the constructor accepts a wide range of physics, camera, and gameplay properties to fine-tune character behavior:

ParameterDefaultTypeDescription
GRAV22numberGravity force pulling the character down
JUMP_PWR9.5numberVertical takeoff impulse force for jumping
SPD_WALK2.5numberMaximum physical walking speed
SPD_JOG3.0numberMaximum physical jogging speed (blend speed threshold)
SPD_SPRINT5.0numberMaximum physical sprinting speed
SPD_CROUCH2.0numberMaximum physical crouching walk speed
SPD_CROUCH_RUN3.2numberMaximum physical crouching run speed
ACCEL14numberMovement acceleration rate (speed-up responsiveness)
DECEL16numberMovement deceleration rate (braking/stopping responsiveness)
ROT_SPD40numberCharacter yaw rotation speed responsiveness
AIR_CONTROLfalsebooleanSteering control in mid-air (true = full control, false = no control)
DYNAMIC_FOVtruebooleanDynamically adjust camera Field of View based on speed
DYNAMIC_FOV_MAX0.10numberMaximum camera FOV expansion amount at full sprint speed
CAM_FOLLOW_LOCKtruebooleanIf true, the camera is locked behind the character's facing direction
CAM_FOLLOW_PITCH1.047numberCamera follow lock pitch (beta angle in radians, approx 60 degrees)
CAM_FOLLOW_DIST8.0numberCamera follow lock distance (radius in meters)
CAM_LOCK_PITCHfalsebooleanIf true, drag input only rotates camera horizontally (locks pitch axis)
JOYSTICK_LOCK_XfalsebooleanIf true, joystick input is locked to vertical axis only (no strafing)
DOUBLE_JUMP_ENABLEDtruebooleanIf true, the character can perform a double jump in mid-air
SPEED_MULTIPLIER1.0numberSpeed multiplier for walking, running, and jogging
PLAY_PARTICLEStruebooleanPlay procedural dust/smoke particles under the character's feet

🔄 Dynamic Animation Remapping

You can dynamically change any animation on the character controller or adjust keyframe ranges at runtime using the AnimCtrl instance (accessed via charCtrl.anim):

1. Reassigning Animations (Setters)

Pass a new Babylon AnimationGroup to dynamically swap any of the pre-mapped animations:

// Remap basic locomotioncharCtrl.anim.setWalkAnim(newWalkAnimGroup);charCtrl.anim.setRunAnim(newRunAnimGroup);charCtrl.anim.setIdleAnim(newIdleAnimGroup);// Remap crouch statescharCtrl.anim.setCrouchIdleAnim(newCrouchIdle);charCtrl.anim.setCrouchFwdAnim(newCrouchWalk);// Remap jumps and actionscharCtrl.anim.setJumpStartAnim(newJumpStart);charCtrl.anim.setJumpLoopAnim(newJumpLoop);charCtrl.anim.setJumpLandAnim(newJumpLand);charCtrl.anim.setRollAnim(newRoll);charCtrl.anim.setPunchJabAnim(newPunchJab);charCtrl.anim.setPunchCrossAnim(newPunchCross);charCtrl.anim.setSpellEnterAnim(newSpellEnter);charCtrl.anim.setSpellShootAnim(newSpellShoot);charCtrl.anim.setSpellExitAnim(newSpellExit);charCtrl.anim.setInteractAnim(newInteract);// Remap any custom animation keycharCtrl.anim.setAnimation('Custom_State_Name',myAnimGroup);

2. Modifying Playback Keyframe Ranges

Change the start/end frames of an animation without replacing the group:

// setAnimationRanges(animKey, startFrame, endFrame)charCtrl.anim.setAnimationRanges('Walk_Loop',10,45);

🕹️ Controls Layout

Keyboard (PC):

  • W, A, S, D / Arrow Keys: Movement.
  • Shift: Sprint (Toggle).
  • Ctrl: Crouch (Toggle).
  • Space: Jump / Double Jump.
  • R: Dodge roll / Air Dash:
    • Action Interrupt: Instantly cancels active attack combos or spell casts.
    • Roll Cooldown: 1.1s cooldown between rolls (triggers a "DODGE COOLDOWN" HUD alert).
    • Air Dash: If Double Jump is enabled in settings, performs a mid-air roll with a horizontal boost and a 55% jump-power vertical hop (usable even after double jumping).
  • Q: Punch combo.
  • E: Spell casting.
  • F: Interaction.
  • Mouse Drag: Orbit camera / Double-click to recenter.

Mobile Touch:

  • Left Hand: Floating Analog Joystick.
  • Right Hand (Buttons): SPELL, ACT, CROUCH, ROLL, SPRINT, JUMP.
  • Canvas Double-Tap: Recenter camera.

🛠️ Implementation Quickstart

The js/ directory is organized into subfolders by role:

  • js/character-controller.js — Unified core engine. Handles Havok Physics and Kinematic modes, locomotion state machines, and animation blending. Exports initPhysics and setupCharacter helpers.
  • js/ui/custom-hud.js — Tactile settings overlay (Camera Lock, Physics toggle, Dynamic FOV, Hide Cursor, Double Jump, Air Control, sliders). Optional.
  • js/ui/custom-pointer.js — Spring-damper trailing cursor ring. Optional.
  • js/examples/ — Ready-to-run setup templates (app.js, app-minimal.js, app-complex.js).
  • js/core/builder.js — Powers builder.html, the visual configuration tool (see below).

⚡ High-Level Setup (Recommended)

You can initialize physics and load the character in just a few lines of code using the shared helper functions: initPhysics and setupCharacter (wrapped in a clean loadCharacter helper function across the app templates). This helper supports configuring model paths, spawn locations, bounding ellipsoids, controls, and animations:

// 1. Define character initialization helperasyncfunctionloadCharacter(scene,shadow,camera,usePhysics){returnsetupCharacter(scene,camera,usePhysics,{
shadow,// Optional: shadow generator to add character meshes topersistPreferences: true,// Optional: allow HUD/browser preference persistenceassetsPath: 'assets/',// Optional: path to GLB assets folder (defaults to 'assets/')filename: 'character_animated.glb',// Optional: GLB file name (defaults to 'character_animated.glb')spawnPosition: newBABYLON.Vector3(0,2,0),// Optional: starting position overrideellipsoid: newBABYLON.Vector3(0.35,0.96,0.35),// Optional: collision ellipsoid overridekeys: {JUMP: ['KeyK']},// Optional: remap keyboard controls directlyconfig: {JUMP_PWR: 12},// Optional: override physical and camera parametersconfigure: ({ animCtrl, filteredGroups })=>{// Optional: callback to remap animations or customize keyframe rangesanimCtrl.setWalkAnim(filteredGroups[15]);}});}// 2. Initialize physics (Havok or Kinematic fallback)constusePhysics=awaitinitPhysics(scene,{persistPreferences: true});// 3. Load the character using the helperconst{ playerCapsule, animCtrl, charCtrl }=awaitloadCharacter(scene,shadow,camera,usePhysics);// 4. Hook up HUD setting toggles dynamically via custom-hud.jsif(typeofbindHUDControls==='function'){bindHUDControls(charCtrl,camera,usePhysics);}

We have provided three setup examples to guide your implementation:

  • js/examples/app-minimal.js: A bare-minimum integration template/guide to quickly see how to set up the Babylon.js engine, scene, capsule collider, parent the mesh, and initialize the controllers.
  • js/examples/app-complex.js: A full-featured setup designed to demonstrate how the character controller functions with a highly complex 3D scenery model (assets/backyard_demo.glb) containing many intricate, complex collisions and polygon-heavy geometry.
  • js/examples/app.js: A fully featured production loading example including advanced lighting, shadows, skyboxes, procedural environment shapes (boxes, ramp, stairs), post-processing, and HUD settings synchronization.

🔧 Visual Builder (builder.html)

BJS Character Controller V2 Builder

builder is an interactive GUI tool for visually configuring and exporting a custom character controller — no code editing required. You can use it as a static page, or run it with the local NodeJS development server to enable full backend-powered retargeting and GLB merges.

🌐 Running with NodeJS / npm (Recommended)

To run the local server which powers advanced skeletal retargeting, GLB animation merges, and asset optimizations via the local backend API:

  1. Install dependencies:

    npm install
  2. Start the local server:

    npm start
  3. Open the builder: Navigate to http://localhost:3000/builder in your browser.

  4. Run the complete verification suite before publishing changes:

    npm run check
    npm audit

Tabs

TabWhat it does
Import & RigImport GLB/FBX, choose the deforming body meshes, adjust transforms and bind pose, inspect skeleton health, edit canonical bone assignments and generate or rebuild rigs
AnimateAuto-match animation names, preview clips, define gameplay frame markers and add custom triggered actions
Input MappingRemap keyboard/gameplay actions and restore individual defaults
ControllerApply presets, use the live test lab, and tune movement, camera and feel
PhysicsConfigure collision, gravity, jumping, grounding, slopes and Havok/kinematic behaviour
Validate & ExportReview diagnostics and generated code, save/restore schema-validated configuration, or export a merged GLB and standalone controller

Builder preferences auto-save locally for editing convenience. Exported runtime controllers do not inherit that storage unless the application explicitly enables persistPreferences.

💀 FBX Direct Import & Bind-Pose Posture Tuning

When running the NodeJS backend, the Import & Rig tab offers advanced rigging, conversion, and alignment utilities:

  • Direct FBX Support: Drag-and-drop .fbx character models and animation files. The server auto-converts them to .glb under-the-hood (using fbx_api.mjs), fixing materials and flattening the RootNode transformation to avoid rotation/scale offset issues.
  • Scale & Pivot Offsets: Fine-tune character sizing using uniform scaling or independent X, Y, and Z scaling. Adjust the pivot offset (X, Y, Z) and use the Pivot to Ground helper to easily snap a character's feet to the ground level.
  • Skeletal Posture Adjustments: Straighten or adjust character postures (e.g., matching A-poses to T-poses) using bind-pose angle sliders for Arm Spread, Arm Splay, Shoulder Raise, Leg Spread, Hips Tilt, and Spine Straightening.
  • Skeleton Tree & Health Report: View the hierarchy, humanoid/quadruped body plan, coverage, duplicate and unresolved roles, and confidence/reason for every canonical mapping. Every role can be reassigned to an exact node without renaming the source asset.

💀 Auto-Rig (skeleton generation for skinless meshes)

If you import a mesh-only GLB (no skeleton/skin), Import & Rig → Skeleton offers Generate Skeleton (Auto-Rig):

  1. Choose exactly which meshes form the deforming body. Automatic selection excludes likely floors, props and accessories; manual selection is available for ambiguous assets. The server then analyzes the selected vertex cloud — not just the bounding box — and selects a humanoid or quadruped body plan.
  2. For humanoids it proposes Mixamo-named joint positions: it detects the crotch (where the body splits into legs), shoulder height, hand positions (works for both T-pose and A-pose meshes), per-leg offsets, and follows hunched spines. For meshes in non-standard poses (crouching, sitting, action poses) a pose-independent topology pass kicks in automatically: the mesh is voxelized, the interior is filled (works on non-watertight meshes), and the five extremities (head, hands, feet) are found on the geodesic graph and classified by body topology — legs merge far from the head, arms merge near it. Joints are placed along the detected limb centerlines.
  3. The builder enters a dedicated rig viewport mode: the character is isolated, draggable yellow joint markers appear, with Front/Side/Top camera presets (keys 1/2/3) and optional symmetric editing (left ↔ right mirroring).
  4. Apply Rig builds the skeleton, computes bounded topology-aware skin weights server-side, preserves unselected rigid meshes and morph deltas, and re-merges the animation set automatically. The final quality report records selection, coverage, warnings and compatibility.

Already-rigged characters get Re-Rig / Adjust Skeleton instead: markers seed from the current bind pose, and applying moves the existing joints while preserving the hierarchy, extra bones (fingers/twist) and the original artist skin weights.

🎭 Custom Actions & Animations

In Animate → Custom Animations, you can extend the controller by registering completely new character actions (e.g., TAUNT, DANCE, WAVE):

  • Map a custom action name to any animation group in the library.
  • Assign key triggers directly to the custom action.
  • In the exported snippet, these actions are configured and bound automatically.
  • You can trigger custom actions through the complete controller state machine using charCtrl.triggerAction('CUSTOM_ACTION_NAME').

🎯 Animation Events (gameplay frame markers)

In Animate → Animation Events you can attach typed markers (footstep, hit, cast, sound, particle, camera, custom) to any mapped animation at a specific frame:

  • Markers fire live in the builder viewport (toast + console) while previewing or playing animations — including during crossfades and inside the Locomotion blend tree (footsteps fire on Walk/Sprint loops).
  • Markers survive character swaps: they are kept as long as the slot maps to the same clip, and a Clear All button removes every marker at once.
  • The Export tab emits them as charCtrl.animationEvents. Consume them in your game:
charCtrl.animationEvents={Punch: [{type: 'hit',frame: 12,label: 'impact'}],Walk_Loop: [{type: 'footstep',frame: 5},{type: 'footstep',frame: 19}],};charCtrl.onAnimationEvent=(evt,animName)=>{if(evt.type==='hit')applyDamage();if(evt.type==='footstep')playFootstepSound();};// or listen globally:window.addEventListener('charanimevent',(e)=>console.log(e.detail));

🧪 Controller Presets & Test Lab

The Controller tab includes four one-click controller presets (Balanced Adventure, Action Combat, Arcade Platformer, Cinematic Walkthrough) and a Controller Test Lab: scenario camera chips (Studio / Motion / Air / Close Cam), action buttons (Idle, Walk, Sprint, Jump, Roll, Crouch — locomotion buttons drive the real blend tree, exactly like in-game), and a live metrics panel (state, speed, grounded, active animation, camera framing).

↺ Parameter Reset Buttons

Beside every slider, toggle, or control mapping under Controller, Physics and Input Mapping, there is an reset button. Clicking it restores that single parameter without clearing the rest of the setup.

🔄 Retargeting & Animation Merging (merge_api.mjs)

The Visual Builder uses the canonical server-side module merge_api.mjs (via server.mjs) to analyze, retarget and combine characters and animations deterministically.

When using the builder, you can import assets in different ways:

  • Separate Import: Load a character in Import & Rig, then one or more external animation GLBs/FBXs in Animate.
  • Embedded Animations: A character's own animation groups are available immediately after import; no duplicate upload is required.

📥 Exporting & Downloading Options

Validate & Export provides four distinct ways to output your configuration and assets for production:

🔀 Integration Modes (Pre-merged GLB vs. Runtime Retargeting)

When exporting your setup, you can choose between two integration architectures depending on your project needs:

  • Pre-merged GLB (Baked Merge):
    • How it works: Merges character meshes and animation channels into a single character_animated.glb file.
    • Best for: Single character games, simple setups, or engines where loading multiple separate files is not desired.
    • Drawback: Duplicate data. If you have 10 characters sharing the same locomotion set, you will be downloading those animation frames 10 times.
  • Runtime Retargeting (Client-Side Dynamic Retargeting):
    • How it works: Keeps character meshes (character.glb) and animation libraries (animations.glb) separate. setupCharacter requests the same canonical server merge used by the builder and falls back to client retargeting when the service is unavailable. Manual boneMapOverrides travel with the exported setup.
    • Best for: Multi-character games, RPGs, or modular projects. Reuses one shared animation file across dozens of characters, drastically reducing download size and memory footprint.
    • Note: Requires Babylon.js 9+. Cross-convention rigs are most accurate with the local merge service available.

1. 📋 Export Code Snippet (Preview & Copy)

This provides a complete, custom loadCharacter helper function matching your settings. Copy and paste it directly into your app.js entry file to replace the default loader. It automatically bakes in:

  • Mesh Transform Scaling (capsuleScale).
  • Custom Key Bindings (keys mappings).
  • Physics Config Parameters (config defaults).
  • Mapped Animations & Custom Actions (configure callback).
  • Animation Events (animationEvents markers).

2. 💾 Saving & Restoring Builder Config (builder-config.json)

Allows you to save/load your visual builder configuration presets:

  • Download builder-config.json: Saves schema-versioned transforms, exact bone assignments, key bindings, physics settings, animation mappings, custom actions and events. It intentionally does not embed model or animation binaries (includesAssets: false).
  • Import builder-config.json: Restore your saved configuration at any time to resume working in the builder without losing your adjustments.

3. 📦 Exporting the Character as GLB (with animations)

Click Download character_animated.glb to download a single, self-contained GLB file that merges your character mesh with the active animations retargeted and merged directly into the skeletal structures on the server. Ready for drag-and-drop into your assets folder.

⚡ 4. Downloading Baked Controller (custom-character-controller.js)

Generates a tailored standalone character-controller.js file with your settings pre-baked:

  • Replaces the default configurations (DEFAULT_CHAR_CONFIG) inside the script with your custom physics, keys, and touch layouts.
  • Keeps baked defaults authoritative and does not inject or mutate localStorage; persistence remains an explicit application choice.
  • Bakes all standard and custom animation remappings, frame ranges, and event markers directly into the controller's setup hooks, acting as a complete drop-in replacement with zero extra code required in your loader scripts.
<!-- Use the downloaded file in place of the original: --><scriptsrc="js/character-controller.js"></script><!-- or, if using the builder export: --><scriptsrc="js/custom-character-controller.js"></script>

📚 Credits & License

  • Rig: Customized Mixamo skeletal rig.
  • Animations: Universal Animation Library by Quaternius.
  • License: Licensed under the MIT License - see LICENSE for details. Keep the copyright notice and attribute the authorship of the Character Controller to Diego Ramirez in all copies.

About

No description, website, or topics provided.

Resources

Stars

8 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' GitHub - crazyramirez/BJS_Character_Controller_V2 · GitHub
Skip to content

Repository files navigation

🎮 3D Character Animation Controller V2 for Babylon.js

An advanced third-person character locomotion and physics framework built with Babylon.js. This framework provides a fluid, powerful, and easy-to-use Character Controller with integrated physics, animations, and high-end visual features.

🎮 Live Demo: https://viseni.com/demos/bjs_character_controller_v2/

BJS Character Controller V2 Screenshot

☕ If this controller saves you time, consider supporting its development!

Buy Me A Coffee


🚀 Key Features

  • Dual-Movement Modes (Physics vs Kinematic): Toggle dynamically between Havok Physics (dynamic simulation with body bodies) and standard Kinematic Collisions (ellipsoid-based movement) directly from the HUD.
  • Locomotion Blend Tree: Smoothly blends weight and speed between Idle, Walk, and Sprint.
  • Dual-State Toggle Coexistence: Crouch and Sprint operate as persistent toggles and can co-exist (allowing crouch-running).
  • Dynamic Zoom & Camera Follow: Smooth camera tracking with automated user-zoom sync (mouse wheel, trackpad, pinch) and double-tap recentering.
  • Dynamic FOV & Camera Shake: Camera Field of View expands with speed. Rotational camera shake is triggered on landing impacts relative to fall height.
  • Camera Follow Lock (Direct Steering): Locks the camera directly behind the character for tank-style direct controls.
  • Visual Enhancements: Procedural dust/smoke trails at the feet, procedural leaning/banking on turns, slope-incline alignment, and squash & stretch scaling.
  • Collision height adjustments & Ceiling protection: Shrinks the capsule automatically when crouching/rolling, prevents standing up or rolling under low ceilings, and expands width when sprinting to prevent wall clipping.
  • Ledge & Stairs Snapping: Keeps the character grounded on sloped surfaces and stairs to prevent airborne jitter.
  • Slope-Aligned Kinematic Traversal: Kinematic collisions mode projects movement directly onto the ground normal to ensure butter-smooth ascent/descent on ramps and slopes.
  • Smart Snap-Down Controls: Dynamically disables downward snap forces when ascending stairs or steep slopes to eliminate physics/collision jitter.
  • Implicit Self-Collision Prevention: Prevents parent-capsule jitter by automatically disabling collision checks (checkCollisions = false) on imported character visual meshes.
  • Mobile Touch Support: Responsive virtual joystick and customizable glassmorphism action buttons.
  • Gamepad Support: Analog movement with configurable dead zone and edge-triggered jump, roll, sprint, crouch and action buttons.
  • Production Rigging Pipeline: Human and quadruped analysis, body-mesh selection, editable canonical bone assignment, topology-aware auto-rigging, quality diagnostics and deterministic animation retargeting.
  • Air Dash (Mid-Air Roll): Perform a responsive dodge roll in mid-air with a horizontal speed boost and a 55% jump-power vertical hop (available if Double Jump is enabled, works even after double jumping).
  • Action Interrupt Roll: Pressing Roll immediately interrupts active attack combos or spell casts for instant responsiveness.
  • Roll Cooldown & HUD Feedback: A 1.1s cooldown prevents roll spamming, displaying a "DODGE COOLDOWN" HUD warning when pressed too early.
  • Toggleable Action HUD Texts: Toggle on-screen action text alerts (like "AIR DASH", "JAB", "CROSS!") directly from the System & UI settings drawer.

⚖️ Physics vs. Kinematic Modes

character-controller.js is a unified single-file engine that runs in two distinct physics regimes. Both modes live in the same class — a single usePhysics flag switches the internal code paths at initialization time.

  • Havok Physics (Default): Leverages the WASM-powered Havok Physics engine. The character capsule is created as a dynamic PhysicsBody with defined mass and inertia properties, interacting naturally with other dynamic aggregates (like boxes, cylinders, and triggers).
  • Kinematic Collisions: Runs entirely within Babylon's native collision engine using kinematic ellipsoids (moveWithCollisions). Havok initialization is skipped entirely, providing maximum performance and deterministic locomotion.

Explicit configuration (default behaviour)

Runtime options are authoritative. initPhysics(scene) tries Havok and falls back to kinematic collisions without reading or modifying browser storage. Pass usePhysics: false when kinematic mode is required.

Overriding the mode

Preference persistence is deliberately opt-in. The bundled HUD examples use persistPreferences: true; embedded applications remain deterministic by default:

constusePhysics=awaitinitPhysics(scene,{usePhysics: true,persistPreferences: true});

The same policy is available on the controller constructor:

constcharCtrl=newCharCtrl(playerCapsule,charRoot,camera,animCtrl,scene,{usePhysics: true,// or falsepersistPreferences: false,config: {SPEED_MULTIPLIER: 1.5// Multiplies walking, running and jogging speeds}});

⚙️ Configuration Parameters

The config object in the constructor accepts a wide range of physics, camera, and gameplay properties to fine-tune character behavior:

ParameterDefaultTypeDescription
GRAV22numberGravity force pulling the character down
JUMP_PWR9.5numberVertical takeoff impulse force for jumping
SPD_WALK2.5numberMaximum physical walking speed
SPD_JOG3.0numberMaximum physical jogging speed (blend speed threshold)
SPD_SPRINT5.0numberMaximum physical sprinting speed
SPD_CROUCH2.0numberMaximum physical crouching walk speed
SPD_CROUCH_RUN3.2numberMaximum physical crouching run speed
ACCEL14numberMovement acceleration rate (speed-up responsiveness)
DECEL16numberMovement deceleration rate (braking/stopping responsiveness)
ROT_SPD40numberCharacter yaw rotation speed responsiveness
AIR_CONTROLfalsebooleanSteering control in mid-air (true = full control, false = no control)
DYNAMIC_FOVtruebooleanDynamically adjust camera Field of View based on speed
DYNAMIC_FOV_MAX0.10numberMaximum camera FOV expansion amount at full sprint speed
CAM_FOLLOW_LOCKtruebooleanIf true, the camera is locked behind the character's facing direction
CAM_FOLLOW_PITCH1.047numberCamera follow lock pitch (beta angle in radians, approx 60 degrees)
CAM_FOLLOW_DIST8.0numberCamera follow lock distance (radius in meters)
CAM_LOCK_PITCHfalsebooleanIf true, drag input only rotates camera horizontally (locks pitch axis)
JOYSTICK_LOCK_XfalsebooleanIf true, joystick input is locked to vertical axis only (no strafing)
DOUBLE_JUMP_ENABLEDtruebooleanIf true, the character can perform a double jump in mid-air
SPEED_MULTIPLIER1.0numberSpeed multiplier for walking, running, and jogging
PLAY_PARTICLEStruebooleanPlay procedural dust/smoke particles under the character's feet

🔄 Dynamic Animation Remapping

You can dynamically change any animation on the character controller or adjust keyframe ranges at runtime using the AnimCtrl instance (accessed via charCtrl.anim):

1. Reassigning Animations (Setters)

Pass a new Babylon AnimationGroup to dynamically swap any of the pre-mapped animations:

// Remap basic locomotioncharCtrl.anim.setWalkAnim(newWalkAnimGroup);charCtrl.anim.setRunAnim(newRunAnimGroup);charCtrl.anim.setIdleAnim(newIdleAnimGroup);// Remap crouch statescharCtrl.anim.setCrouchIdleAnim(newCrouchIdle);charCtrl.anim.setCrouchFwdAnim(newCrouchWalk);// Remap jumps and actionscharCtrl.anim.setJumpStartAnim(newJumpStart);charCtrl.anim.setJumpLoopAnim(newJumpLoop);charCtrl.anim.setJumpLandAnim(newJumpLand);charCtrl.anim.setRollAnim(newRoll);charCtrl.anim.setPunchJabAnim(newPunchJab);charCtrl.anim.setPunchCrossAnim(newPunchCross);charCtrl.anim.setSpellEnterAnim(newSpellEnter);charCtrl.anim.setSpellShootAnim(newSpellShoot);charCtrl.anim.setSpellExitAnim(newSpellExit);charCtrl.anim.setInteractAnim(newInteract);// Remap any custom animation keycharCtrl.anim.setAnimation('Custom_State_Name',myAnimGroup);

2. Modifying Playback Keyframe Ranges

Change the start/end frames of an animation without replacing the group:

// setAnimationRanges(animKey, startFrame, endFrame)charCtrl.anim.setAnimationRanges('Walk_Loop',10,45);

🕹️ Controls Layout

Keyboard (PC):

  • W, A, S, D / Arrow Keys: Movement.
  • Shift: Sprint (Toggle).
  • Ctrl: Crouch (Toggle).
  • Space: Jump / Double Jump.
  • R: Dodge roll / Air Dash:
    • Action Interrupt: Instantly cancels active attack combos or spell casts.
    • Roll Cooldown: 1.1s cooldown between rolls (triggers a "DODGE COOLDOWN" HUD alert).
    • Air Dash: If Double Jump is enabled in settings, performs a mid-air roll with a horizontal boost and a 55% jump-power vertical hop (usable even after double jumping).
  • Q: Punch combo.
  • E: Spell casting.
  • F: Interaction.
  • Mouse Drag: Orbit camera / Double-click to recenter.

Mobile Touch:

  • Left Hand: Floating Analog Joystick.
  • Right Hand (Buttons): SPELL, ACT, CROUCH, ROLL, SPRINT, JUMP.
  • Canvas Double-Tap: Recenter camera.

🛠️ Implementation Quickstart

The js/ directory is organized into subfolders by role:

  • js/character-controller.js — Unified core engine. Handles Havok Physics and Kinematic modes, locomotion state machines, and animation blending. Exports initPhysics and setupCharacter helpers.
  • js/ui/custom-hud.js — Tactile settings overlay (Camera Lock, Physics toggle, Dynamic FOV, Hide Cursor, Double Jump, Air Control, sliders). Optional.
  • js/ui/custom-pointer.js — Spring-damper trailing cursor ring. Optional.
  • js/examples/ — Ready-to-run setup templates (app.js, app-minimal.js, app-complex.js).
  • js/core/builder.js — Powers builder.html, the visual configuration tool (see below).

⚡ High-Level Setup (Recommended)

You can initialize physics and load the character in just a few lines of code using the shared helper functions: initPhysics and setupCharacter (wrapped in a clean loadCharacter helper function across the app templates). This helper supports configuring model paths, spawn locations, bounding ellipsoids, controls, and animations:

// 1. Define character initialization helperasyncfunctionloadCharacter(scene,shadow,camera,usePhysics){returnsetupCharacter(scene,camera,usePhysics,{
shadow,// Optional: shadow generator to add character meshes topersistPreferences: true,// Optional: allow HUD/browser preference persistenceassetsPath: 'assets/',// Optional: path to GLB assets folder (defaults to 'assets/')filename: 'character_animated.glb',// Optional: GLB file name (defaults to 'character_animated.glb')spawnPosition: newBABYLON.Vector3(0,2,0),// Optional: starting position overrideellipsoid: newBABYLON.Vector3(0.35,0.96,0.35),// Optional: collision ellipsoid overridekeys: {JUMP: ['KeyK']},// Optional: remap keyboard controls directlyconfig: {JUMP_PWR: 12},// Optional: override physical and camera parametersconfigure: ({ animCtrl, filteredGroups })=>{// Optional: callback to remap animations or customize keyframe rangesanimCtrl.setWalkAnim(filteredGroups[15]);}});}// 2. Initialize physics (Havok or Kinematic fallback)constusePhysics=awaitinitPhysics(scene,{persistPreferences: true});// 3. Load the character using the helperconst{ playerCapsule, animCtrl, charCtrl }=awaitloadCharacter(scene,shadow,camera,usePhysics);// 4. Hook up HUD setting toggles dynamically via custom-hud.jsif(typeofbindHUDControls==='function'){bindHUDControls(charCtrl,camera,usePhysics);}

We have provided three setup examples to guide your implementation:

  • js/examples/app-minimal.js: A bare-minimum integration template/guide to quickly see how to set up the Babylon.js engine, scene, capsule collider, parent the mesh, and initialize the controllers.
  • js/examples/app-complex.js: A full-featured setup designed to demonstrate how the character controller functions with a highly complex 3D scenery model (assets/backyard_demo.glb) containing many intricate, complex collisions and polygon-heavy geometry.
  • js/examples/app.js: A fully featured production loading example including advanced lighting, shadows, skyboxes, procedural environment shapes (boxes, ramp, stairs), post-processing, and HUD settings synchronization.

🔧 Visual Builder (builder.html)

BJS Character Controller V2 Builder

builder is an interactive GUI tool for visually configuring and exporting a custom character controller — no code editing required. You can use it as a static page, or run it with the local NodeJS development server to enable full backend-powered retargeting and GLB merges.

🌐 Running with NodeJS / npm (Recommended)

To run the local server which powers advanced skeletal retargeting, GLB animation merges, and asset optimizations via the local backend API:

  1. Install dependencies:

    npm install
  2. Start the local server:

    npm start
  3. Open the builder: Navigate to http://localhost:3000/builder in your browser.

  4. Run the complete verification suite before publishing changes:

    npm run check
    npm audit

Tabs

TabWhat it does
Import & RigImport GLB/FBX, choose the deforming body meshes, adjust transforms and bind pose, inspect skeleton health, edit canonical bone assignments and generate or rebuild rigs
AnimateAuto-match animation names, preview clips, define gameplay frame markers and add custom triggered actions
Input MappingRemap keyboard/gameplay actions and restore individual defaults
ControllerApply presets, use the live test lab, and tune movement, camera and feel
PhysicsConfigure collision, gravity, jumping, grounding, slopes and Havok/kinematic behaviour
Validate & ExportReview diagnostics and generated code, save/restore schema-validated configuration, or export a merged GLB and standalone controller

Builder preferences auto-save locally for editing convenience. Exported runtime controllers do not inherit that storage unless the application explicitly enables persistPreferences.

💀 FBX Direct Import & Bind-Pose Posture Tuning

When running the NodeJS backend, the Import & Rig tab offers advanced rigging, conversion, and alignment utilities:

  • Direct FBX Support: Drag-and-drop .fbx character models and animation files. The server auto-converts them to .glb under-the-hood (using fbx_api.mjs), fixing materials and flattening the RootNode transformation to avoid rotation/scale offset issues.
  • Scale & Pivot Offsets: Fine-tune character sizing using uniform scaling or independent X, Y, and Z scaling. Adjust the pivot offset (X, Y, Z) and use the Pivot to Ground helper to easily snap a character's feet to the ground level.
  • Skeletal Posture Adjustments: Straighten or adjust character postures (e.g., matching A-poses to T-poses) using bind-pose angle sliders for Arm Spread, Arm Splay, Shoulder Raise, Leg Spread, Hips Tilt, and Spine Straightening.
  • Skeleton Tree & Health Report: View the hierarchy, humanoid/quadruped body plan, coverage, duplicate and unresolved roles, and confidence/reason for every canonical mapping. Every role can be reassigned to an exact node without renaming the source asset.

💀 Auto-Rig (skeleton generation for skinless meshes)

If you import a mesh-only GLB (no skeleton/skin), Import & Rig → Skeleton offers Generate Skeleton (Auto-Rig):

  1. Choose exactly which meshes form the deforming body. Automatic selection excludes likely floors, props and accessories; manual selection is available for ambiguous assets. The server then analyzes the selected vertex cloud — not just the bounding box — and selects a humanoid or quadruped body plan.
  2. For humanoids it proposes Mixamo-named joint positions: it detects the crotch (where the body splits into legs), shoulder height, hand positions (works for both T-pose and A-pose meshes), per-leg offsets, and follows hunched spines. For meshes in non-standard poses (crouching, sitting, action poses) a pose-independent topology pass kicks in automatically: the mesh is voxelized, the interior is filled (works on non-watertight meshes), and the five extremities (head, hands, feet) are found on the geodesic graph and classified by body topology — legs merge far from the head, arms merge near it. Joints are placed along the detected limb centerlines.
  3. The builder enters a dedicated rig viewport mode: the character is isolated, draggable yellow joint markers appear, with Front/Side/Top camera presets (keys 1/2/3) and optional symmetric editing (left ↔ right mirroring).
  4. Apply Rig builds the skeleton, computes bounded topology-aware skin weights server-side, preserves unselected rigid meshes and morph deltas, and re-merges the animation set automatically. The final quality report records selection, coverage, warnings and compatibility.

Already-rigged characters get Re-Rig / Adjust Skeleton instead: markers seed from the current bind pose, and applying moves the existing joints while preserving the hierarchy, extra bones (fingers/twist) and the original artist skin weights.

🎭 Custom Actions & Animations

In Animate → Custom Animations, you can extend the controller by registering completely new character actions (e.g., TAUNT, DANCE, WAVE):

  • Map a custom action name to any animation group in the library.
  • Assign key triggers directly to the custom action.
  • In the exported snippet, these actions are configured and bound automatically.
  • You can trigger custom actions through the complete controller state machine using charCtrl.triggerAction('CUSTOM_ACTION_NAME').

🎯 Animation Events (gameplay frame markers)

In Animate → Animation Events you can attach typed markers (footstep, hit, cast, sound, particle, camera, custom) to any mapped animation at a specific frame:

  • Markers fire live in the builder viewport (toast + console) while previewing or playing animations — including during crossfades and inside the Locomotion blend tree (footsteps fire on Walk/Sprint loops).
  • Markers survive character swaps: they are kept as long as the slot maps to the same clip, and a Clear All button removes every marker at once.
  • The Export tab emits them as charCtrl.animationEvents. Consume them in your game:
charCtrl.animationEvents={Punch: [{type: 'hit',frame: 12,label: 'impact'}],Walk_Loop: [{type: 'footstep',frame: 5},{type: 'footstep',frame: 19}],};charCtrl.onAnimationEvent=(evt,animName)=>{if(evt.type==='hit')applyDamage();if(evt.type==='footstep')playFootstepSound();};// or listen globally:window.addEventListener('charanimevent',(e)=>console.log(e.detail));

🧪 Controller Presets & Test Lab

The Controller tab includes four one-click controller presets (Balanced Adventure, Action Combat, Arcade Platformer, Cinematic Walkthrough) and a Controller Test Lab: scenario camera chips (Studio / Motion / Air / Close Cam), action buttons (Idle, Walk, Sprint, Jump, Roll, Crouch — locomotion buttons drive the real blend tree, exactly like in-game), and a live metrics panel (state, speed, grounded, active animation, camera framing).

↺ Parameter Reset Buttons

Beside every slider, toggle, or control mapping under Controller, Physics and Input Mapping, there is an reset button. Clicking it restores that single parameter without clearing the rest of the setup.

🔄 Retargeting & Animation Merging (merge_api.mjs)

The Visual Builder uses the canonical server-side module merge_api.mjs (via server.mjs) to analyze, retarget and combine characters and animations deterministically.

When using the builder, you can import assets in different ways:

  • Separate Import: Load a character in Import & Rig, then one or more external animation GLBs/FBXs in Animate.
  • Embedded Animations: A character's own animation groups are available immediately after import; no duplicate upload is required.

📥 Exporting & Downloading Options

Validate & Export provides four distinct ways to output your configuration and assets for production:

🔀 Integration Modes (Pre-merged GLB vs. Runtime Retargeting)

When exporting your setup, you can choose between two integration architectures depending on your project needs:

  • Pre-merged GLB (Baked Merge):
    • How it works: Merges character meshes and animation channels into a single character_animated.glb file.
    • Best for: Single character games, simple setups, or engines where loading multiple separate files is not desired.
    • Drawback: Duplicate data. If you have 10 characters sharing the same locomotion set, you will be downloading those animation frames 10 times.
  • Runtime Retargeting (Client-Side Dynamic Retargeting):
    • How it works: Keeps character meshes (character.glb) and animation libraries (animations.glb) separate. setupCharacter requests the same canonical server merge used by the builder and falls back to client retargeting when the service is unavailable. Manual boneMapOverrides travel with the exported setup.
    • Best for: Multi-character games, RPGs, or modular projects. Reuses one shared animation file across dozens of characters, drastically reducing download size and memory footprint.
    • Note: Requires Babylon.js 9+. Cross-convention rigs are most accurate with the local merge service available.

1. 📋 Export Code Snippet (Preview & Copy)

This provides a complete, custom loadCharacter helper function matching your settings. Copy and paste it directly into your app.js entry file to replace the default loader. It automatically bakes in:

  • Mesh Transform Scaling (capsuleScale).
  • Custom Key Bindings (keys mappings).
  • Physics Config Parameters (config defaults).
  • Mapped Animations & Custom Actions (configure callback).
  • Animation Events (animationEvents markers).

2. 💾 Saving & Restoring Builder Config (builder-config.json)

Allows you to save/load your visual builder configuration presets:

  • Download builder-config.json: Saves schema-versioned transforms, exact bone assignments, key bindings, physics settings, animation mappings, custom actions and events. It intentionally does not embed model or animation binaries (includesAssets: false).
  • Import builder-config.json: Restore your saved configuration at any time to resume working in the builder without losing your adjustments.

3. 📦 Exporting the Character as GLB (with animations)

Click Download character_animated.glb to download a single, self-contained GLB file that merges your character mesh with the active animations retargeted and merged directly into the skeletal structures on the server. Ready for drag-and-drop into your assets folder.

⚡ 4. Downloading Baked Controller (custom-character-controller.js)

Generates a tailored standalone character-controller.js file with your settings pre-baked:

  • Replaces the default configurations (DEFAULT_CHAR_CONFIG) inside the script with your custom physics, keys, and touch layouts.
  • Keeps baked defaults authoritative and does not inject or mutate localStorage; persistence remains an explicit application choice.
  • Bakes all standard and custom animation remappings, frame ranges, and event markers directly into the controller's setup hooks, acting as a complete drop-in replacement with zero extra code required in your loader scripts.
<!-- Use the downloaded file in place of the original: --><scriptsrc="js/character-controller.js"></script><!-- or, if using the builder export: --><scriptsrc="js/custom-character-controller.js"></script>

📚 Credits & License

  • Rig: Customized Mixamo skeletal rig.
  • Animations: Universal Animation Library by Quaternius.
  • License: Licensed under the MIT License - see LICENSE for details. Keep the copyright notice and attribute the authorship of the Character Controller to Diego Ramirez in all copies.

About

No description, website, or topics provided.

Resources

Stars

8 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' GitHub - crazyramirez/BJS_Character_Controller_V2 · GitHub
Skip to content

Repository files navigation

🎮 3D Character Animation Controller V2 for Babylon.js

An advanced third-person character locomotion and physics framework built with Babylon.js. This framework provides a fluid, powerful, and easy-to-use Character Controller with integrated physics, animations, and high-end visual features.

🎮 Live Demo: https://viseni.com/demos/bjs_character_controller_v2/

BJS Character Controller V2 Screenshot

☕ If this controller saves you time, consider supporting its development!

Buy Me A Coffee


🚀 Key Features

  • Dual-Movement Modes (Physics vs Kinematic): Toggle dynamically between Havok Physics (dynamic simulation with body bodies) and standard Kinematic Collisions (ellipsoid-based movement) directly from the HUD.
  • Locomotion Blend Tree: Smoothly blends weight and speed between Idle, Walk, and Sprint.
  • Dual-State Toggle Coexistence: Crouch and Sprint operate as persistent toggles and can co-exist (allowing crouch-running).
  • Dynamic Zoom & Camera Follow: Smooth camera tracking with automated user-zoom sync (mouse wheel, trackpad, pinch) and double-tap recentering.
  • Dynamic FOV & Camera Shake: Camera Field of View expands with speed. Rotational camera shake is triggered on landing impacts relative to fall height.
  • Camera Follow Lock (Direct Steering): Locks the camera directly behind the character for tank-style direct controls.
  • Visual Enhancements: Procedural dust/smoke trails at the feet, procedural leaning/banking on turns, slope-incline alignment, and squash & stretch scaling.
  • Collision height adjustments & Ceiling protection: Shrinks the capsule automatically when crouching/rolling, prevents standing up or rolling under low ceilings, and expands width when sprinting to prevent wall clipping.
  • Ledge & Stairs Snapping: Keeps the character grounded on sloped surfaces and stairs to prevent airborne jitter.
  • Slope-Aligned Kinematic Traversal: Kinematic collisions mode projects movement directly onto the ground normal to ensure butter-smooth ascent/descent on ramps and slopes.
  • Smart Snap-Down Controls: Dynamically disables downward snap forces when ascending stairs or steep slopes to eliminate physics/collision jitter.
  • Implicit Self-Collision Prevention: Prevents parent-capsule jitter by automatically disabling collision checks (checkCollisions = false) on imported character visual meshes.
  • Mobile Touch Support: Responsive virtual joystick and customizable glassmorphism action buttons.
  • Gamepad Support: Analog movement with configurable dead zone and edge-triggered jump, roll, sprint, crouch and action buttons.
  • Production Rigging Pipeline: Human and quadruped analysis, body-mesh selection, editable canonical bone assignment, topology-aware auto-rigging, quality diagnostics and deterministic animation retargeting.
  • Air Dash (Mid-Air Roll): Perform a responsive dodge roll in mid-air with a horizontal speed boost and a 55% jump-power vertical hop (available if Double Jump is enabled, works even after double jumping).
  • Action Interrupt Roll: Pressing Roll immediately interrupts active attack combos or spell casts for instant responsiveness.
  • Roll Cooldown & HUD Feedback: A 1.1s cooldown prevents roll spamming, displaying a "DODGE COOLDOWN" HUD warning when pressed too early.
  • Toggleable Action HUD Texts: Toggle on-screen action text alerts (like "AIR DASH", "JAB", "CROSS!") directly from the System & UI settings drawer.

⚖️ Physics vs. Kinematic Modes

character-controller.js is a unified single-file engine that runs in two distinct physics regimes. Both modes live in the same class — a single usePhysics flag switches the internal code paths at initialization time.

  • Havok Physics (Default): Leverages the WASM-powered Havok Physics engine. The character capsule is created as a dynamic PhysicsBody with defined mass and inertia properties, interacting naturally with other dynamic aggregates (like boxes, cylinders, and triggers).
  • Kinematic Collisions: Runs entirely within Babylon's native collision engine using kinematic ellipsoids (moveWithCollisions). Havok initialization is skipped entirely, providing maximum performance and deterministic locomotion.

Explicit configuration (default behaviour)

Runtime options are authoritative. initPhysics(scene) tries Havok and falls back to kinematic collisions without reading or modifying browser storage. Pass usePhysics: false when kinematic mode is required.

Overriding the mode

Preference persistence is deliberately opt-in. The bundled HUD examples use persistPreferences: true; embedded applications remain deterministic by default:

constusePhysics=awaitinitPhysics(scene,{usePhysics: true,persistPreferences: true});

The same policy is available on the controller constructor:

constcharCtrl=newCharCtrl(playerCapsule,charRoot,camera,animCtrl,scene,{usePhysics: true,// or falsepersistPreferences: false,config: {SPEED_MULTIPLIER: 1.5// Multiplies walking, running and jogging speeds}});

⚙️ Configuration Parameters

The config object in the constructor accepts a wide range of physics, camera, and gameplay properties to fine-tune character behavior:

ParameterDefaultTypeDescription
GRAV22numberGravity force pulling the character down
JUMP_PWR9.5numberVertical takeoff impulse force for jumping
SPD_WALK2.5numberMaximum physical walking speed
SPD_JOG3.0numberMaximum physical jogging speed (blend speed threshold)
SPD_SPRINT5.0numberMaximum physical sprinting speed
SPD_CROUCH2.0numberMaximum physical crouching walk speed
SPD_CROUCH_RUN3.2numberMaximum physical crouching run speed
ACCEL14numberMovement acceleration rate (speed-up responsiveness)
DECEL16numberMovement deceleration rate (braking/stopping responsiveness)
ROT_SPD40numberCharacter yaw rotation speed responsiveness
AIR_CONTROLfalsebooleanSteering control in mid-air (true = full control, false = no control)
DYNAMIC_FOVtruebooleanDynamically adjust camera Field of View based on speed
DYNAMIC_FOV_MAX0.10numberMaximum camera FOV expansion amount at full sprint speed
CAM_FOLLOW_LOCKtruebooleanIf true, the camera is locked behind the character's facing direction
CAM_FOLLOW_PITCH1.047numberCamera follow lock pitch (beta angle in radians, approx 60 degrees)
CAM_FOLLOW_DIST8.0numberCamera follow lock distance (radius in meters)
CAM_LOCK_PITCHfalsebooleanIf true, drag input only rotates camera horizontally (locks pitch axis)
JOYSTICK_LOCK_XfalsebooleanIf true, joystick input is locked to vertical axis only (no strafing)
DOUBLE_JUMP_ENABLEDtruebooleanIf true, the character can perform a double jump in mid-air
SPEED_MULTIPLIER1.0numberSpeed multiplier for walking, running, and jogging
PLAY_PARTICLEStruebooleanPlay procedural dust/smoke particles under the character's feet

🔄 Dynamic Animation Remapping

You can dynamically change any animation on the character controller or adjust keyframe ranges at runtime using the AnimCtrl instance (accessed via charCtrl.anim):

1. Reassigning Animations (Setters)

Pass a new Babylon AnimationGroup to dynamically swap any of the pre-mapped animations:

// Remap basic locomotioncharCtrl.anim.setWalkAnim(newWalkAnimGroup);charCtrl.anim.setRunAnim(newRunAnimGroup);charCtrl.anim.setIdleAnim(newIdleAnimGroup);// Remap crouch statescharCtrl.anim.setCrouchIdleAnim(newCrouchIdle);charCtrl.anim.setCrouchFwdAnim(newCrouchWalk);// Remap jumps and actionscharCtrl.anim.setJumpStartAnim(newJumpStart);charCtrl.anim.setJumpLoopAnim(newJumpLoop);charCtrl.anim.setJumpLandAnim(newJumpLand);charCtrl.anim.setRollAnim(newRoll);charCtrl.anim.setPunchJabAnim(newPunchJab);charCtrl.anim.setPunchCrossAnim(newPunchCross);charCtrl.anim.setSpellEnterAnim(newSpellEnter);charCtrl.anim.setSpellShootAnim(newSpellShoot);charCtrl.anim.setSpellExitAnim(newSpellExit);charCtrl.anim.setInteractAnim(newInteract);// Remap any custom animation keycharCtrl.anim.setAnimation('Custom_State_Name',myAnimGroup);

2. Modifying Playback Keyframe Ranges

Change the start/end frames of an animation without replacing the group:

// setAnimationRanges(animKey, startFrame, endFrame)charCtrl.anim.setAnimationRanges('Walk_Loop',10,45);

🕹️ Controls Layout

Keyboard (PC):

  • W, A, S, D / Arrow Keys: Movement.
  • Shift: Sprint (Toggle).
  • Ctrl: Crouch (Toggle).
  • Space: Jump / Double Jump.
  • R: Dodge roll / Air Dash:
    • Action Interrupt: Instantly cancels active attack combos or spell casts.
    • Roll Cooldown: 1.1s cooldown between rolls (triggers a "DODGE COOLDOWN" HUD alert).
    • Air Dash: If Double Jump is enabled in settings, performs a mid-air roll with a horizontal boost and a 55% jump-power vertical hop (usable even after double jumping).
  • Q: Punch combo.
  • E: Spell casting.
  • F: Interaction.
  • Mouse Drag: Orbit camera / Double-click to recenter.

Mobile Touch:

  • Left Hand: Floating Analog Joystick.
  • Right Hand (Buttons): SPELL, ACT, CROUCH, ROLL, SPRINT, JUMP.
  • Canvas Double-Tap: Recenter camera.

🛠️ Implementation Quickstart

The js/ directory is organized into subfolders by role:

  • js/character-controller.js — Unified core engine. Handles Havok Physics and Kinematic modes, locomotion state machines, and animation blending. Exports initPhysics and setupCharacter helpers.
  • js/ui/custom-hud.js — Tactile settings overlay (Camera Lock, Physics toggle, Dynamic FOV, Hide Cursor, Double Jump, Air Control, sliders). Optional.
  • js/ui/custom-pointer.js — Spring-damper trailing cursor ring. Optional.
  • js/examples/ — Ready-to-run setup templates (app.js, app-minimal.js, app-complex.js).
  • js/core/builder.js — Powers builder.html, the visual configuration tool (see below).

⚡ High-Level Setup (Recommended)

You can initialize physics and load the character in just a few lines of code using the shared helper functions: initPhysics and setupCharacter (wrapped in a clean loadCharacter helper function across the app templates). This helper supports configuring model paths, spawn locations, bounding ellipsoids, controls, and animations:

// 1. Define character initialization helperasyncfunctionloadCharacter(scene,shadow,camera,usePhysics){returnsetupCharacter(scene,camera,usePhysics,{
shadow,// Optional: shadow generator to add character meshes topersistPreferences: true,// Optional: allow HUD/browser preference persistenceassetsPath: 'assets/',// Optional: path to GLB assets folder (defaults to 'assets/')filename: 'character_animated.glb',// Optional: GLB file name (defaults to 'character_animated.glb')spawnPosition: newBABYLON.Vector3(0,2,0),// Optional: starting position overrideellipsoid: newBABYLON.Vector3(0.35,0.96,0.35),// Optional: collision ellipsoid overridekeys: {JUMP: ['KeyK']},// Optional: remap keyboard controls directlyconfig: {JUMP_PWR: 12},// Optional: override physical and camera parametersconfigure: ({ animCtrl, filteredGroups })=>{// Optional: callback to remap animations or customize keyframe rangesanimCtrl.setWalkAnim(filteredGroups[15]);}});}// 2. Initialize physics (Havok or Kinematic fallback)constusePhysics=awaitinitPhysics(scene,{persistPreferences: true});// 3. Load the character using the helperconst{ playerCapsule, animCtrl, charCtrl }=awaitloadCharacter(scene,shadow,camera,usePhysics);// 4. Hook up HUD setting toggles dynamically via custom-hud.jsif(typeofbindHUDControls==='function'){bindHUDControls(charCtrl,camera,usePhysics);}

We have provided three setup examples to guide your implementation:

  • js/examples/app-minimal.js: A bare-minimum integration template/guide to quickly see how to set up the Babylon.js engine, scene, capsule collider, parent the mesh, and initialize the controllers.
  • js/examples/app-complex.js: A full-featured setup designed to demonstrate how the character controller functions with a highly complex 3D scenery model (assets/backyard_demo.glb) containing many intricate, complex collisions and polygon-heavy geometry.
  • js/examples/app.js: A fully featured production loading example including advanced lighting, shadows, skyboxes, procedural environment shapes (boxes, ramp, stairs), post-processing, and HUD settings synchronization.

🔧 Visual Builder (builder.html)

BJS Character Controller V2 Builder

builder is an interactive GUI tool for visually configuring and exporting a custom character controller — no code editing required. You can use it as a static page, or run it with the local NodeJS development server to enable full backend-powered retargeting and GLB merges.

🌐 Running with NodeJS / npm (Recommended)

To run the local server which powers advanced skeletal retargeting, GLB animation merges, and asset optimizations via the local backend API:

  1. Install dependencies:

    npm install
  2. Start the local server:

    npm start
  3. Open the builder: Navigate to http://localhost:3000/builder in your browser.

  4. Run the complete verification suite before publishing changes:

    npm run check
    npm audit

Tabs

TabWhat it does
Import & RigImport GLB/FBX, choose the deforming body meshes, adjust transforms and bind pose, inspect skeleton health, edit canonical bone assignments and generate or rebuild rigs
AnimateAuto-match animation names, preview clips, define gameplay frame markers and add custom triggered actions
Input MappingRemap keyboard/gameplay actions and restore individual defaults
ControllerApply presets, use the live test lab, and tune movement, camera and feel
PhysicsConfigure collision, gravity, jumping, grounding, slopes and Havok/kinematic behaviour
Validate & ExportReview diagnostics and generated code, save/restore schema-validated configuration, or export a merged GLB and standalone controller

Builder preferences auto-save locally for editing convenience. Exported runtime controllers do not inherit that storage unless the application explicitly enables persistPreferences.

💀 FBX Direct Import & Bind-Pose Posture Tuning

When running the NodeJS backend, the Import & Rig tab offers advanced rigging, conversion, and alignment utilities:

  • Direct FBX Support: Drag-and-drop .fbx character models and animation files. The server auto-converts them to .glb under-the-hood (using fbx_api.mjs), fixing materials and flattening the RootNode transformation to avoid rotation/scale offset issues.
  • Scale & Pivot Offsets: Fine-tune character sizing using uniform scaling or independent X, Y, and Z scaling. Adjust the pivot offset (X, Y, Z) and use the Pivot to Ground helper to easily snap a character's feet to the ground level.
  • Skeletal Posture Adjustments: Straighten or adjust character postures (e.g., matching A-poses to T-poses) using bind-pose angle sliders for Arm Spread, Arm Splay, Shoulder Raise, Leg Spread, Hips Tilt, and Spine Straightening.
  • Skeleton Tree & Health Report: View the hierarchy, humanoid/quadruped body plan, coverage, duplicate and unresolved roles, and confidence/reason for every canonical mapping. Every role can be reassigned to an exact node without renaming the source asset.

💀 Auto-Rig (skeleton generation for skinless meshes)

If you import a mesh-only GLB (no skeleton/skin), Import & Rig → Skeleton offers Generate Skeleton (Auto-Rig):

  1. Choose exactly which meshes form the deforming body. Automatic selection excludes likely floors, props and accessories; manual selection is available for ambiguous assets. The server then analyzes the selected vertex cloud — not just the bounding box — and selects a humanoid or quadruped body plan.
  2. For humanoids it proposes Mixamo-named joint positions: it detects the crotch (where the body splits into legs), shoulder height, hand positions (works for both T-pose and A-pose meshes), per-leg offsets, and follows hunched spines. For meshes in non-standard poses (crouching, sitting, action poses) a pose-independent topology pass kicks in automatically: the mesh is voxelized, the interior is filled (works on non-watertight meshes), and the five extremities (head, hands, feet) are found on the geodesic graph and classified by body topology — legs merge far from the head, arms merge near it. Joints are placed along the detected limb centerlines.
  3. The builder enters a dedicated rig viewport mode: the character is isolated, draggable yellow joint markers appear, with Front/Side/Top camera presets (keys 1/2/3) and optional symmetric editing (left ↔ right mirroring).
  4. Apply Rig builds the skeleton, computes bounded topology-aware skin weights server-side, preserves unselected rigid meshes and morph deltas, and re-merges the animation set automatically. The final quality report records selection, coverage, warnings and compatibility.

Already-rigged characters get Re-Rig / Adjust Skeleton instead: markers seed from the current bind pose, and applying moves the existing joints while preserving the hierarchy, extra bones (fingers/twist) and the original artist skin weights.

🎭 Custom Actions & Animations

In Animate → Custom Animations, you can extend the controller by registering completely new character actions (e.g., TAUNT, DANCE, WAVE):

  • Map a custom action name to any animation group in the library.
  • Assign key triggers directly to the custom action.
  • In the exported snippet, these actions are configured and bound automatically.
  • You can trigger custom actions through the complete controller state machine using charCtrl.triggerAction('CUSTOM_ACTION_NAME').

🎯 Animation Events (gameplay frame markers)

In Animate → Animation Events you can attach typed markers (footstep, hit, cast, sound, particle, camera, custom) to any mapped animation at a specific frame:

  • Markers fire live in the builder viewport (toast + console) while previewing or playing animations — including during crossfades and inside the Locomotion blend tree (footsteps fire on Walk/Sprint loops).
  • Markers survive character swaps: they are kept as long as the slot maps to the same clip, and a Clear All button removes every marker at once.
  • The Export tab emits them as charCtrl.animationEvents. Consume them in your game:
charCtrl.animationEvents={Punch: [{type: 'hit',frame: 12,label: 'impact'}],Walk_Loop: [{type: 'footstep',frame: 5},{type: 'footstep',frame: 19}],};charCtrl.onAnimationEvent=(evt,animName)=>{if(evt.type==='hit')applyDamage();if(evt.type==='footstep')playFootstepSound();};// or listen globally:window.addEventListener('charanimevent',(e)=>console.log(e.detail));

🧪 Controller Presets & Test Lab

The Controller tab includes four one-click controller presets (Balanced Adventure, Action Combat, Arcade Platformer, Cinematic Walkthrough) and a Controller Test Lab: scenario camera chips (Studio / Motion / Air / Close Cam), action buttons (Idle, Walk, Sprint, Jump, Roll, Crouch — locomotion buttons drive the real blend tree, exactly like in-game), and a live metrics panel (state, speed, grounded, active animation, camera framing).

↺ Parameter Reset Buttons

Beside every slider, toggle, or control mapping under Controller, Physics and Input Mapping, there is an reset button. Clicking it restores that single parameter without clearing the rest of the setup.

🔄 Retargeting & Animation Merging (merge_api.mjs)

The Visual Builder uses the canonical server-side module merge_api.mjs (via server.mjs) to analyze, retarget and combine characters and animations deterministically.

When using the builder, you can import assets in different ways:

  • Separate Import: Load a character in Import & Rig, then one or more external animation GLBs/FBXs in Animate.
  • Embedded Animations: A character's own animation groups are available immediately after import; no duplicate upload is required.

📥 Exporting & Downloading Options

Validate & Export provides four distinct ways to output your configuration and assets for production:

🔀 Integration Modes (Pre-merged GLB vs. Runtime Retargeting)

When exporting your setup, you can choose between two integration architectures depending on your project needs:

  • Pre-merged GLB (Baked Merge):
    • How it works: Merges character meshes and animation channels into a single character_animated.glb file.
    • Best for: Single character games, simple setups, or engines where loading multiple separate files is not desired.
    • Drawback: Duplicate data. If you have 10 characters sharing the same locomotion set, you will be downloading those animation frames 10 times.
  • Runtime Retargeting (Client-Side Dynamic Retargeting):
    • How it works: Keeps character meshes (character.glb) and animation libraries (animations.glb) separate. setupCharacter requests the same canonical server merge used by the builder and falls back to client retargeting when the service is unavailable. Manual boneMapOverrides travel with the exported setup.
    • Best for: Multi-character games, RPGs, or modular projects. Reuses one shared animation file across dozens of characters, drastically reducing download size and memory footprint.
    • Note: Requires Babylon.js 9+. Cross-convention rigs are most accurate with the local merge service available.

1. 📋 Export Code Snippet (Preview & Copy)

This provides a complete, custom loadCharacter helper function matching your settings. Copy and paste it directly into your app.js entry file to replace the default loader. It automatically bakes in:

  • Mesh Transform Scaling (capsuleScale).
  • Custom Key Bindings (keys mappings).
  • Physics Config Parameters (config defaults).
  • Mapped Animations & Custom Actions (configure callback).
  • Animation Events (animationEvents markers).

2. 💾 Saving & Restoring Builder Config (builder-config.json)

Allows you to save/load your visual builder configuration presets:

  • Download builder-config.json: Saves schema-versioned transforms, exact bone assignments, key bindings, physics settings, animation mappings, custom actions and events. It intentionally does not embed model or animation binaries (includesAssets: false).
  • Import builder-config.json: Restore your saved configuration at any time to resume working in the builder without losing your adjustments.

3. 📦 Exporting the Character as GLB (with animations)

Click Download character_animated.glb to download a single, self-contained GLB file that merges your character mesh with the active animations retargeted and merged directly into the skeletal structures on the server. Ready for drag-and-drop into your assets folder.

⚡ 4. Downloading Baked Controller (custom-character-controller.js)

Generates a tailored standalone character-controller.js file with your settings pre-baked:

  • Replaces the default configurations (DEFAULT_CHAR_CONFIG) inside the script with your custom physics, keys, and touch layouts.
  • Keeps baked defaults authoritative and does not inject or mutate localStorage; persistence remains an explicit application choice.
  • Bakes all standard and custom animation remappings, frame ranges, and event markers directly into the controller's setup hooks, acting as a complete drop-in replacement with zero extra code required in your loader scripts.
<!-- Use the downloaded file in place of the original: --><scriptsrc="js/character-controller.js"></script><!-- or, if using the builder export: --><scriptsrc="js/custom-character-controller.js"></script>

📚 Credits & License

  • Rig: Customized Mixamo skeletal rig.
  • Animations: Universal Animation Library by Quaternius.
  • License: Licensed under the MIT License - see LICENSE for details. Keep the copyright notice and attribute the authorship of the Character Controller to Diego Ramirez in all copies.

About

No description, website, or topics provided.

Resources

Stars

8 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); })(); GitHub - crazyramirez/BJS_Character_Controller_V2 · GitHub
Skip to content

Repository files navigation

🎮 3D Character Animation Controller V2 for Babylon.js

An advanced third-person character locomotion and physics framework built with Babylon.js. This framework provides a fluid, powerful, and easy-to-use Character Controller with integrated physics, animations, and high-end visual features.

🎮 Live Demo: https://viseni.com/demos/bjs_character_controller_v2/

BJS Character Controller V2 Screenshot

☕ If this controller saves you time, consider supporting its development!

Buy Me A Coffee


🚀 Key Features

  • Dual-Movement Modes (Physics vs Kinematic): Toggle dynamically between Havok Physics (dynamic simulation with body bodies) and standard Kinematic Collisions (ellipsoid-based movement) directly from the HUD.
  • Locomotion Blend Tree: Smoothly blends weight and speed between Idle, Walk, and Sprint.
  • Dual-State Toggle Coexistence: Crouch and Sprint operate as persistent toggles and can co-exist (allowing crouch-running).
  • Dynamic Zoom & Camera Follow: Smooth camera tracking with automated user-zoom sync (mouse wheel, trackpad, pinch) and double-tap recentering.
  • Dynamic FOV & Camera Shake: Camera Field of View expands with speed. Rotational camera shake is triggered on landing impacts relative to fall height.
  • Camera Follow Lock (Direct Steering): Locks the camera directly behind the character for tank-style direct controls.
  • Visual Enhancements: Procedural dust/smoke trails at the feet, procedural leaning/banking on turns, slope-incline alignment, and squash & stretch scaling.
  • Collision height adjustments & Ceiling protection: Shrinks the capsule automatically when crouching/rolling, prevents standing up or rolling under low ceilings, and expands width when sprinting to prevent wall clipping.
  • Ledge & Stairs Snapping: Keeps the character grounded on sloped surfaces and stairs to prevent airborne jitter.
  • Slope-Aligned Kinematic Traversal: Kinematic collisions mode projects movement directly onto the ground normal to ensure butter-smooth ascent/descent on ramps and slopes.
  • Smart Snap-Down Controls: Dynamically disables downward snap forces when ascending stairs or steep slopes to eliminate physics/collision jitter.
  • Implicit Self-Collision Prevention: Prevents parent-capsule jitter by automatically disabling collision checks (checkCollisions = false) on imported character visual meshes.
  • Mobile Touch Support: Responsive virtual joystick and customizable glassmorphism action buttons.
  • Gamepad Support: Analog movement with configurable dead zone and edge-triggered jump, roll, sprint, crouch and action buttons.
  • Production Rigging Pipeline: Human and quadruped analysis, body-mesh selection, editable canonical bone assignment, topology-aware auto-rigging, quality diagnostics and deterministic animation retargeting.
  • Air Dash (Mid-Air Roll): Perform a responsive dodge roll in mid-air with a horizontal speed boost and a 55% jump-power vertical hop (available if Double Jump is enabled, works even after double jumping).
  • Action Interrupt Roll: Pressing Roll immediately interrupts active attack combos or spell casts for instant responsiveness.
  • Roll Cooldown & HUD Feedback: A 1.1s cooldown prevents roll spamming, displaying a "DODGE COOLDOWN" HUD warning when pressed too early.
  • Toggleable Action HUD Texts: Toggle on-screen action text alerts (like "AIR DASH", "JAB", "CROSS!") directly from the System & UI settings drawer.

⚖️ Physics vs. Kinematic Modes

character-controller.js is a unified single-file engine that runs in two distinct physics regimes. Both modes live in the same class — a single usePhysics flag switches the internal code paths at initialization time.

  • Havok Physics (Default): Leverages the WASM-powered Havok Physics engine. The character capsule is created as a dynamic PhysicsBody with defined mass and inertia properties, interacting naturally with other dynamic aggregates (like boxes, cylinders, and triggers).
  • Kinematic Collisions: Runs entirely within Babylon's native collision engine using kinematic ellipsoids (moveWithCollisions). Havok initialization is skipped entirely, providing maximum performance and deterministic locomotion.

Explicit configuration (default behaviour)

Runtime options are authoritative. initPhysics(scene) tries Havok and falls back to kinematic collisions without reading or modifying browser storage. Pass usePhysics: false when kinematic mode is required.

Overriding the mode

Preference persistence is deliberately opt-in. The bundled HUD examples use persistPreferences: true; embedded applications remain deterministic by default:

constusePhysics=awaitinitPhysics(scene,{usePhysics: true,persistPreferences: true});

The same policy is available on the controller constructor:

constcharCtrl=newCharCtrl(playerCapsule,charRoot,camera,animCtrl,scene,{usePhysics: true,// or falsepersistPreferences: false,config: {SPEED_MULTIPLIER: 1.5// Multiplies walking, running and jogging speeds}});

⚙️ Configuration Parameters

The config object in the constructor accepts a wide range of physics, camera, and gameplay properties to fine-tune character behavior:

ParameterDefaultTypeDescription
GRAV22numberGravity force pulling the character down
JUMP_PWR9.5numberVertical takeoff impulse force for jumping
SPD_WALK2.5numberMaximum physical walking speed
SPD_JOG3.0numberMaximum physical jogging speed (blend speed threshold)
SPD_SPRINT5.0numberMaximum physical sprinting speed
SPD_CROUCH2.0numberMaximum physical crouching walk speed
SPD_CROUCH_RUN3.2numberMaximum physical crouching run speed
ACCEL14numberMovement acceleration rate (speed-up responsiveness)
DECEL16numberMovement deceleration rate (braking/stopping responsiveness)
ROT_SPD40numberCharacter yaw rotation speed responsiveness
AIR_CONTROLfalsebooleanSteering control in mid-air (true = full control, false = no control)
DYNAMIC_FOVtruebooleanDynamically adjust camera Field of View based on speed
DYNAMIC_FOV_MAX0.10numberMaximum camera FOV expansion amount at full sprint speed
CAM_FOLLOW_LOCKtruebooleanIf true, the camera is locked behind the character's facing direction
CAM_FOLLOW_PITCH1.047numberCamera follow lock pitch (beta angle in radians, approx 60 degrees)
CAM_FOLLOW_DIST8.0numberCamera follow lock distance (radius in meters)
CAM_LOCK_PITCHfalsebooleanIf true, drag input only rotates camera horizontally (locks pitch axis)
JOYSTICK_LOCK_XfalsebooleanIf true, joystick input is locked to vertical axis only (no strafing)
DOUBLE_JUMP_ENABLEDtruebooleanIf true, the character can perform a double jump in mid-air
SPEED_MULTIPLIER1.0numberSpeed multiplier for walking, running, and jogging
PLAY_PARTICLEStruebooleanPlay procedural dust/smoke particles under the character's feet

🔄 Dynamic Animation Remapping

You can dynamically change any animation on the character controller or adjust keyframe ranges at runtime using the AnimCtrl instance (accessed via charCtrl.anim):

1. Reassigning Animations (Setters)

Pass a new Babylon AnimationGroup to dynamically swap any of the pre-mapped animations:

// Remap basic locomotioncharCtrl.anim.setWalkAnim(newWalkAnimGroup);charCtrl.anim.setRunAnim(newRunAnimGroup);charCtrl.anim.setIdleAnim(newIdleAnimGroup);// Remap crouch statescharCtrl.anim.setCrouchIdleAnim(newCrouchIdle);charCtrl.anim.setCrouchFwdAnim(newCrouchWalk);// Remap jumps and actionscharCtrl.anim.setJumpStartAnim(newJumpStart);charCtrl.anim.setJumpLoopAnim(newJumpLoop);charCtrl.anim.setJumpLandAnim(newJumpLand);charCtrl.anim.setRollAnim(newRoll);charCtrl.anim.setPunchJabAnim(newPunchJab);charCtrl.anim.setPunchCrossAnim(newPunchCross);charCtrl.anim.setSpellEnterAnim(newSpellEnter);charCtrl.anim.setSpellShootAnim(newSpellShoot);charCtrl.anim.setSpellExitAnim(newSpellExit);charCtrl.anim.setInteractAnim(newInteract);// Remap any custom animation keycharCtrl.anim.setAnimation('Custom_State_Name',myAnimGroup);

2. Modifying Playback Keyframe Ranges

Change the start/end frames of an animation without replacing the group:

// setAnimationRanges(animKey, startFrame, endFrame)charCtrl.anim.setAnimationRanges('Walk_Loop',10,45);

🕹️ Controls Layout

Keyboard (PC):

  • W, A, S, D / Arrow Keys: Movement.
  • Shift: Sprint (Toggle).
  • Ctrl: Crouch (Toggle).
  • Space: Jump / Double Jump.
  • R: Dodge roll / Air Dash:
    • Action Interrupt: Instantly cancels active attack combos or spell casts.
    • Roll Cooldown: 1.1s cooldown between rolls (triggers a "DODGE COOLDOWN" HUD alert).
    • Air Dash: If Double Jump is enabled in settings, performs a mid-air roll with a horizontal boost and a 55% jump-power vertical hop (usable even after double jumping).
  • Q: Punch combo.
  • E: Spell casting.
  • F: Interaction.
  • Mouse Drag: Orbit camera / Double-click to recenter.

Mobile Touch:

  • Left Hand: Floating Analog Joystick.
  • Right Hand (Buttons): SPELL, ACT, CROUCH, ROLL, SPRINT, JUMP.
  • Canvas Double-Tap: Recenter camera.

🛠️ Implementation Quickstart

The js/ directory is organized into subfolders by role:

  • js/character-controller.js — Unified core engine. Handles Havok Physics and Kinematic modes, locomotion state machines, and animation blending. Exports initPhysics and setupCharacter helpers.
  • js/ui/custom-hud.js — Tactile settings overlay (Camera Lock, Physics toggle, Dynamic FOV, Hide Cursor, Double Jump, Air Control, sliders). Optional.
  • js/ui/custom-pointer.js — Spring-damper trailing cursor ring. Optional.
  • js/examples/ — Ready-to-run setup templates (app.js, app-minimal.js, app-complex.js).
  • js/core/builder.js — Powers builder.html, the visual configuration tool (see below).

⚡ High-Level Setup (Recommended)

You can initialize physics and load the character in just a few lines of code using the shared helper functions: initPhysics and setupCharacter (wrapped in a clean loadCharacter helper function across the app templates). This helper supports configuring model paths, spawn locations, bounding ellipsoids, controls, and animations:

// 1. Define character initialization helperasyncfunctionloadCharacter(scene,shadow,camera,usePhysics){returnsetupCharacter(scene,camera,usePhysics,{
shadow,// Optional: shadow generator to add character meshes topersistPreferences: true,// Optional: allow HUD/browser preference persistenceassetsPath: 'assets/',// Optional: path to GLB assets folder (defaults to 'assets/')filename: 'character_animated.glb',// Optional: GLB file name (defaults to 'character_animated.glb')spawnPosition: newBABYLON.Vector3(0,2,0),// Optional: starting position overrideellipsoid: newBABYLON.Vector3(0.35,0.96,0.35),// Optional: collision ellipsoid overridekeys: {JUMP: ['KeyK']},// Optional: remap keyboard controls directlyconfig: {JUMP_PWR: 12},// Optional: override physical and camera parametersconfigure: ({ animCtrl, filteredGroups })=>{// Optional: callback to remap animations or customize keyframe rangesanimCtrl.setWalkAnim(filteredGroups[15]);}});}// 2. Initialize physics (Havok or Kinematic fallback)constusePhysics=awaitinitPhysics(scene,{persistPreferences: true});// 3. Load the character using the helperconst{ playerCapsule, animCtrl, charCtrl }=awaitloadCharacter(scene,shadow,camera,usePhysics);// 4. Hook up HUD setting toggles dynamically via custom-hud.jsif(typeofbindHUDControls==='function'){bindHUDControls(charCtrl,camera,usePhysics);}

We have provided three setup examples to guide your implementation:

  • js/examples/app-minimal.js: A bare-minimum integration template/guide to quickly see how to set up the Babylon.js engine, scene, capsule collider, parent the mesh, and initialize the controllers.
  • js/examples/app-complex.js: A full-featured setup designed to demonstrate how the character controller functions with a highly complex 3D scenery model (assets/backyard_demo.glb) containing many intricate, complex collisions and polygon-heavy geometry.
  • js/examples/app.js: A fully featured production loading example including advanced lighting, shadows, skyboxes, procedural environment shapes (boxes, ramp, stairs), post-processing, and HUD settings synchronization.

🔧 Visual Builder (builder.html)

BJS Character Controller V2 Builder

builder is an interactive GUI tool for visually configuring and exporting a custom character controller — no code editing required. You can use it as a static page, or run it with the local NodeJS development server to enable full backend-powered retargeting and GLB merges.

🌐 Running with NodeJS / npm (Recommended)

To run the local server which powers advanced skeletal retargeting, GLB animation merges, and asset optimizations via the local backend API:

  1. Install dependencies:

    npm install
  2. Start the local server:

    npm start
  3. Open the builder: Navigate to http://localhost:3000/builder in your browser.

  4. Run the complete verification suite before publishing changes:

    npm run check
    npm audit

Tabs

TabWhat it does
Import & RigImport GLB/FBX, choose the deforming body meshes, adjust transforms and bind pose, inspect skeleton health, edit canonical bone assignments and generate or rebuild rigs
AnimateAuto-match animation names, preview clips, define gameplay frame markers and add custom triggered actions
Input MappingRemap keyboard/gameplay actions and restore individual defaults
ControllerApply presets, use the live test lab, and tune movement, camera and feel
PhysicsConfigure collision, gravity, jumping, grounding, slopes and Havok/kinematic behaviour
Validate & ExportReview diagnostics and generated code, save/restore schema-validated configuration, or export a merged GLB and standalone controller

Builder preferences auto-save locally for editing convenience. Exported runtime controllers do not inherit that storage unless the application explicitly enables persistPreferences.

💀 FBX Direct Import & Bind-Pose Posture Tuning

When running the NodeJS backend, the Import & Rig tab offers advanced rigging, conversion, and alignment utilities:

  • Direct FBX Support: Drag-and-drop .fbx character models and animation files. The server auto-converts them to .glb under-the-hood (using fbx_api.mjs), fixing materials and flattening the RootNode transformation to avoid rotation/scale offset issues.
  • Scale & Pivot Offsets: Fine-tune character sizing using uniform scaling or independent X, Y, and Z scaling. Adjust the pivot offset (X, Y, Z) and use the Pivot to Ground helper to easily snap a character's feet to the ground level.
  • Skeletal Posture Adjustments: Straighten or adjust character postures (e.g., matching A-poses to T-poses) using bind-pose angle sliders for Arm Spread, Arm Splay, Shoulder Raise, Leg Spread, Hips Tilt, and Spine Straightening.
  • Skeleton Tree & Health Report: View the hierarchy, humanoid/quadruped body plan, coverage, duplicate and unresolved roles, and confidence/reason for every canonical mapping. Every role can be reassigned to an exact node without renaming the source asset.

💀 Auto-Rig (skeleton generation for skinless meshes)

If you import a mesh-only GLB (no skeleton/skin), Import & Rig → Skeleton offers Generate Skeleton (Auto-Rig):

  1. Choose exactly which meshes form the deforming body. Automatic selection excludes likely floors, props and accessories; manual selection is available for ambiguous assets. The server then analyzes the selected vertex cloud — not just the bounding box — and selects a humanoid or quadruped body plan.
  2. For humanoids it proposes Mixamo-named joint positions: it detects the crotch (where the body splits into legs), shoulder height, hand positions (works for both T-pose and A-pose meshes), per-leg offsets, and follows hunched spines. For meshes in non-standard poses (crouching, sitting, action poses) a pose-independent topology pass kicks in automatically: the mesh is voxelized, the interior is filled (works on non-watertight meshes), and the five extremities (head, hands, feet) are found on the geodesic graph and classified by body topology — legs merge far from the head, arms merge near it. Joints are placed along the detected limb centerlines.
  3. The builder enters a dedicated rig viewport mode: the character is isolated, draggable yellow joint markers appear, with Front/Side/Top camera presets (keys 1/2/3) and optional symmetric editing (left ↔ right mirroring).
  4. Apply Rig builds the skeleton, computes bounded topology-aware skin weights server-side, preserves unselected rigid meshes and morph deltas, and re-merges the animation set automatically. The final quality report records selection, coverage, warnings and compatibility.

Already-rigged characters get Re-Rig / Adjust Skeleton instead: markers seed from the current bind pose, and applying moves the existing joints while preserving the hierarchy, extra bones (fingers/twist) and the original artist skin weights.

🎭 Custom Actions & Animations

In Animate → Custom Animations, you can extend the controller by registering completely new character actions (e.g., TAUNT, DANCE, WAVE):

  • Map a custom action name to any animation group in the library.
  • Assign key triggers directly to the custom action.
  • In the exported snippet, these actions are configured and bound automatically.
  • You can trigger custom actions through the complete controller state machine using charCtrl.triggerAction('CUSTOM_ACTION_NAME').

🎯 Animation Events (gameplay frame markers)

In Animate → Animation Events you can attach typed markers (footstep, hit, cast, sound, particle, camera, custom) to any mapped animation at a specific frame:

  • Markers fire live in the builder viewport (toast + console) while previewing or playing animations — including during crossfades and inside the Locomotion blend tree (footsteps fire on Walk/Sprint loops).
  • Markers survive character swaps: they are kept as long as the slot maps to the same clip, and a Clear All button removes every marker at once.
  • The Export tab emits them as charCtrl.animationEvents. Consume them in your game:
charCtrl.animationEvents={Punch: [{type: 'hit',frame: 12,label: 'impact'}],Walk_Loop: [{type: 'footstep',frame: 5},{type: 'footstep',frame: 19}],};charCtrl.onAnimationEvent=(evt,animName)=>{if(evt.type==='hit')applyDamage();if(evt.type==='footstep')playFootstepSound();};// or listen globally:window.addEventListener('charanimevent',(e)=>console.log(e.detail));

🧪 Controller Presets & Test Lab

The Controller tab includes four one-click controller presets (Balanced Adventure, Action Combat, Arcade Platformer, Cinematic Walkthrough) and a Controller Test Lab: scenario camera chips (Studio / Motion / Air / Close Cam), action buttons (Idle, Walk, Sprint, Jump, Roll, Crouch — locomotion buttons drive the real blend tree, exactly like in-game), and a live metrics panel (state, speed, grounded, active animation, camera framing).

↺ Parameter Reset Buttons

Beside every slider, toggle, or control mapping under Controller, Physics and Input Mapping, there is an reset button. Clicking it restores that single parameter without clearing the rest of the setup.

🔄 Retargeting & Animation Merging (merge_api.mjs)

The Visual Builder uses the canonical server-side module merge_api.mjs (via server.mjs) to analyze, retarget and combine characters and animations deterministically.

When using the builder, you can import assets in different ways:

  • Separate Import: Load a character in Import & Rig, then one or more external animation GLBs/FBXs in Animate.
  • Embedded Animations: A character's own animation groups are available immediately after import; no duplicate upload is required.

📥 Exporting & Downloading Options

Validate & Export provides four distinct ways to output your configuration and assets for production:

🔀 Integration Modes (Pre-merged GLB vs. Runtime Retargeting)

When exporting your setup, you can choose between two integration architectures depending on your project needs:

  • Pre-merged GLB (Baked Merge):
    • How it works: Merges character meshes and animation channels into a single character_animated.glb file.
    • Best for: Single character games, simple setups, or engines where loading multiple separate files is not desired.
    • Drawback: Duplicate data. If you have 10 characters sharing the same locomotion set, you will be downloading those animation frames 10 times.
  • Runtime Retargeting (Client-Side Dynamic Retargeting):
    • How it works: Keeps character meshes (character.glb) and animation libraries (animations.glb) separate. setupCharacter requests the same canonical server merge used by the builder and falls back to client retargeting when the service is unavailable. Manual boneMapOverrides travel with the exported setup.
    • Best for: Multi-character games, RPGs, or modular projects. Reuses one shared animation file across dozens of characters, drastically reducing download size and memory footprint.
    • Note: Requires Babylon.js 9+. Cross-convention rigs are most accurate with the local merge service available.

1. 📋 Export Code Snippet (Preview & Copy)

This provides a complete, custom loadCharacter helper function matching your settings. Copy and paste it directly into your app.js entry file to replace the default loader. It automatically bakes in:

  • Mesh Transform Scaling (capsuleScale).
  • Custom Key Bindings (keys mappings).
  • Physics Config Parameters (config defaults).
  • Mapped Animations & Custom Actions (configure callback).
  • Animation Events (animationEvents markers).

2. 💾 Saving & Restoring Builder Config (builder-config.json)

Allows you to save/load your visual builder configuration presets:

  • Download builder-config.json: Saves schema-versioned transforms, exact bone assignments, key bindings, physics settings, animation mappings, custom actions and events. It intentionally does not embed model or animation binaries (includesAssets: false).
  • Import builder-config.json: Restore your saved configuration at any time to resume working in the builder without losing your adjustments.

3. 📦 Exporting the Character as GLB (with animations)

Click Download character_animated.glb to download a single, self-contained GLB file that merges your character mesh with the active animations retargeted and merged directly into the skeletal structures on the server. Ready for drag-and-drop into your assets folder.

⚡ 4. Downloading Baked Controller (custom-character-controller.js)

Generates a tailored standalone character-controller.js file with your settings pre-baked:

  • Replaces the default configurations (DEFAULT_CHAR_CONFIG) inside the script with your custom physics, keys, and touch layouts.
  • Keeps baked defaults authoritative and does not inject or mutate localStorage; persistence remains an explicit application choice.
  • Bakes all standard and custom animation remappings, frame ranges, and event markers directly into the controller's setup hooks, acting as a complete drop-in replacement with zero extra code required in your loader scripts.
<!-- Use the downloaded file in place of the original: --><scriptsrc="js/character-controller.js"></script><!-- or, if using the builder export: --><scriptsrc="js/custom-character-controller.js"></script>

📚 Credits & License

  • Rig: Customized Mixamo skeletal rig.
  • Animations: Universal Animation Library by Quaternius.
  • License: Licensed under the MIT License - see LICENSE for details. Keep the copyright notice and attribute the authorship of the Character Controller to Diego Ramirez in all copies.

About

No description, website, or topics provided.

Resources

Stars

8 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages