--- order: 3 title: 角色控制器 type: 物理 label: Physics --- 角色控制器([CharacterController](/apis/core/#CharacterController))是一种特殊的碰撞器组件,专门用于处理角色的物理运动。它提供了专门的移动算法和碰撞检测,能够处理台阶、斜坡等复杂地形,特别适合第一人称或第三人称游戏中的角色控制。 ## 与动态碰撞器的区别 角色控制器和[动态碰撞器](/docs/physics/collider/dynamicCollider)虽然都可以实现物体的移动,但它们有着显著的区别: | 特性 | 角色控制器 | 动态碰撞器 | |------|------------|------------| | 移动方式 | 通过 `move()` 方法直接控制位移 | 通过施加力或冲量来移动 | | 碰撞响应 | 手动处理碰撞响应,自动处理台阶和斜坡 | 基于物理模拟的碰撞响应 | | 重力处理 | 需要手动实现重力效果 | 自动应用场景重力 | | 形状限制 | 只能使用一个碰撞形状 | 可以使用多个碰撞形状 | | 适用场景 | 角色控制、第一/第三人称游戏 | 物理模拟、刚体运动 | | 性能开销 | 较低 | 较高 | 选择建议: - 如果需要精确控制角色的移动和碰撞响应,使用角色控制器 - 如果需要真实的物理模拟效果,使用动态碰撞器 ## 使用方法 1. 选中目标实体,并在检查器中点击添加组件按钮,添加 CharacterController 组件。 2. 设置控制器的碰撞形状,使碰撞形状与角色的外形相尽量匹配。关于碰撞形状的详细说明请参考[碰撞形状](/docs/physics/collider/colliderShape)文档。 与其他碰撞器不同,角色控制器只能添加一个碰撞形状。通常建议使用胶囊体(CapsuleColliderShape)作为角色的碰撞形状。 3. 根据需要设置碰撞器的属性调整物体的物理行为,各属性的含义和作用请参考下文。 ## 属性说明 ### 继承自 Collider 的属性 | 属性 | 描述 | | ----------------------------------------- | ------------ | | [**shapes**](/apis/core/#Collider-shapes) | 碰撞形状集合 | ### 特有属性 | 属性 | 描述 | 默认值 | | ---- | ---- | ------ | | [**stepOffset**](/apis/core/#CharacterController-stepOffset) | 角色可以自动跨越的最大台阶高度。 | 0.5 | | [**slopeLimit**](/apis/core/#CharacterController-slopeLimit) | 角色可以行走的最大斜坡角度(度)。 | 45° | | [**nonWalkableMode**](/apis/core/#CharacterController-nonWalkableMode) | 定义如何处理不可行走的表面。 | PreventClimbing | | [**upDirection**](/apis/core/#CharacterController-upDirection) | 定义角色的向上方向。默认为 (0, 1, 0),即世界空间的 Y 轴向上。影响移动和碰撞检测的方向判定 | (0, 1, 0) | ## 公开方法 ### 继承自 Collider 的方法 | 方法名 | 描述 | | --------------------------------------------------- | ---------------- | | [**addShape**](/apis/core/#Collider-addShape) | 添加碰撞形状 | | [**removeShape**](/apis/core/#Collider-removeShape) | 移除指定碰撞形状 | | [**clearShapes**](/apis/core/#Collider-clearShapes) | 清空所有碰撞形状 | ### 特有方法 | 方法名 | 描述 | | ---- | ---- | | [**move**](/apis/core/#CharacterController-move) | 移动角色控制器。返回一个碰撞标志值,标识碰撞状态。 | ### 碰撞标志说明 移动函数 `move()` 会返回一个碰撞标志值,用于表示角色控制器与环境的碰撞状态。这些标志可以通过按位与运算(&)来检测: | 标志名称 | 值 | 说明 | |---------|----|----| | None | 0 | 没有发生任何碰撞 | | Sides | 1 | 与侧面发生碰撞 | | Up | 2 | 与上方发生碰撞(如天花板) | | Down | 4 | 与下方发生碰撞(如地面) | ## 脚本使用 ### 基础配置 ```typescript // 创建角色控制器 const controller = entity.addComponent(CharacterController); // 添加胶囊体形状 const capsule = new CapsuleColliderShape(); capsule.radius = 0.5; capsule.height = 2; controller.addShape(capsule); // 配置控制器属性 controller.stepOffset = 0.5; // 设置台阶高度 controller.slopeLimit = 45; // 设置最大可行走斜坡角度 controller.upDirection = new Vector3(0, 1, 0); // 设置向上方向 ``` ### 使用移动函数 ```typescript class CharacterMovement extends Script { private _velocity = new Vector3(); onUpdate(deltaTime: number) { const controller = this.entity.getComponent(CharacterController); // 创建位移向量 const displacement = new Vector3(); Vector3.scale(this._velocity, deltaTime, displacement); // 执行移动并获取碰撞标志 // minDist: 最小移动距离,通常设为0 // deltaTime: 经过的时间,用于物理计算 const collisionFlags = controller.move(displacement, 0, deltaTime); // 处理碰撞响应 if (collisionFlags & ControllerCollisionFlag.Down) { // 角色接触地面 } } } ``` 使用示例: ```typescript const flags = controller.move(displacement, 0, deltaTime); // 检查是否接触地面 if (flags & ControllerCollisionFlag.Down) { // 角色在地面上 this._isGrounded = true; } // 检查是否撞到天花板 if (flags & ControllerCollisionFlag.Up) { // 角色撞到头部 this._velocity.y = 0; } // 检查是否撞到墙壁 if (flags & ControllerCollisionFlag.Sides) { // 角色撞到墙壁 this._handleWallCollision(); } // 可以同时检查多个标志 if ((flags & ControllerCollisionFlag.Down) && (flags & ControllerCollisionFlag.Sides)) { // 角色同时接触地面和墙壁 } ``` ### 斜坡/台阶行走 1. **斜坡行走** ```typescript // 通过设置 slopeLimit 控制可行走的斜坡角度 controller.slopeLimit = 60; // 允许更陡的斜坡 // 设置不可行走斜面的处理方式 controller.nonWalkableMode = ControllerNonWalkableMode.PreventClimbingAndForceSliding; // 在太陡的斜面上会滑下 ``` 2. **台阶行走调整** ```typescript // 调整 stepOffset 来控制可跨越的台阶高度 controller.stepOffset = 0.3; // 较低的台阶 controller.stepOffset = 0.5; // 较高的台阶 ``` ### 重力处理 角色控制器本身不包含重力处理,需要在脚本中手动实现重力效果。以下是一个完整的重力处理示例: ```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; // 获取输入 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); // 计算移动方向 const moveDirection = new Vector3(horizontal, 0, vertical); moveDirection.normalize(); // 应用移动速度 this._velocity.x = moveDirection.x * this._moveSpeed; this._velocity.z = moveDirection.z * this._moveSpeed; // 应用重力 if (!this._isGrounded) { this._velocity.y += this._gravity.y * deltaTime; } // 处理跳跃 if (this._isGrounded && jump) { this._velocity.y = this._jumpForce; this._isGrounded = false; } // 执行移动 const displacement = new Vector3(); Vector3.scale(this._velocity, deltaTime, displacement); const collisionFlags = this._controller.move(displacement, 0, deltaTime); // 更新地面状态 this._isGrounded = (collisionFlags & ControllerCollisionFlag.Down) !== 0; if (this._isGrounded) { this._velocity.y = 0; } } } ``` ## 限制和注意事项 1. **形状限制** - 只能使用一个碰撞形状 - 建议使用胶囊体(CapsuleColliderShape)作为碰撞形状 2. **性能考虑** - 角色控制器比动态碰撞器性能更好 - 但每个角色控制器都会占用一定的物理计算资源 - 建议在场景中控制角色控制器的数量