Unity中两个CharacterController间如何实现单向推动功能
问题解答
你的推测是对的,Unity原生刚体推送组件完全无法在两个挂载CharacterController的对象之间生效。
原因说明
CharacterController是Unity脱离PhysX物理系统独立实现的角色控制组件,它的移动、碰撞响应完全由Move/SimpleMove接口驱动,不会响应刚体的力、冲量交互。两个CharacterController碰撞时默认只会产生互相阻挡的效果,哪怕给对象同时挂载刚体,也会因为CharacterController主动接管碰撞后的位置校正逻辑,导致刚体推力完全失效。
单向推动实现步骤
不需要依赖刚体,直接基于CharacterController自带的碰撞回调就能实现,具体操作如下:
- 给拥有推动权限的角色A设置专属Tag,比如
CanPushOthers,确保两个角色都正常挂载Starter Assets自带的FirstPersonController和CharacterController组件。 - 新建C#脚本挂载到角色A上,监听
OnControllerColliderHit碰撞回调,这个回调会在角色A移动碰到其他碰撞体时自动触发。 - 在回调中判断碰撞到的对象是否是可被推动的角色B:判断对方是否挂载
CharacterController,且不属于带CanPushOthersTag的推动源对象。 - 计算水平方向的推动位移,直接调用被撞角色的
CharacterController.Move()方法施加位移,实现推动效果。 - 角色B不需要挂载任何推送相关脚本,它碰撞到角色A时,因为没有对应的推送逻辑,自然无法推动角色A。
示例实现代码
using UnityEngine; public class UnilateralPush : MonoBehaviour { [Tooltip("推力强度,根据实际手感调整")] public float pushForce = 2.5f; private CharacterController _ownController; void Awake() { _ownController = GetComponent<CharacterController>(); } void OnControllerColliderHit(ControllerColliderHit hit) { // 碰撞对象不是CharacterController就跳过 if (!hit.collider.TryGetComponent<CharacterController>(out var hitController)) return; // 碰到其他有权限推人的角色就跳过,避免互推 if (hit.collider.CompareTag("CanPushOthers")) return; // 只取水平方向的推动方向,避免把角色推得上下飞 Vector3 pushDirection = new Vector3(hit.moveDirection.x, 0, hit.moveDirection.z).normalized; // 按自身移动速度计算推移动位移 Vector3 pushOffset = pushDirection * (_ownController.velocity.magnitude * pushForce * Time.deltaTime); // 让被碰角色产生位移 hitController.Move(pushOffset); } }
调参提示:如果出现推动卡顿、穿模的问题,可以适当调大两个角色
CharacterController的Skin Width参数,数值设为半径的10%左右体验最好。Starter Assets自带的FirstPersonController默认不会锁死外部调用Move产生的位移,不需要修改原资源包的代码。
内容的提问来源于stack exchange,提问作者c0d3l0v3r
相关产品推荐
相关产品推荐

