Unity 不使用OnCollisionStay获取所有活跃碰撞的方法咨询
解决方案
方案1:缓存激活碰撞数据(性能最优,适合高频使用场景)
在角色脚本内维护一个集合存储当前所有生效的碰撞数据,通过Unity碰撞生命周期方法实时更新集合内容,跳跃时直接读取缓存即可:
OnCollisionEnter触发时,把符合过滤条件的碰撞数据加入集合OnCollisionStay触发时,更新集合中对应碰撞的最新接触点信息,避免数据过时OnCollisionExit触发时,把结束碰撞的数据从集合中移除
示例代码:
using System.Collections.Generic; using UnityEngine; public class BallPlayerController : MonoBehaviour { [SerializeField] private LayerMask groundLayer; private SphereCollider ballCollider; // 存储当前所有激活的碰撞数据 private List<Collision> activeCollisions = new List<Collision>(); private void Awake() { ballCollider = GetComponent<SphereCollider>(); } private void OnCollisionEnter(Collision collision) { // 过滤只保留需要的碰撞(比如地面、可踩踏物体),减少冗余数据 if (((1 << collision.gameObject.layer) & groundLayer) != 0) { activeCollisions.Add(collision); } } private void OnCollisionStay(Collision collision) { // 更新已有碰撞的最新接触点数据 int matchIndex = activeCollisions.FindIndex(c => c.collider == collision.collider); if (matchIndex >= 0) { activeCollisions[matchIndex] = collision; } } private void OnCollisionExit(Collision collision) { activeCollisions.RemoveAll(c => c.collider == collision.collider); } // 跳跃时调用该方法计算方向 public Vector3 GetJumpDirection() { Vector3 totalNormal = Vector3.zero; foreach (var collision in activeCollisions) { foreach (var contact in collision.contacts) { totalNormal += contact.normal; } } // 没有接触时默认向上,可根据需求修改 return totalNormal == Vector3.zero ? Vector3.up : totalNormal.normalized; } }
方案2:跳跃时主动物理检测(稳定性更高,避免缓存同步问题)
如果不需要持续跟踪碰撞状态,也可以在跳跃触发的瞬间主动做物理检测,直接获取当前的接触数据:
public Vector3 GetJumpDirectionRealtime() { Vector3 totalNormal = Vector3.zero; float checkRadius = ballCollider.radius * transform.lossyScale.x + 0.05f; // 检测范围内所有符合层级的碰撞体 Collider[] hitColliders = Physics.OverlapSphere(transform.position, checkRadius, groundLayer); foreach (var collider in hitColliders) { if (collider == ballCollider) continue; // 计算两个碰撞体的穿透信息,获取接触法线 if (Physics.ComputePenetration( ballCollider, transform.position, transform.rotation, collider, collider.transform.position, collider.transform.rotation, out Vector3 penetrationDir, out float _ )) { totalNormal += penetrationDir.normalized; } } return totalNormal == Vector3.zero ? Vector3.up : totalNormal.normalized; }
方案选择建议:如果跳跃可用性判断、方向计算需要每帧运行,优先选缓存方案,性能开销更低;如果跳跃触发频率低,或者经常遇到碰撞体销毁未触发
OnCollisionExit导致的缓存不同步问题,选择实时检测方案更稳定。
内容的提问来源于stack exchange,提问作者Šimon Brecher
相关产品推荐
相关产品推荐

