@babylonjs/core
Version:
Getting started? Play directly with the Babylon.js API using our [playground](https://playground.babylonjs.com/). It also contains a lot of samples to learn how to use it.
490 lines (489 loc) • 19.5 kB
TypeScript
import { Vector3, Matrix } from "../../Maths/math.vector.js";
import { type Scene } from "../../scene.js";
import { type DeepImmutableObject } from "../../types.js";
import { PhysicsBody } from "./physicsBody.js";
import { type PhysicsShape } from "./physicsShape.js";
import { Observable } from "../../Misc/observable.js";
/**
* Shape properties for the character controller
*/
export interface CharacterShapeOptions {
/**
* optional shape used for collision detection
*/
shape?: PhysicsShape;
/**
* capsule height for the capsule shape if no shape is provided
*/
capsuleHeight?: number;
/**
* capsule radius for the capsule shape if no shape is provided
*/
capsuleRadius?: number;
}
/**
* Collision event data for the character controller
*/
export interface ICharacterControllerCollisionEvent {
/**
* The collider physics body
*/
collider: PhysicsBody;
/**
* Index of the collider in instances
*/
colliderIndex: number;
/**
* Separation force applied to the collider
*/
impulse: Vector3;
/**
* Position where the impulse is applied
*/
impulsePosition: Vector3;
}
/**
* State of the character on the surface
*/
export declare enum CharacterSupportedState {
UNSUPPORTED = 0,
SLIDING = 1,
SUPPORTED = 2
}
/**
* Surface information computed by checkSupport method
*/
export interface CharacterSurfaceInfo {
/**
* Indicates whether the surface is dynamic.
* A dynamic surface is one that can change its properties over time,
* such as moving platforms or surfaces that can be affected by external forces.
* surfaceInfo.supportedState is always CharacterSupportedState.SUPPORTED when isSurfaceDynamic is true.
*/
isSurfaceDynamic: boolean;
/**
* The supported state of the character on the surface.
*/
supportedState: CharacterSupportedState;
/**
* The average normal vector of the surface.
* This vector is perpendicular to the surface and points outwards.
*/
averageSurfaceNormal: Vector3;
/**
* The average velocity of the surface.
* This vector represents the speed and direction in which the surface is moving.
*/
averageSurfaceVelocity: Vector3;
/**
* The average angular velocity of the surface.
*/
averageAngularSurfaceVelocity: Vector3;
}
interface IContact {
/** @internal */
position: Vector3;
/** @internal */
normal: Vector3;
/** @internal */
distance: number;
/** @internal */
fraction: number;
/** @internal */
bodyB: {
body: PhysicsBody;
index: number;
};
/** @internal */
allowedPenetration: number;
}
interface ISurfaceConstraintInfo {
/** @internal */
planeNormal: Vector3;
/** @internal */
planeDistance: number;
/** @internal */
velocity: Vector3;
/** @internal */
angularVelocity: Vector3;
/** @internal */
staticFriction: number;
/** @internal */
extraUpStaticFriction: number;
/** @internal */
extraDownStaticFriction: number;
/** @internal */
dynamicFriction: number;
/** @internal */
priority: number;
}
declare enum SurfaceConstraintInteractionStatus {
OK = 0,
FAILURE_3D = 1,
FAILURE_2D = 2
}
interface ISurfaceConstraintInteraction {
/** @internal */
touched: boolean;
/** @internal */
stopped: boolean;
/** @internal */
surfaceTime: number;
/** @internal */
penaltyDistance: number;
/** @internal */
status: SurfaceConstraintInteractionStatus;
}
/** @internal */
declare class SimplexSolverOutput {
/** @internal */
position: Vector3;
/** @internal */
velocity: Vector3;
/** @internal */
deltaTime: number;
/** @internal */
planeInteractions: ISurfaceConstraintInteraction[];
}
/** @internal */
declare class SimplexSolverActivePlanes {
/** @internal */
index: number;
/** @internal */
constraint: ISurfaceConstraintInfo;
/** @internal */
interaction: ISurfaceConstraintInteraction;
/** @internal */
copyFrom(other: SimplexSolverActivePlanes): void;
}
/** @internal */
declare class SimplexSolverInfo {
/** @internal */
supportPlanes: Array<SimplexSolverActivePlanes>;
/** @internal */
numSupportPlanes: number;
/** @internal */
currentTime: number;
/** @internal */
inputConstraints: ISurfaceConstraintInfo[];
/** @internal */
outputInteractions: ISurfaceConstraintInteraction[];
/** @internal */
getOutput(constraint: ISurfaceConstraintInfo): ISurfaceConstraintInteraction;
}
/**
* Character controller using physics
*/
export declare class PhysicsCharacterController {
private _position;
private _orientation;
private _velocity;
private _lastVelocity;
private _shape;
private _body;
private _transformNode;
private _ownShape;
private _manifold;
private _stepUpSavedManifold;
private _lastDisplacement;
private _contactAngleSensitivity;
private _lastInvDeltaTime;
private _scene;
private _tmpMatrix;
private _tmpVecs;
/**
* minimum distance to make contact
* default 0.05
*/
keepDistance: number;
/**
* maximum distance to keep contact
* default 0.1
*/
keepContactTolerance: number;
/**
* maximum number of raycast per integration starp
* default 10
*/
maxCastIterations: number;
/**
* speed when recovery from penetration
* default 1.0
*/
penetrationRecoverySpeed: number;
/**
* friction with static surfaces
* default 0
*/
staticFriction: number;
/**
* friction with dynamic surfaces
* default 1
*/
dynamicFriction: number;
/**
* cosine value of slope angle that can be climbed
* computed as `Math.cos(Math.PI * (angleInDegree / 180.0));`
* default 0.5 (value for a 60deg angle)
*/
maxSlopeCosine: number;
/**
* Maximum height the character can automatically step up onto a walkable surface.
* When greater than 0 the controller enforces this as a strict cap on step climbing,
* independent of the collision shape's geometry:
*
* - Obstacles whose top is at most maxStepHeight above the character's foot are
* climbed (either rolled over naturally by the capsule, or snapped up via the
* step-up sweep when the simplex would otherwise be blocked).
* - Obstacles taller than maxStepHeight are blocked, even ones the capsule's
* rounded bottom would otherwise glide over.
*
* This is enforced by demoting any "walkable" contact that sits more than
* maxStepHeight above the foot into an extra horizontal wall constraint, so the
* step-height limit does not depend on the capsule radius. As a documented side
* effect, slopes whose contact rises above maxStepHeight (roughly when
* `capsuleRadius * (1 - cos(slopeAngle)) > maxStepHeight`) are also treated as
* walls. Pick maxStepHeight large enough to clear the slope angles you want to
* remain walkable, or rely on `maxSlopeCosine` alone (with maxStepHeight = 0)
* when the rounded-capsule riding behavior is acceptable.
*
* Step-up only triggers against STATIC and ANIMATED bodies. Dynamic bodies fall
* through to normal contact resolution and pushing behavior.
*
* Thin walls / fences with floor behind them are not considered steppable: the
* landing must be measurably higher than the starting position along `up`.
*
* The foot is computed as `position - up * footOffset`. Override `footOffset` if
* you supply a custom collision shape whose center is not at half-height.
*
* Assumes `up` is a unit vector.
*
* default 0 (disabled)
*/
maxStepHeight: number;
/**
* Distance from the body's `position` to the character's foot along `up`.
* Used by `maxStepHeight` to measure how high a contact sits above the foot.
* Defaults to half the capsule height passed at construction. Override when
* supplying a custom collision shape whose center is not at half-height.
*/
footOffset: number;
/**
* character maximum speed
* default 10
*/
maxCharacterSpeedForSolver: number;
/**
* up vector
*/
up: Vector3;
/**
* Strength when pushing other bodies
* default 1e38
*/
characterStrength: number;
/**
* Acceleration factor. A value of 1 means reaching max velocity immediately
*/
acceleration: number;
/**
* maximum acceleration in world space coordinate
*/
maxAcceleration: number;
/**
* character mass
* default 0
*/
characterMass: number;
/**
* Observable for trigger entered and trigger exited events
*/
onTriggerCollisionObservable: Observable<ICharacterControllerCollisionEvent>;
private _startCollector;
private _castCollector;
private _displacementEps;
/**
* instanciate a new characterController
* @param position Initial position
* @param characterShapeOptions character physics shape options
* @param scene Scene
*/
constructor(position: Vector3, characterShapeOptions: CharacterShapeOptions, scene: Scene);
/**
* Dispose the character controller
*/
dispose(): void;
/**
* Get shape used for collision
*/
get shape(): PhysicsShape;
/**
* Set shape used for collision
*/
set shape(value: PhysicsShape);
/**
* Character position
* @returns Character position
*/
getPosition(): Vector3;
/**
* Teleport character to a new position
* @param position new position
*/
setPosition(position: Vector3): void;
/**
* Character velocity
* @returns Character velocity vector
*/
getVelocity(): Vector3;
/**
* Set velocity vector
* @param velocity vector
*/
setVelocity(velocity: Vector3): void;
protected _validateManifold(): void;
private _getPointVelocityToRef;
protected _compareContacts(contactA: IContact, contactB: IContact): number;
protected _findContact(referenceContact: IContact, contactList: IContact[], threshold: number): number;
protected _updateManifold(startCollector: any, castCollector: any, castPath: Vector3): number;
protected _bodyPositionTracking: Map<any, any>;
protected _createSurfaceConstraint(dt: number, contact: IContact, timeTravelled: number): ISurfaceConstraintInfo;
protected _addMaxSlopePlane(maxSlopeCos: number, up: Vector3, index: number, constraints: ISurfaceConstraintInfo[], allowedPenetration: number): boolean;
/**
* Adds an extra horizontal wall constraint when a "walkable" contact sits more than
* `maxStepHeight` above the character's foot along `up`. Mirrors the structure of
* `_addMaxSlopePlane` but gates on contact height rather than slope steepness.
*
* This makes `maxStepHeight` a strict cap on step climbing independent of the
* capsule's curved bottom: without this, the rounded hemisphere produces an up-tilted
* (walkable) contact normal for any obstacle shorter than the capsule radius, and
* the simplex rides over it regardless of `maxStepHeight`.
* @param constraints constraint list being assembled for the current manifold
* @param contact source manifold contact backing `constraints[index]`
* @param index index of the constraint in `constraints` whose contact is under test
* @param allowedPenetration allowed penetration distance for this contact
* @returns true if an extra wall constraint was appended
*/
protected _addStepHeightWallPlane(constraints: ISurfaceConstraintInfo[], contact: IContact, index: number, allowedPenetration: number): boolean;
protected _resolveConstraintPenetration(constraint: ISurfaceConstraintInfo, penetrationRecoverySpeed: number): void;
protected _createConstraintsFromManifold(dt: number, timeTravelled: number): ISurfaceConstraintInfo[];
protected _simplexSolverSortInfo(info: SimplexSolverInfo): void;
protected _simplexSolverSolve1d(info: SimplexSolverInfo, sci: ISurfaceConstraintInfo, velocityIn: Vector3, velocityOut: Vector3): void;
protected _simplexSolverSolveTest1d(sci: ISurfaceConstraintInfo, velocityIn: Vector3): boolean;
protected _simplexSolverSolve2d(info: SimplexSolverInfo, maxSurfaceVelocity: Vector3, sci0: ISurfaceConstraintInfo, sci1: ISurfaceConstraintInfo, velocityIn: Vector3, velocityOut: Vector3): void;
protected _simplexSolverSolve3d(info: SimplexSolverInfo, maxSurfaceVelocity: Vector3, sci0: ISurfaceConstraintInfo, sci1: ISurfaceConstraintInfo, sci2: ISurfaceConstraintInfo, allowResort: boolean, velocityIn: Vector3, velocityOut: Vector3): void;
protected _simplexSolverExamineActivePlanes(info: SimplexSolverInfo, maxSurfaceVelocity: Vector3, velocityIn: Vector3, velocityOut: Vector3): void;
protected _simplexSolverSolve(constraints: ISurfaceConstraintInfo[], velocity: Vector3, deltaTime: number, minDeltaTime: number, up: Vector3, maxSurfaceVelocity: Vector3): SimplexSolverOutput;
/**
* Compute a CharacterSurfaceInfo from current state and a direction
* @param deltaTime frame delta time in seconds. When using scene.deltaTime divide by 1000.0
* @param direction direction to check, usually gravity direction
* @returns a CharacterSurfaceInfo object
*/
checkSupport(deltaTime: number, direction: Vector3): CharacterSurfaceInfo;
/**
* Compute a CharacterSurfaceInfo from current state and a direction
* @param deltaTime frame delta time in seconds. When using scene.deltaTime divide by 1000.0
* @param direction direction to check, usually gravity direction
* @param surfaceInfo output for surface info
*/
checkSupportToRef(deltaTime: number, direction: Vector3, surfaceInfo: CharacterSurfaceInfo): void;
protected _castWithCollectors(startPos: Vector3, endPos: Vector3, castCollector: any, startCollector?: any): void;
/**
* Rebuild the contact manifold from a proximity query at the given position.
* Used by step-up to validate a candidate landing without the merging logic of `_updateManifold`,
* which is not suited to a zero-length cast.
* @param position position at which to run the proximity query
*/
protected _refreshManifoldAtPosition(position: Vector3): void;
/**
* Search the simplex solver output for a constraint that blocks horizontal motion:
* touched by the solver, non-walkable along `up`, and opposing the requested horizontal direction.
* @param simplexOutput output of `_simplexSolverSolve`
* @param constraints constraint array passed to the solver
* @param horizDir normalized horizontal direction of intent
* @returns the index of the first matching constraint, or -1
*/
protected _findBlockingConstraintIndex(simplexOutput: SimplexSolverOutput, constraints: ISurfaceConstraintInfo[], horizDir: Vector3): number;
/**
* Iterate hits in the cast collector to find the closest one.
* @returns object with fraction, normal, body and index of the closest hit; null if there were no hits
*/
protected _getClosestCastHit(): {
fraction: number;
normal: Vector3;
body: {
body: PhysicsBody;
index: number;
} | null;
} | null;
/**
* Attempt a step-up sweep when the character is blocked by a vertical-ish obstacle.
* Runs three shape casts (up, forward, down) and, if a valid walkable landing is found,
* commits a new position, refreshes the manifold and updates `_lastDisplacement`.
*
* Caller responsibilities on success:
* - subtract the returned time from `remainingTime`
* - skip `_resolveContacts`, the recast block and the position update for the iteration
* (the step is a teleport, not a contact-resolution motion)
*
* @param remainingTime time budget left in the current `_integrateManifolds` iteration
* @param inputVelocity character velocity at the start of the integration call
* @param simplexOutput output of the iteration's simplex solve
* @param constraints constraint array passed to the solver
* @returns time consumed by the step on success, -1 on failure (no state mutated)
*/
protected _tryStepUp(remainingTime: number, inputVelocity: Vector3, simplexOutput: SimplexSolverOutput, constraints: ISurfaceConstraintInfo[]): number;
protected _resolveContacts(deltaTime: number, gravity: Vector3): void;
protected _getInverseInertiaWorld(body: {
body: PhysicsBody;
index: number;
}): DeepImmutableObject<Matrix>;
protected _getComWorldToRef(body: {
body: PhysicsBody;
index: number;
}, result: Vector3): void;
protected _getInvMass(body: {
body: PhysicsBody;
index: number;
}): number;
protected _integrateManifolds(deltaTime: number, gravity: Vector3): void;
/**
* Move the character with collisions
* @param displacement defines the requested displacement vector
*/
moveWithCollisions(displacement: Vector3): void;
/**
* Update internal state. Must be called once per frame
* @param deltaTime frame delta time in seconds. When using scene.deltaTime divide by 1000.0
* @param surfaceInfo surface information returned by checkSupport
* @param gravity gravity applied to the character. Can be different that world gravity
*/
integrate(deltaTime: number, surfaceInfo: CharacterSurfaceInfo, gravity: Vector3): void;
/**
* Helper function to calculate velocity based on surface informations and current velocity state and target
* @param deltaTime frame delta time in seconds. When using scene.deltaTime divide by 1000.0
* @param forwardWorld character forward in world coordinates
* @param surfaceNormal surface normal direction
* @param currentVelocity current velocity
* @param surfaceVelocity velocity induced by the surface
* @param desiredVelocity desired character velocity
* @param upWorld up vector in world space
* @param result resulting velocity vector
* @returns boolean true if result has been computed
*/
calculateMovementToRef(deltaTime: number, forwardWorld: Vector3, surfaceNormal: Vector3, currentVelocity: Vector3, surfaceVelocity: Vector3, desiredVelocity: Vector3, upWorld: Vector3, result: Vector3): boolean;
/**
* Helper function to calculate velocity based on surface informations and current velocity state and target
* @param deltaTime frame delta time in seconds. When using scene.deltaTime divide by 1000.0
* @param forwardWorld character forward in world coordinates
* @param surfaceNormal surface normal direction
* @param currentVelocity current velocity
* @param surfaceVelocity velocity induced by the surface
* @param desiredVelocity desired character velocity
* @param upWorld up vector in world space
* @returns a new velocity vector
*/
calculateMovement(deltaTime: number, forwardWorld: Vector3, surfaceNormal: Vector3, currentVelocity: Vector3, surfaceVelocity: Vector3, desiredVelocity: Vector3, upWorld: Vector3): Vector3;
}
export {};