mirror of
https://github.com/galacean/engine.git
synced 2026-05-19 19:46:19 +08:00
247 lines
9.8 KiB
Plaintext
247 lines
9.8 KiB
Plaintext
---
|
|
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
|