Unity方块拼图游戏Bug:方块莫名消失问题求助
方块拼图游戏Bug求助:移动左侧/中间方块时右侧方块随机消失
问题描述
跟着教程开发方块拼图游戏,其余功能正常,仅存在一个其他用户也遇到的问题:
- 每次生成新方块后,约半数场景下,先移动中间或左侧方块,右侧方块会随之消失;另一半场景下右侧方块正常显示。
- 有用户推测原因是方块未回到初始位置,放置中间方块时,检测到右侧方块不在初始位置,便将其禁用。尝试过其他用户的解决方案无效,自行调试2小时后问题更严重,求解决思路。
问题截图
- 问题前状态:

- 问题后状态:

相关代码
Shape类
public class Shape : MonoBehaviour, IPointerClickHandler, IPointerUpHandler, IBeginDragHandler, IDragHandler, IEndDragHandler, IPointerDownHandler { public GameObject squareShapeImage; public Vector3 shapeSelectedScale; public Vector2 offset = new Vector2(0f, 700f); [HideInInspector] public ShapeData CurrentShapeData; public int TotalSquareNumber { get; set; } private List<GameObject> _currentShape = new List<GameObject>(); private Vector3 _shapeStartScale; private RectTransform _transform; private bool _shapeDraggable = true; private Canvas _canvas; private Vector3 _startPosition; private bool _shapeActive = true; public void Awake() { _shapeStartScale = this.GetComponent<RectTransform>().localScale; _transform = this.GetComponent<RectTransform>(); _canvas = GetComponentInParent<Canvas>(); _shapeDraggable = true; _startPosition = _transform.localPosition; _shapeActive = true; } private void OnEnable() { GameEvents.MoveShapeToStartPosition += MoveShapeToStartPosition; GameEvents.SetShapeInactive += SetShapeInactive; } private void OnDisable() { GameEvents.MoveShapeToStartPosition -= MoveShapeToStartPosition; GameEvents.SetShapeInactive -= SetShapeInactive; } public bool IsOnStartPosition() { return _transform.localPosition == _startPosition; } public bool IsAnyOfShapeSquareActive() { foreach (var square in _currentShape) { if (square.gameObject.activeSelf) { return true; } } return false; } public void DeactivateShape() { if (_shapeActive) { foreach (var square in _currentShape) { square?.GetComponent<ShapeSquare>().DeactivateShape(); } } _shapeActive = false; } private void SetShapeInactive() { if (IsOnStartPosition() == false && IsAnyOfShapeSquareActive()) { foreach (var square in _currentShape) { square.gameObject.SetActive(false); } } } public void ActivateShape() { if (!_shapeActive) { foreach (var square in _currentShape) { square?.GetComponent<ShapeSquare>().ActivateShape(); } } _shapeActive = true; } public void RequestNewShape(ShapeData shapeData) { _transform.localPosition = _startPosition; CreateShape(shapeData); } public void CreateShape(ShapeData shapeData) { CurrentShapeData = shapeData; TotalSquareNumber = GetNumberOfSquares(shapeData); while (_currentShape.Count <= TotalSquareNumber) { _currentShape.Add(Instantiate(squareShapeImage, transform) as GameObject); } foreach (var square in _currentShape) { square.gameObject.transform.position = Vector3.zero; square.gameObject.SetActive(false); } var squareRect = squareShapeImage.GetComponent<RectTransform>(); var moveDistance = new Vector2(squareRect.rect.width * squareRect.localScale.x, squareRect.rect.height * squareRect.localScale.y); int currentIndexInList = 0; // set positions to form a final shape for (var row = 0; row < shapeData.rows; row++) { for (var column = 0; column < shapeData.columns; column++) { if (shapeData.board[row].column[column]) { _currentShape[currentIndexInList].SetActive(true); _currentShape[currentIndexInList].GetComponent<RectTransform>().localPosition = new Vector2(GetXPositionForShapeSquare(shapeData, column, moveDistance), GetYPositionForShapeSquare(shapeData, row, moveDistance)); currentIndexInList++; } } } } public void OnBeginDrag(PointerEventData eventData) { this.GetComponent<RectTransform>().localScale = shapeSelectedScale; } public void OnDrag(PointerEventData eventData) { _transform.anchorMin = new Vector2(0, 0); _transform.anchorMax = new Vector2(0, 0); _transform.pivot = new Vector2(0.5f, 0.5f); Vector2 pos; RectTransformUtility.ScreenPointToLocalPointInRectangle(_canvas.transform as RectTransform, eventData.position, Camera.main, out pos); _transform.localPosition = pos + offset; } public void OnEndDrag(PointerEventData eventData) { this.GetComponent<RectTransform>().localScale = _shapeStartScale; GameEvents.CheckIfShapeCanBePlaced(); } public void MoveShapeToStartPosition() { _transform.transform.localPosition = _startPosition; } }
Grid类
public class Grid : MonoBehaviour { public ShapeStorage shapeStorage; public int columns = 0; public int rows = 0; public float squaresGap = 0.1f; public GameObject gridSquare; public Vector2 startPosition = new Vector2(0.0f, 0.0f); public float squareScale = 0.5f; public float everySquareOffset = 0.0f; private Vector2 _offset = new Vector2(0.0f, 0.0f); private List<GameObject> _gridSquares = new List<GameObject>(); private void CheckIfShapeCanBePlaced() { var squareIndexes = new List<int>(); foreach (var square in _gridSquares) { var gridSquare = square.GetComponent<GridSquare>(); if (gridSquare.Selected && !gridSquare.SquareOccupied) { squareIndexes.Add(gridSquare.SquareIndex); gridSquare.Selected = false; //gridSquare.ActivateSquare(); } } var currentSelectedShape = shapeStorage.GetCurrentSelectedShape(); if (currentSelectedShape == null) return; //There is no selected shape; if (currentSelectedShape.TotalSquareNumber == squareIndexes.Count) { foreach (var squareIndex in squareIndexes) { _gridSquares[squareIndex].GetComponent<GridSquare>().PlaceShapeOnBoard(); } var shapeLeft = 0; foreach (var shape in shapeStorage.shapeList) { if (shape.IsOnStartPosition() && shape.IsAnyOfShapeSquareActive()) { shapeLeft++; } } var counter = shapeLeft; if (shapeLeft == 0) { GameEvents.RequestNewShapes(); } else { GameEvents.SetShapeInactive(); } } else { GameEvents.MoveShapeToStartPosition(); } } }
GameEvents类
public class GameEvents : MonoBehaviour { public static Action CheckIfShapeCanBePlaced; public static Action MoveShapeToStartPosition; public static Action RequestNewShapes; public static Action SetShapeInactive; }
ShapeStorage类
public class ShapeStorage : MonoBehaviour { public List<ShapeData> shapeData; public List<Shape> shapeList; private void OnEnable() { GameEvents.RequestNewShapes += RequestNewShapes; } private void OnDisable() { GameEvents.RequestNewShapes -= RequestNewShapes; } void Start() { foreach(var shape in shapeList) { var shapeIndex = UnityEngine.Random.Range(0, shapeData.Count); shape.CreateShape(shapeData[shapeIndex]); } } public Shape GetCurrentSelectedShape() { foreach (var shape in shapeList) { if (shape.IsOnStartPosition() == false && shape.IsAnyOfShapeSquareActive()) { return shape; } } Debug.LogError("No shape selected"); return null; } private void RequestNewShapes() { foreach (var shape in shapeList) { var shapeIndex = UnityEngine.Random.Range(0, shapeData.Count); // exclusive shape.RequestNewShape(shapeData[shapeIndex]); } } }
排查与解决思路
修复初始位置判断的浮点精度问题
当前用_transform.localPosition == _startPosition直接比较向量,UI布局的微小浮点偏差会导致判断错误。改成基于距离的模糊判断:public bool IsOnStartPosition() { // 0.01f为容错阈值,可根据UI精度调整 return Vector3.Distance(_transform.localPosition, _startPosition) < 0.01f; }验证SetShapeInactive的误触发情况
在SetShapeInactive()方法中添加日志,打印每个Shape的位置判断结果,确认是否有本该在初始位置的Shape被误判:private void SetShapeInactive() { bool isOnStart = IsOnStartPosition(); bool hasActiveSquares = IsAnyOfShapeSquareActive(); Debug.Log($"Shape {gameObject.name} - IsOnStart: {isOnStart}, HasActiveSquares: {hasActiveSquares}"); if (!isOnStart && hasActiveSquares) { foreach (var square in _currentShape) { square.gameObject.SetActive(false); } } }确保新生成方块的位置重置彻底
在RequestNewShape()中添加强制更新RectTransform的逻辑,避免UI布局延迟导致位置偏差:public void RequestNewShape(ShapeData shapeData) { _transform.localPosition = _startPosition; // 强制刷新UI布局,确保位置立即生效 _transform.ForceUpdateRectTransforms(); CreateShape(shapeData); // 重置激活状态,避免旧状态影响新生成的方块 _shapeActive = true; }检查事件订阅与状态同步
确认每个Shape实例在OnEnable()和OnDisable()中正确订阅/取消订阅事件,避免重复订阅导致逻辑多次触发。同时,新生成方块时确保_shapeActive状态被正确重置为true。
内容的提问来源于stack exchange,提问作者Andrei Vechar
相关产品推荐
相关产品推荐

