Unity自定义碰撞脚本:子弹弹跳后速度异常增加问题排查
问题根源分析与修复方案
核心问题1:位置更新未做时间缩放
你的循环中直接将velocity加到位置上,没有乘以Time.fixedDeltaTime:
bulletTransf.position += velocity; // 错误:速度单位是米/秒,未匹配帧时间
velocity的单位是米/秒,而FixedUpdate的执行间隔是Time.fixedDeltaTime(通常为0.02秒),直接叠加velocity会让实际移动速度变成设定值的1/Time.fixedDeltaTime倍(比如50倍),完全违背速度的物理意义。
同时重力的叠加是正确的(乘以了Time.fixedDeltaTime),这就导致速度增长和位置更新的缩放完全不匹配,碰撞反弹后错误的速度会被进一步放大,最终出现速度异常增加的现象。
核心问题2:碰撞法线方向错误
你计算碰撞法线的逻辑是:
var collisionNormal = ((Vector2)bulletTransf.position - collisionPoints[0]).normalized;
collisionPoints[0]是碰撞体表面到子弹的最近点,当子弹穿透碰撞体内部时,子弹位置 - 最近点的向量指向碰撞体内部,和实际需要的“碰撞体表面指向外部”的法线方向完全相反。错误的法线会让Vector2.Reflect计算出错误的反弹方向,比如子弹在碰撞体内部来回反弹,每次叠加重力后速度越来越大。
次要问题:重力施加的判断逻辑错误
if (velocity.y > -gravityIntensity) velocity += gravityIntensity * Time.fixedDeltaTime * Vector3.down;
用加速度值gravityIntensity作为终端速度的阈值完全不合理——终端速度是速度值(比如-10米/秒),而非加速度。这个判断会导致重力施加不完整,甚至在某些场景下错误叠加。
修复后的代码
var velocity = speed * transform.right; var collDetect = bullet.GetComponent<CollisionDetect>(); var bulletTransf = bullet.transform; bulletTransf.position = transform.position; // 终端速度(根据需求调整,单位:米/秒) float terminalVelocity = -10f; collDetect.OnTriggerEnter += coll => { if (coll.gameObject.layer != environmentLayer) return; var collisionPoints = collDetect.Colliders .Where(c=>c.gameObject.layer == environmentLayer) .Select(c=>c.ClosestPoint(bulletTransf.position)) .ToArray(); // 修正法线方向:碰撞体表面指向外部(指向子弹) var collisionNormal = ((Vector2)collisionPoints[0] - bulletTransf.position).normalized; var bounceVector = Vector2.Reflect(velocity, collisionNormal); velocity = bounceVector; // 可选:添加微小偏移避免子弹卡在碰撞体内 bulletTransf.position += collisionNormal * 0.01f; }; collDetect.OnObjDisable += collDtct => { collDetect.MyCollider.isTrigger = true; collDtct.EmptyOnTriggerEnter(); collDtct.EmptyOnTriggerExit(); collDtct.EmptyOnObjDisable(); }; while (true) { // 位置更新乘以Time.fixedDeltaTime,匹配速度单位 bulletTransf.position += velocity * Time.fixedDeltaTime; // 修正重力逻辑:持续施加直到达到终端速度 if (velocity.y > terminalVelocity) velocity += gravityIntensity * Time.fixedDeltaTime * Vector3.down; yield return fixedUpdate; }
额外注意事项
- 确保
speed的单位是米/秒,和Unity物理系统单位统一; - 如果不需要终端速度,可直接移除
if (velocity.y > terminalVelocity)判断,持续施加重力; - 碰撞后的微小位置偏移可避免子弹卡在碰撞体内部反复触发碰撞。
内容的提问来源于stack exchange,提问作者Stefan Carpeliuc
相关产品推荐
相关产品推荐

