You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.09 08:39:04