1 / 46
Dec 2024

post by Cedric on Dec 9, 2024

I’m happy to annouce the availability of the Physics Character Controller.
It provides a simple api to control and extent a character in a physicalized world.

A documented PG is available here : https://playground.babylonjs.com/#FMQX86#1

Documentation page is here : Babylon.js docs

As usual, this feature is driven by Babylon.js community. Feel free to discuss, comment and contribute on this new feature.

Have fun !

read 5 min

post by ertugrulcetin on Dec 9, 2024

post by Dad72 on Dec 9, 2024

post by sebavan on Dec 10, 2024

post by roland on Dec 10, 2024

post by regna on Dec 10, 2024

post by CrashMaster on Dec 10, 2024

post by j-te on Dec 10, 2024

Great work! Just looking at the controller part: does this want to have it’s own class/separate it because I’m understanding this code actually as a “PhysicsCharacter”. As it could be controlled by a Player or AI right? Plus I couldn’t see any controller related code (I’m using my phone). The Controller part I usually define as the interface for the Player or AI.

post by Cedric on Dec 10, 2024

This new class controls and constraints a physics body. Input can be human or AI. Human inputs are handled in the PG.

post by brightMoon on Dec 11, 2024

post by jelster on Dec 12, 2024

post by dnrn on Dec 12, 2024

14 days later

post by AmKreta on Dec 27, 2024

Hi, could you please modify the example to include rotation functionality? Specifically, I’d like the player to rotate when pressing A/D keys, always face the forward direction, and also rotate while jumping. Thank you!

10 days later

post by GekkePop on Jan 6, 2025

Great addition, really appreciate it. Will use this as the base for my own character controller!

3 months later

post by MackeyK24 on Apr 6, 2025

Hey @Cedric … Can you please explain a bit about how you use the character controller. It seems a bit different than how you would use btKinematicCharacterController for example.

getDesiredVelocity … That seems to be doing alot of stuff. Where as before you might just do a inputVector.normalized*speed and the that was your movement velocity you used. I think moveWithCollisions worked that way as well.

I am trying to replace my RigidBody based character controller i wrote that worked like the Ammo btKinematicCharacterController with the new BABYLON.PhysicsCharacterController underneath my ThirdPersonCharacterController, but I am having trouble understanding all this, and how i can use this in place of my Unity Style Character controller I was using with Ammo.js

// State handling // depending on character state and support, set the new state var getNextState = function(supportInfo) { if (state == "IN_AIR") { if (supportInfo.supportedState == BABYLON.CharacterSupportedState.SUPPORTED) { return "ON_GROUND"; } return "IN_AIR"; } else if (state == "ON_GROUND") { if (supportInfo.supportedState != BABYLON.CharacterSupportedState.SUPPORTED) { return "IN_AIR"; } if (wantJump) { return "START_JUMP"; } return "ON_GROUND"; } else if (state == "START_JUMP") { return "IN_AIR"; } } // From aiming direction and state, compute a desired velocity // That velocity depends on current state (in air, on ground, jumping, ...) and surface properties var getDesiredVelocity = function(deltaTime, supportInfo, characterOrientation, currentVelocity) { let nextState = getNextState(supportInfo); if (nextState != state) { state = nextState; } let upWorld = characterGravity.normalizeToNew(); upWorld.scaleInPlace(-1.0); let forwardWorld = forwardLocalSpace.applyRotationQuaternion(characterOrientation); if (state == "IN_AIR") { let desiredVelocity = inputDirection.scale(inAirSpeed).applyRotationQuaternion(characterOrientation); let outputVelocity = characterController.calculateMovement(deltaTime, forwardWorld, upWorld, currentVelocity, BABYLON.Vector3.ZeroReadOnly, desiredVelocity, upWorld); // Restore to original vertical component outputVelocity.addInPlace(upWorld.scale(-outputVelocity.dot(upWorld))); outputVelocity.addInPlace(upWorld.scale(currentVelocity.dot(upWorld))); // Add gravity outputVelocity.addInPlace(characterGravity.scale(deltaTime)); return outputVelocity; } else if (state == "ON_GROUND") { // Move character relative to the surface we're standing on // Correct input velocity to apply instantly any changes in the velocity of the standing surface and this way // avoid artifacts caused by filtering of the output velocity when standing on moving objects. let desiredVelocity = inputDirection.scale(onGroundSpeed).applyRotationQuaternion(characterOrientation); let outputVelocity = characterController.calculateMovement(deltaTime, forwardWorld, supportInfo.averageSurfaceNormal, currentVelocity, supportInfo.averageSurfaceVelocity, desiredVelocity, upWorld); // Horizontal projection { outputVelocity.subtractInPlace(supportInfo.averageSurfaceVelocity); let inv1k = 1e-3; if (outputVelocity.dot(upWorld) > inv1k) { let velLen = outputVelocity.length(); outputVelocity.normalizeFromLength(velLen); // Get the desired length in the horizontal direction let horizLen = velLen / supportInfo.averageSurfaceNormal.dot(upWorld); // Re project the velocity onto the horizontal plane let c = supportInfo.averageSurfaceNormal.cross(outputVelocity); outputVelocity = c.cross(upWorld); outputVelocity.scaleInPlace(horizLen); } outputVelocity.addInPlace(supportInfo.averageSurfaceVelocity); return outputVelocity; } } else if (state == "START_JUMP") { let u = Math.sqrt(2 * characterGravity.length() * jumpHeight); let curRelVel = currentVelocity.dot(upWorld); return currentVelocity.add(upWorld.scale(u - curRelVel)); } return Vector3.Zero(); } // Display tick update: compute new camera position/target, update the capsule for the character display scene.onBeforeRenderObservable.add((scene) => { displayCapsule.position.copyFrom(characterController.getPosition()); // camera following var cameraDirection = camera.getDirection(new BABYLON.Vector3(0,0,1)); cameraDirection.y = 0; cameraDirection.normalize(); camera.setTarget(BABYLON.Vector3.Lerp(camera.getTarget(), displayCapsule.position, 0.1)); var dist = BABYLON.Vector3.Distance(camera.position, displayCapsule.position); const amount = (Math.min(dist - 6, 0) + Math.max(dist - 9, 0)) * 0.04; cameraDirection.scaleAndAddToRef(amount, camera.position); camera.position.y += (displayCapsule.position.y + 2 - camera.position.y) * 0.04; });

post by Cedric on Apr 7, 2025

The new character controller gives a lot of control but user has to take care of state machine.
To replace the previous Ammo, I would not care about the state machine and just call calculateMovement with appropriate parameters.

post by MackeyK24 on Apr 8, 2025

Yo @Cedric

How do we set or change the current position of the physics based character.

In other character controller system, we had the teleport or someway of overriding and setting the position of the physics based character. I dont see an easy way to set the position beside the initial character position in the constructor.

Also… There is alot of easing and the character still keeps moving after you stop. Can we control how much easing so it does not keep moving so much? Its hard to sync the character animations with all that extra easing in the movement.

post by Cedric on Apr 8, 2025

post by Cedric on Apr 8, 2025

post by MackeyK24 on Apr 9, 2025

Hey @Cedric In the following playground: https://playground.babylonjs.com/#FMQX86#1

the character position is set to ground position with 0.3 height. That is the floor height. when you do anything that makes that character rise higher, like traversing stairs or even just jumping, it never goes back (gravity) to the floor height of 0.3. like the contact or internal raycast or what ever is not fulling grounding the player character. Sometimes you jump and land and the character is at position.y = 0.33 or 0.32 or 0.28… but almost never back to the ground position of 0.3.

The big problem for me, is when I stick a player skeletal mesh in the player character and his feet are perfectly aligned on the ground at 0.3 floor height. After jumping it may seem like the character is floating when above 0.3 floor. height or even sunk into the floor when less than floor height.

Is there any anything we can set to make the collision detection with the ground more precise ?

post by MackeyK24 on Apr 9, 2025

post by Cedric on Apr 9, 2025

Assigned @Cedric on Apr 9, 2025

post by MackeyK24 on Apr 10, 2025

post by MackeyK24 on Apr 10, 2025

post by Cedric on Apr 10, 2025

post by MackeyK24 on Apr 10, 2025

post by MackeyK24 on Apr 10, 2025

post by Cedric on Apr 11, 2025

post by MackeyK24 on Apr 15, 2025

post by Cedric on Apr 15, 2025

post by MackeyK24 on Apr 15, 2025

post by Cedric on Apr 15, 2025

post by MackeyK24 on Apr 15, 2025

post by MackeyK24 on Apr 15, 2025

post by MackeyK24 on Apr 15, 2025

post by Cedric on Apr 15, 2025

8 days later

post by labris on Apr 24, 2025

2 months later

Unassigned @Cedric on Jun 24, 2025

post by tornado_jack on Jul 2, 2025

Load more posts below