Unity C#开发魔方游戏:如何避免列表重复添加同一元素?
我看你遇到的问题核心在于自定义node类的List.Contains()方法没有按预期工作,导致同一逻辑下的元素被重复添加到列表里。这背后的原因和C#中自定义类型的默认比较规则有关,咱们一步步来解决:
为什么Contains()会失效?
C#里的List.Contains()默认使用对象的引用相等性来判断是否存在该元素。而你在Update()的三重循环里,每一帧都new node(...)创建全新的实例——哪怕两个node的InRubikCubePos完全一样,它们也是内存中不同的对象,所以Contains()会返回false,自然就会重复添加。
解决方案1:重写node类的Equals和GetHashCode方法
既然InRubikCubePos是每个魔方块的唯一标识(比如(1,1,1)这种位置不会重复),我们可以基于这个字段来定义两个node对象相等的规则:
修改你的node类:
public class node{ public Vector3 InRubikCubePos; public Vector3 inWorldPos; public node(Vector3 IRCPos, Vector3 IWPos) { InRubikCubePos = IRCPos; inWorldPos = IWPos; } // 重写Equals方法,基于InRubikCubePos比较 public override bool Equals(object obj) { if (obj is node otherNode) { // Vector3是值类型,可以直接用==比较 return InRubikCubePos == otherNode.InRubikCubePos; } return false; } // 必须同时重写GetHashCode,和Equals逻辑保持一致 public override int GetHashCode() { // 用InRubikCubePos的哈希值作为当前对象的哈希码 return InRubikCubePos.GetHashCode(); } }
这样修改后,List.Contains()就会基于InRubikCubePos的值来判断元素是否存在,而不是引用了。
解决方案2:不依赖Contains(),直接通过位置判断
如果你不想修改node类,也可以在添加/移除元素时,直接检查列表中是否存在InRubikCubePos匹配的元素:
比如处理topYFaces的逻辑可以改成这样:
// 替换原来的if (!topYFaces.Contains(Node_)) topYFaces.Add(Node_); if (!topYFaces.Any(n => n.InRubikCubePos == Node_.InRubikCubePos)) { topYFaces.Add(Node_); }
移除的时候同理:
// 替换原来的if (topYFaces.Contains(Node_)) topYFaces.Remove(Node_); var existingNode = topYFaces.FirstOrDefault(n => n.InRubikCubePos == Node_.InRubikCubePos); if (existingNode != null) { topYFaces.Remove(existingNode); }
这种方式不需要修改node类,但每次判断都会遍历列表,性能上和Contains()差不多,适合小规模列表(你的魔方最多27个元素,完全没问题)。
额外优化:避免每一帧都重新创建nodes数组
你现在在Update()里每一帧都new node[3,3,3]并创建27个新node实例,这会产生大量不必要的GC垃圾,长期运行可能导致卡顿。建议把nodes的初始化移到Start()或Awake()里,只在魔方位置变化时(比如旋转后)更新inWorldPos,而不是每一帧都重建整个数组:
private node[,,] nodes; void Start() { // 只初始化一次nodes数组 nodes = new node[3, 3, 3]; Vector3 cubeButtonLeftDown = new Vector3((size / 2 * -1) + (size / 3), (size / 2 * -1) + (size / 3) , (size / 2 * -1) + (size / 3)); for (int x = -1; x <= 1; x++) { for (int y = -1; y <= 1; y++) { for (int z = -1; z <= 1; z++) { node Node = new node(new Vector3(x, y, z), new Vector3 (x * size / 6, y * size / 6, z * size / 6)); nodes[x + 1, y + 1, z + 1] = Node; } } } } void Update() { // 只在需要的时候更新node的inWorldPos,比如魔方旋转后更新位置... // 后续的面判断逻辑保持不变(或者用上面的解决方案修改) }
这样既能解决重复添加的问题,又能优化性能,一举两得~
内容的提问来源于stack exchange,提问作者Yacine_Dev_Artist

