Unity 3D移动端跳跃游戏两类碰撞检测异常问题求助
Unity移动端跳跃游戏碰撞检测问题解决方案
我正在Unity 3D中开发一款移动端跳跃游戏,玩法为从一面墙跳跃到另一面墙并躲避尖刺。游戏视觉上呈现垂直视角,但实际仅为相机旋转效果。目前遇到两类碰撞检测相关问题,以下是玩家控制器代码:
using System.Collections; using System.Collections.Generic; using UnityEngine; public class PlayerController : MonoBehaviour { public Rigidbody rgb; public float jumpStrenght; public bool walled; public bool jump; void Start() { } void FixedUpdate() { if(Input.GetButtonDown("Jump")) { Jump(); } if(jump) { Jump(); } if (walled) { rgb.isKinematic = true; } } public void Jump() { if (walled) { walled = false; rgb.isKinematic = false; rgb.AddForce(transform.up * jumpStrenght, ForceMode.Impulse); } } void OnCollisionEnter(Collision col) { if (col.gameObject.CompareTag("Wall")) { walled = true; } //print("col enter"); } void OnTriggerEnter(Collider col) { if (col.gameObject.CompareTag("WallEnd")) { walled = false; } //print("trigger enter"); } void OnCollisionExit(Collision col) { //print("col exit"); } public void OnPress() { jump = true; } public void OnRelese() { jump = false; } }
问题1:玩家落到墙面边缘时OnCollisionExit不触发
现象:玩家落到墙面边缘时OnCollisionExit完全不触发;补充说明显示,OnCollisionExit会在玩家进入碰撞时错误触发,且玩家已与地面碰撞时OnCollisionEnter也不会触发。当前通过设置Rigidbody为Kinematic实现玩家贴墙状态。
问题分析
核心原因是设置Rigidbody为Kinematic后,Unity物理引擎会停止跟踪该物体的碰撞状态变化。当玩家贴墙时设置rgb.isKinematic = true,物理引擎不再检测碰撞退出事件,自然不会调用OnCollisionExit。此外,边缘碰撞时接触点可能瞬间消失,引擎来不及捕获退出事件。
解决方案
- 替换Kinematic状态为冻结位置/速度,保留物理碰撞检测能力:
// 替换原FixedUpdate中walled的处理逻辑 if (walled) { rgb.velocity = Vector3.zero; rgb.constraints = RigidbodyConstraints.FreezePosition; } else { rgb.constraints = RigidbodyConstraints.None; } - 增加
OnCollisionStay持续验证墙面接触状态,避免边缘接触时状态异常:void OnCollisionStay(Collision col) { if (col.gameObject.CompareTag("Wall")) { // 通过接触点数量判断是否仍有效贴墙 walled = col.contacts.Length > 0; } } - 修复
OnCollisionExit逻辑,确保离开墙面时正确重置状态:void OnCollisionExit(Collision col) { if (col.gameObject.CompareTag("Wall")) { walled = false; rgb.constraints = RigidbodyConstraints.None; } }
问题2:非上方接触尖刺时碰撞检测不触发
现象:玩家不从上方落到尖刺上时,尖刺的OnCollisionEnter不会触发,游戏仍正常运行。以下是尖刺代码:
using System.Collections; using System.Collections.Generic; using UnityEngine; public class Danger : MonoBehaviour { void OnCollisionEnter(Collision col) { print("col"); if(col.gameObject.GetComponent<PlayerController>()) { print("colPl"); GameManager.instance.EndGame(); } } }
问题分析
可能的触发原因:
- 尖刺碰撞体形状/大小不合理,非上方角度接触时无法触发碰撞检测;
- 玩家Rigidbody处于Kinematic状态时,不会触发普通碰撞事件;
- 碰撞层矩阵设置错误,玩家与尖刺所在层未开启碰撞检测。
解决方案
- 将尖刺改为Trigger碰撞体,使用
OnTriggerEnter实现全角度检测:
修改尖刺脚本:
同时在Inspector中勾选尖刺碰撞体的Is Trigger选项。using UnityEngine; public class Danger : MonoBehaviour { void OnTriggerEnter(Collider col) { if (col.TryGetComponent(out PlayerController player)) { GameManager.instance.EndGame(); } } } - 确保玩家在碰撞尖刺时,Rigidbody的
isKinematic为false(贴墙时的位置冻结不影响Trigger检测); - 检查
Edit → Project Settings → Physics中的碰撞层矩阵,确认玩家层与尖刺层的碰撞检测处于开启状态。
玩家控制器代码优化建议
原代码存在逻辑冗余、命名不规范等问题,整理后版本如下:
using UnityEngine; public class PlayerController : MonoBehaviour { public Rigidbody rgb; public float jumpStrength; public bool isWalled; private bool jumpInput; void FixedUpdate() { // 合并跳跃输入逻辑 if (jumpInput && isWalled) { Jump(); } // 贴墙时冻结位置,保留物理检测 if (isWalled) { rgb.velocity = Vector3.zero; rgb.constraints = RigidbodyConstraints.FreezePosition; } else { rgb.constraints = RigidbodyConstraints.None; } } public void Jump() { isWalled = false; rgb.AddForce(transform.up * jumpStrength, ForceMode.Impulse); jumpInput = false; } void OnCollisionEnter(Collision col) { if (col.gameObject.CompareTag("Wall")) { isWalled = true; } } void OnCollisionStay(Collision col) { if (col.gameObject.CompareTag("Wall")) { isWalled = col.contacts.Length > 0; } } void OnCollisionExit(Collision col) { if (col.gameObject.CompareTag("Wall")) { isWalled = false; } } void OnTriggerEnter(Collider col) { if (col.gameObject.CompareTag("WallEnd")) { isWalled = false; } } // 移动端输入回调(修正拼写错误) public void OnPress() { jumpInput = true; } public void OnRelease() { jumpInput = false; } }
内容的提问来源于stack exchange,提问作者chrismaksymiuk
相关产品推荐
相关产品推荐

