--- order: 3 title: Character Controller type: Physics label: Physics --- The [CharacterController](/apis/core/#CharacterController) is a special collider component designed specifically for handling character physics movement. It provides specialized movement algorithms and collision detection, capable of handling complex terrain like steps and slopes, making it particularly suitable for character control in first-person or third-person games. ## Differences from Dynamic Collider While both the Character Controller and [Dynamic Collider](/en/docs/physics/collider/dynamicCollider) can achieve object movement, they have significant differences: | Feature | Character Controller | Dynamic Collider | |---------|---------------------|------------------| | Movement Method | Direct displacement control via `move()` method | Movement through forces or impulses | | Collision Response | Manual collision response, automatic step and slope handling | Physics-based collision response | | Gravity Handling | Requires manual gravity implementation | Automatic scene gravity application | | Shape Limitations | Can only use one collision shape | Can use multiple collision shapes | | Use Cases | Character control, first/third-person games | Physics simulation, rigid body motion | | Performance Cost | Lower | Higher | Recommendations: - Use Character Controller when precise control over character movement and collision response is needed - Use Dynamic Collider when realistic physics simulation is required ## Usage 1. Select the target entity and click the Add Component button in the inspector to add the CharacterController component. 2. Set the controller's collision shape to match the character's appearance as closely as possible. For detailed information about collision shapes, please refer to the [ColliderShape](/en/docs/physics/collider/colliderShape) documentation. Unlike other colliders, the Character Controller can only have one collision shape. It is recommended to use a CapsuleColliderShape as the character's collision shape. 3. Adjust the collider's properties according to your needs to modify the object's physical behavior. The meaning and function of each property are explained below. ## Property Description ### Properties Inherited from Collider | Property | Description | |----------|-------------| | [**shapes**](/apis/core/#Collider-shapes) | Collection of collision shapes | ### Specific Properties | Property | Description | Default | |----------|-------------|---------| | [**stepOffset**](/apis/core/#CharacterController-stepOffset) | Maximum step height the character can automatically climb. | 0.5 | | [**slopeLimit**](/apis/core/#CharacterController-slopeLimit) | Maximum slope angle (in degrees) the character can walk on. | 45° | | [**nonWalkableMode**](/apis/core/#CharacterController-nonWalkableMode) | Defines how to handle unwalkable surfaces. | PreventClimbing | | [**upDirection**](/apis/core/#CharacterController-upDirection) | Defines the character's up direction. Default is (0, 1, 0), which means Y-axis up in world space. Affects movement and collision detection direction | (0, 1, 0) | ## Public Methods ### Methods Inherited from Collider | Method | Description | |--------|-------------| | [**addShape**](/apis/core/#Collider-addShape) | Add a collision shape | | [**removeShape**](/apis/core/#Collider-removeShape) | Remove a specified collision shape | | [**clearShapes**](/apis/core/#Collider-clearShapes) | Clear all collision shapes | ### Specific Methods | Method | Description | |--------|-------------| | [**move**](/apis/core/#CharacterController-move) | Move the character controller. Returns a collision flag value indicating collision status. | ### Collision Flags The `move()` function returns a collision flag value that indicates the character controller's collision status with the environment. These flags can be detected using bitwise AND operations (&): | Flag Name | Value | Description | |-----------|-------|-------------| | None | 0 | No collision occurred | | Sides | 1 | Collided with sides | | Up | 2 | Collided with the top (e.g., ceiling) | | Down | 4 | Collided with the bottom (e.g., ground) | ## Script Usage ### Basic Configuration ```typescript // Create character controller const controller = entity.addComponent(CharacterController); // Add capsule shape const capsule = new CapsuleColliderShape(); capsule.radius = 0.5; capsule.height = 2; controller.addShape(capsule); // Configure controller properties controller.stepOffset = 0.5; // Set step height controller.slopeLimit = 45; // Set maximum walkable slope angle controller.upDirection = new Vector3(0, 1, 0); // Set upward direction ``` ### Using the Move Function ```typescript class CharacterMovement extends Script { private _velocity = new Vector3(); onUpdate(deltaTime: number) { const controller = this.entity.getComponent(CharacterController); // Create displacement vector const displacement = new Vector3(); Vector3.scale(this._velocity, deltaTime, displacement); // Execute movement and get collision flags // minDist: Minimum movement distance, usually set to 0 // deltaTime: Elapsed time for physics calculations const collisionFlags = controller.move(displacement, 0, deltaTime); // Handle collision response if (collisionFlags & ControllerCollisionFlag.Down) { // Character is on the ground } } } ``` Usage example: ```typescript const flags = controller.move(displacement, 0, deltaTime); // Check if touching ground if (flags & ControllerCollisionFlag.Down) { // Character is on ground this._isGrounded = true; } // Check if hitting ceiling if (flags & ControllerCollisionFlag.Up) { // Character hit ceiling this._velocity.y = 0; } // Check if hitting wall if (flags & ControllerCollisionFlag.Sides) { // Character hit wall this._handleWallCollision(); } // Can check multiple flags simultaneously if ((flags & ControllerCollisionFlag.Down) && (flags & ControllerCollisionFlag.Sides)) { // Character is touching both ground and wall } ``` ### Slope/Step Walking 1. **Slope Walking** ```typescript // Control walkable slope angle by setting slopeLimit controller.slopeLimit = 60; // Allow steeper slopes // Set how to handle unwalkable slopes controller.nonWalkableMode = ControllerNonWalkableMode.PreventClimbingAndForceSliding; // Will slide down on too steep slopes ``` 2. **Step Walking Adjustment** ```typescript // Adjust stepOffset to control climbable step height controller.stepOffset = 0.3; // Lower steps controller.stepOffset = 0.5; // Higher steps ``` ### Gravity Handling The Character Controller itself doesn't include gravity handling; you need to implement gravity effects manually in scripts. Here's a complete gravity handling example: ```typescript class CharacterMovement extends Script { private _controller: CharacterController; private _velocity = new Vector3(); private _isGrounded = false; private _moveSpeed = 5; private _jumpForce = 5; private _gravity: Vector3; onAwake() { this._controller = this.entity.getComponent(CharacterController); this._gravity = this.scene.physics.gravity; } onUpdate(deltaTime: number) { const inputManager = this.engine.inputManager; // Get input const horizontal = inputManager.isKeyHeldDown(Keys.KeyA) ? -1 : inputManager.isKeyHeldDown(Keys.KeyD) ? 1 : 0; const vertical = inputManager.isKeyHeldDown(Keys.KeyS) ? -1 : inputManager.isKeyHeldDown(Keys.KeyW) ? 1 : 0; const jump = inputManager.isKeyDown(Keys.Space); // Calculate movement direction const moveDirection = new Vector3(horizontal, 0, vertical); moveDirection.normalize(); // Apply movement speed this._velocity.x = moveDirection.x * this._moveSpeed; this._velocity.z = moveDirection.z * this._moveSpeed; // Apply gravity if (!this._isGrounded) { this._velocity.y += this._gravity.y * deltaTime; } // Handle jumping if (this._isGrounded && jump) { this._velocity.y = this._jumpForce; this._isGrounded = false; } // Execute movement const displacement = new Vector3(); Vector3.scale(this._velocity, deltaTime, displacement); const collisionFlags = this._controller.move(displacement, 0, deltaTime); // Update ground status this._isGrounded = (collisionFlags & ControllerCollisionFlag.Down) !== 0; if (this._isGrounded) { this._velocity.y = 0; } } } ``` ## Limitations and Considerations 1. **Shape Limitations** - Can only use one collision shape - Recommended to use CapsuleColliderShape as the collision shape 2. **Performance Considerations** - Character Controller has better performance than Dynamic Collider - But each Character Controller consumes some physics calculation resources - It's recommended to control the number of Character Controllers in the scene