---
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 |

<Callout type="positive">
Recommendations:
- Use Character Controller when precise control over character movement and collision response is needed
- Use Dynamic Collider when realistic physics simulation is required
</Callout>

## Usage

1. Select the target entity and click the Add Component button in the inspector to add the CharacterController component.

<Image src="https://mdn.alipayobjects.com/huamei_3zduhr/afts/img/A*nEMXRKiqpy8AAAAAAAAAAAAAesJ_AQ/original" />

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.

<Callout type="positive">
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.
</Callout>

<Image src="https://mdn.alipayobjects.com/huamei_3zduhr/afts/img/A*4QvUTI4D89EAAAAAAAAAAAAAesJ_AQ/original" />

<Image src="https://mdn.alipayobjects.com/huamei_3zduhr/afts/img/A*aRGqSIMqDmsAAAAAAAAAAAAAesJ_AQ/original" />

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.<ul><li>Must be greater than or equal to 0</li><li>Actual climbable height = stepOffset + contact offset of collision shape</li></ul> | 0.5 |
| [**slopeLimit**](/apis/core/#CharacterController-slopeLimit) | Maximum slope angle (in degrees) the character can walk on.<ul><li>Surfaces steeper than this angle will be treated as unwalkable walls</li><li>Affects the character's climbing ability</li></ul> | 45° |
| [**nonWalkableMode**](/apis/core/#CharacterController-nonWalkableMode) | Defines how to handle unwalkable surfaces.<ul><li>PreventClimbing: Prevents the character from climbing unwalkable slopes but doesn't force other movements (default)</li><li>PreventClimbingAndForceSliding: Prevents the character from climbing unwalkable slopes and forces the character to slide down</li></ul> | 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.<ul><li>displacement: Movement vector</li><li>minDist: Minimum movement distance</li><li>elapsedTime: Elapsed time</li></ul> |

### 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
