Unity3D脚本重构后方块无法移动的问题排查与原因分析
你在重构WallCreation脚本时遇到的方块不移动问题,根源其实是C#里引用类型传递的特性在搞鬼——这个坑很容易被忽略,毕竟控制台连报错都没有,排查起来确实头疼。
问题复盘
原本的代码能正常运行,是因为你在给每个WallMovement脚本赋值indizes数组后,立刻调用了initializeMovement(),此时数组里的索引值还是当前循环的正确值。但重构后,你把赋值和初始化移动逻辑拆分到了两个不同的方法(newWallMovement和wallMoving),而且所有方块共享同一个indizes数组实例,这就出问题了:
在newWallMovement的循环里,你把同一个indizes数组赋值给所有WallMovement的indizes字段。由于数组是引用类型,这相当于所有方块都指向内存里的同一个数组对象。当循环继续执行,你不断修改indizes里的x、y、z值时,所有方块的indizes都会同步变化。等你在wallMoving里调用initializeMovement()时,所有方块拿到的都是循环结束后的最终数组值,自然没法按照预期的顺序移动。
解决方案
解决这个问题的核心是让每个方块拥有独立的索引数据,不再共享同一个数组实例,有两种简单的实现方式:
方案1:传递数组的副本而非引用
在给WallMovement赋值indizes时,创建一个数组的拷贝,这样每个方块都会拿到属于自己的数组:
void newWallMovement() { for (int x = 1; x < oldWallsizeX-1; x++) { indizes[0] = x; for (int y = 0; y < oldWallsizeY; y++) { indizes[1] = y; for (int z = 0; z < oldWallsizeZ; z++) { indizes[2] = z; newWall = wallSegments[x,y,z]; GameObject _wallSegment = newWall.gameObject; WallMovement _WallMovement = _wallSegment.GetComponent<WallMovement>(); // 克隆数组,传递副本而非引用 _WallMovement.indizes = (int[])indizes.Clone(); } } } }
之后在wallMoving里直接调用initializeMovement()就可以了,每个方块的数组都是独立的,不会被后续循环修改影响。
方案2:改用独立的索引字段(更推荐)
既然索引是三个独立的整数,完全可以不用数组,直接在WallMovement脚本里定义三个单独的字段,这样从根源上避免引用类型的问题:
首先修改WallMovement脚本:
public class WallMovement : MonoBehaviour { // 替换原来的indizes数组为独立字段 public int xIndex; public int yIndex; public int zIndex; public void initializeMovement() { // 这里用xIndex、yIndex、zIndex来处理移动逻辑 // 比如原来的indizes[0]替换成xIndex即可 } }
然后在newWallMovement里赋值:
void newWallMovement() { for (int x = 1; x < oldWallsizeX-1; x++) { for (int y = 0; y < oldWallsizeY; y++) { for (int z = 0; z < oldWallsizeZ; z++) { newWall = wallSegments[x,y,z]; GameObject _wallSegment = newWall.gameObject; WallMovement _WallMovement = _wallSegment.GetComponent<WallMovement>(); // 直接赋值独立字段,值类型传递不会有共享问题 _WallMovement.xIndex = x; _WallMovement.yIndex = y; _WallMovement.zIndex = z; } } } }
这种方式更直观,代码可读性更高,也不容易再出现类似的引用传递问题。
总结
这个问题的本质是对C#中值类型和引用类型的区别理解不够深入:值类型(比如int、float)赋值时传递的是数据副本,而引用类型(比如数组、自定义类)传递的是内存地址,多个变量会指向同一个实例。只要让每个方块拥有独立的索引数据,就能解决这个移动失效的问题。
内容的提问来源于stack exchange,提问作者ZenBreaker

