Skip to content
🎉 Angular Three v4 is here! Read the announcement

Introduction

angular-three-ecctrl is an Angular Three port of the default pmndrs/ecctrl core entry: a Rapier-backed dynamic capsule controller for characters.

No additional peer dependencies required beyond the core dependencies.

Terminal window
npm install angular-three-ecctrl angular-three-rapier @dimforge/rapier3d-compat
# yarn add angular-three-ecctrl angular-three-rapier @dimforge/rapier3d-compat
# pnpm add angular-three-ecctrl angular-three-rapier @dimforge/rapier3d-compat

Make sure you already have angular-three installed.

angular-three-soba is an optional peer only for the /camera integration.

NgteEcctrl is the ngte-ecctrl component and is exported as ecctrl. It creates a dynamic rigid body and capsule collider, and must be nested under NgtrPhysics. Project the character’s visual content into the controller:

import { Component, CUSTOM_ELEMENTS_SCHEMA, signal } from '@angular/core';
import { extend } from 'angular-three';
import { NgteEcctrl, type NgteEcctrlMovementInput } from 'angular-three-ecctrl';
import { NgteEcctrlAnimationState, type NgteEcctrlAnimationStateValue } from 'angular-three-ecctrl/animation';
import { NgtrPhysics } from 'angular-three-rapier';
import * as THREE from 'three';
extend(THREE);
@Component({
template: `
<ngtr-physics>
<ng-template>
<ngte-ecctrl
#player="ecctrl"
[movement]="movement()"
[animationState]="true"
(animationStateChange)="onAnimationStateChange($event)"
>
<ngt-mesh>
<!-- Project the character's visual mesh or model here. -->
</ngt-mesh>
</ngte-ecctrl>
</ng-template>
</ngtr-physics>
`,
imports: [NgtrPhysics, NgteEcctrl, NgteEcctrlAnimationState],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class SceneGraph {
readonly movement = signal<NgteEcctrlMovementInput>({
forward: false,
backward: false,
leftward: false,
rightward: false,
run: false,
jump: false,
joystick: { x: 0, y: 0 },
});
onAnimationStateChange(_state: NgteEcctrlAnimationStateValue) {}
}

animationState is optional and comes from the explicit /animation entry point. See Animation state for its API.

SurfacePublic API
Selector / exportngte-ecctrl / ecctrl
Models and inputsoptions, position, rotation, quaternion, scale, userData, rigidBodyOptions, colliderOptions, movement
Rigid-body outputswake, sleep, collisionEnter, collisionExit, intersectionEnter, intersectionExit, contactForce
Public referencesbody is the controller rigid body, collider is its capsule collider, state is the live controller state, and handle is the programmatic controller handle.
Public methodssetMovement, setLockForward, setForwardDir

Movement is source-agnostic. NgteEcctrlMovementInput has boolean forward, backward, leftward, rightward, run, and jump fields plus joystick: { x, y }. Bind the movement model from a keyboard, gamepad, virtual controls, or a custom adapter. Imperative sources can call player.setMovement({ forward: true }); the controller merges a partial update.

NgteEcctrlGroundDetection is 'shapeCast' | 'rayCast'. NgteEcctrlUserData supports ecctrl.excludeRay, ecctrl.excludeCharacterRay, and ecctrl.excludeVehicleRay for filtering Ecctrl ray queries.

NgteEcctrl forwards the listed transform inputs and rigid-body event outputs to its Rapier body. Use state to observe the controller and handle when an integration needs programmatic access; the component methods above update its movement and forward-direction behavior.

NgteEcctrlOptions configures the core controller. Its fields are grouped below so option names remain discoverable without duplicating their defaults.

AreaFields
General and capsuleenable, debug, capsuleHalfHeight, capsuleRadius
OrientationlockForward, useCustomForward, useCharacterUpAxis
GravityenableCustomGravity, customGravity, gravityField, gravityDirLerpSpeed
MovementmaxWalkVel, maxRunVel, accDeltaTime, decDeltaTime, rejectVelFactor, moveImpulsePointOffset
Jump and airjumpVel, jumpDuration, slopeJumpFactor, airDragFactor, slideGripFactor, fallingGravityScale, fallingMaxVel, enableToggleRun
Ground and floatinggroundDetection, slopeMaxAngle, floatHeight, rayOriginOffest, rayHitForgiveness, rayLength, rayRadius, springK, dampingC
Auto balance and platformsautoBalance, autoBalanceSpringK, autoBalanceDampingC, autoBalanceSpringOnY, autoBalanceDampingOnY, followPlatform
Counter forcesmassRatioFallOffCurveData, applyCounterMass, applyCounterJumpImp, counterJumpImpFactor, applyCounterMoveImp, counterMoveImpFactor

rayOriginOffest is the intentionally retained upstream option spelling.

The root entry also exports NgteEcctrlRigidBodyOptions, NgteEcctrlColliderOptions, NgteEcctrlState, and NgteEcctrlHandle, alongside DEFAULT_ECCTRL_OPTIONS and DEFAULT_ECCTRL_POSITION. Use the options and companion input types to configure the controller, NgteEcctrlState to type observed state, and NgteEcctrlHandle to type programmatic controller references.

The controller’s configured custom gravity replaces its native response to Rapier world gravity. See Gravity fields for the distinct additive behavior of fields applied to arbitrary rigid bodies.

The root entry contains the controller and its core types. Import each optional integration explicitly:

Entry pointReference
angular-three-ecctrlCore controller
angular-three-ecctrl/animationAnimation state
angular-three-ecctrl/cameraCamera follow
angular-three-ecctrl/curvesCurves
angular-three-ecctrl/gravityGravity fields
angular-three-ecctrl/inputInput
angular-three-ecctrl/timeTime control
angular-three-ecctrl/vehicleVehicles

There is intentionally no /all, /leva, or /utils entry point, and no backwards-compatibility shims. Optional secondary entry points are not root re-exports.