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
相关产品推荐
相关产品推荐

