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

Unity中多相同物体碰撞时,OnCollisionEnter实现材质延迟恢复的问题

解决方案:避免碰撞物体Renderer引用被覆盖

你的问题核心在于类中的meshRenderer是全局变量,每次碰撞新的Obstacle时都会覆盖这个引用,导致之前触发的延迟恢复操作(不管是Invoke还是Coroutine)最终修改的是最后一次碰撞的物体材质,而非触发该操作的原始物体。

要解决这个问题,需要让每个延迟操作持有独立的目标Renderer引用,而不是依赖全局变量。下面是两种可行的修正方案:

方案1:带参数的协程(推荐)

直接将碰撞到的Renderer作为参数传入协程,每个协程独立保存自己的目标引用,不会被后续碰撞覆盖:

using System.Collections;
using UnityEngine;

public class CollisionBehavior : MonoBehaviour
{
    [SerializeField] Material highlightMat;
    [SerializeField] Material defaultMat;

    void OnCollisionEnter(Collision other)
    {
        if (other.gameObject.CompareTag("Obstacle"))
        {
            Renderer targetRenderer = other.gameObject.GetComponent<Renderer>();
            if (targetRenderer == null) return;

            // 切换高亮材质
            targetRenderer.material = highlightMat;
            // 启动协程,传入当前碰撞的Renderer
            StartCoroutine(RevertMaterialAfterDelay(targetRenderer, 2f));
        }
    }

    IEnumerator RevertMaterialAfterDelay(Renderer rendererToRevert, float delay)
    {
        yield return new WaitForSeconds(delay);
        // 恢复默认材质,这里用的是协程自己持有的Renderer引用
        rendererToRevert.material = defaultMat;
    }
}

方案2:封装独立逻辑到Obstacle物体

另一种更清晰的方式是给每个Obstacle单独挂载脚本,让物体自己处理材质切换和恢复,小球只负责触发事件:

小球脚本(CollisionBehavior)

using UnityEngine;

public class CollisionBehavior : MonoBehaviour
{
    void OnCollisionEnter(Collision other)
    {
        if (other.gameObject.CompareTag("Obstacle"))
        {
            // 调用Obstacle自身的材质切换方法
            other.gameObject.GetComponent<ObstacleMaterialHandler>()?.TriggerHighlight();
        }
    }
}

Obstacle挂载的脚本(ObstacleMaterialHandler)

using System.Collections;
using UnityEngine;

public class ObstacleMaterialHandler : MonoBehaviour
{
    [SerializeField] Material highlightMat;
    private Material originalMat;
    private Renderer meshRenderer;
    private Coroutine currentRevertCoroutine;

    void Awake()
    {
        meshRenderer = GetComponent<Renderer>();
        originalMat = meshRenderer.material;
    }

    public void TriggerHighlight()
    {
        // 如果正在执行恢复协程,先停止(避免重复触发导致多次恢复)
        if (currentRevertCoroutine != null)
        {
            StopCoroutine(currentRevertCoroutine);
        }

        meshRenderer.material = highlightMat;
        currentRevertCoroutine = StartCoroutine(RevertToOriginalMaterial());
    }

    IEnumerator RevertToOriginalMaterial()
    {
        yield return new WaitForSeconds(2f);
        meshRenderer.material = originalMat;
        currentRevertCoroutine = null;
    }
}

这种方式的优势是逻辑解耦,每个Obstacle自己管理状态,还能处理重复碰撞的情况(比如小球短时间内多次撞同一个Obstacle,不会触发多个延迟恢复)。

注意点

  • 尽量用CompareTag代替==判断标签,性能更好且能避免拼写错误导致的问题。
  • 操作材质时,如果多个物体共享材质,建议使用material(实例化材质)而非sharedMaterial,避免影响其他物体。

内容的提问来源于stack exchange,提问作者andi arizal

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 07:40:02