Unity 实例化GameObject落地改Material 如何在Update中实时获取transform.position.y
问题根因定位
你之前的实时检测逻辑没有放在Update的常驻执行流里,大概率是把遍历列表、读取坐标的代码写在了鼠标左键点击的事件判断分支内部。这段代码只会在触发点击的那一帧执行一次,后续Cube受重力下落的过程中,因为不满足“点击左键”的触发条件,整个判断分支根本不会运行,自然无法拿到后续每帧的实时坐标,效果就和Start函数只跑一次没区别。
正确实现方式
核心调整原则
- 所有需要跨帧使用的变量(生成的Cube列表、地面高度阈值、红色材质引用)都要定义为类的全局成员,不要写在局部方法的临时作用域里
- 鼠标点击的分支逻辑只负责生成Cube、给随机初始位置、把生成的对象存入全局列表,绝对不要把落地检测、材质修改的代码嵌在这个分支内部
- 遍历列表、实时读取坐标的检测逻辑要和点击判断逻辑平级放在Update的固定执行流中,保证每帧都会执行一次,读取Cube当前帧的最新位置值
参考实现代码
public class CubeSpawnLogic : MonoBehaviour { // 全局跨帧使用的成员 public List<GameObject> spawnedCubeList = new List<GameObject>(); public float GroundLevel = 0f; public Material RedMat; public GameObject CubePrefab; // 生成范围参数 public Vector2 spawnXRange = new Vector2(-6f, 6f); public Vector2 spawnYRange = new Vector2(8f, 15f); void Update() { // 1. 点击左键仅做生成逻辑 if (Input.GetMouseButtonDown(0)) { for (int i = 0; i < 10; i++) { float posX = UnityEngine.Random.Range(spawnXRange.x, spawnXRange.y); float posY = UnityEngine.Random.Range(spawnYRange.x, spawnYRange.y); GameObject spawnedCube = Instantiate(CubePrefab, new Vector3(posX, posY, 0), Quaternion.identity); spawnedCubeList.Add(spawnedCube); } } // 2. 实时检测逻辑:和点击判断平级,每帧都会执行 // 用反向for循环遍历,方便后续需要销毁Cube时直接移除列表元素,避免迭代器报错 for (int i = spawnedCubeList.Count - 1; i >= 0; i--) { // 提前判空,避免Cube被销毁后出现空引用异常 if (spawnedCubeList[i] == null) { spawnedCubeList.RemoveAt(i); continue; } // 每帧读取当前Cube的实时y坐标做判断 float currentY = spawnedCubeList[i].transform.position.y; if (currentY < GroundLevel) { Renderer cubeRender = spawnedCubeList[i].GetComponent<Renderer>(); // 增加判断避免重复赋值材质的性能损耗 if (cubeRender.sharedMaterial != RedMat) { cubeRender.material = RedMat; } } } } }
常见踩坑提醒
- 写代码时注意大括号的闭合位置:如果遍历检测的代码块被闭合在
if (Input.GetMouseButtonDown(0))的大括号内部,就会回到之前只在点击帧执行的问题 - 读取坐标的时候不要缓存生成时的初始位置,直接每帧通过
transform.position获取物理引擎计算的最新值,才能拿到下落过程中的实时y坐标 - 遍历列表时如果要做移除元素的操作,不要用foreach语法,Unity的列表在foreach过程中修改集合会直接抛出迭代异常,反向for循环是最稳妥的遍历方式
内容的提问来源于stack exchange,提问作者vito
相关产品推荐
相关产品推荐

