Unity2D推箱游戏网格系统对象重叠问题求助
Unity2D推箱游戏:方块重叠问题修复
我在Unity2D开发推箱游戏时,需要实现玩家推动方块时,方块不能和已被其他方块占据的格子重叠,但当前代码始终无法实现该功能,以下是关键代码片段:
MovableObject脚本
public class MovableObject : MonoBehaviour { private GridManager _gridManager; public float currentX; public float currentY; void MoveToRightTile(Transform protagonistTransform) { Debug.Log("Right"); float newX = transform.position.x + _gridManager._tileSize; float newY = transform.position.y; int targetGridX = _gridManager.GetGridX(newX); int targetGridY = _gridManager.GetGridY(newY); if(_gridManager.MinX < newX && newX < _gridManager.MaxX) { IsWithinGridBounds = true; } else { IsWithinGridBounds = false; } if (IsWithinGridBounds && canMove && !_gridManager.IsTileOccupied(targetGridX, targetGridY)) { Vector2 newPosition = new Vector2(newX, newY); transform.position = newPosition; // Update the grid position in the GridManager _gridManager.MoveGameObjectTo(this, _gridManager.GetGridX(newX), _gridManager.GetGridY(newY)); newX = currentX; newY = currentY; } }
GridManager脚本
public class GridManager : MonoBehaviour { [SerializeField] private int _width, _height; [SerializeField] private Tile _tilePrefab; [SerializeField] private GameObject _gameObjectPrefab; private GameObject[,] _gameObjects; public void RegisterObjectAt(MovableObject movableObject, int x, int y) { _gameObjects[x, y] = movableObject.gameObject; } public bool IsTileOccupied(int x, int y) { // Check if the specified tile is occupied return _gameObjects[x, y] != null; } public void MoveGameObjectTo(MovableObject movableObject, int x, int y) { int currentX = GetGridX(movableObject.transform.position.x); int currentY = GetGridY(movableObject.transform.position.y); // Check if the destination is valid (within grid bounds and unoccupied) if (x >= 0 && x < _width && y >= 0 && y < _height && _gameObjects[x, y] == null) { // Clear the current grid position _gameObjects[currentX, currentY] = null; MarkTileUnoccupied(currentX, currentY); // Update the grid position movableObject.UpdateGridPosition(); } } public void MarkTileUnoccupied(int x, int y) { _gameObjects[x, y] = null; }
问题分析
- GridManager核心逻辑缺失:
MoveGameObjectTo方法只清空了方块的原网格位置,但未将方块注册到目标网格位置,导致_gameObjects[x,y]始终为null,IsTileOccupied无法正确判断目标格子状态。 - MovableObject位置更新错误:
newX = currentX; newY = currentY;是将旧值覆盖新变量,应该反过来把新位置赋值给currentX和currentY,否则对象的当前位置记录完全错误。 - 冗余计算与边界判断不严谨:
MoveToRightTile中调用MoveGameObjectTo时重复计算网格索引,且仅通过世界坐标判断边界,未验证网格索引是否在有效范围内,可能触发数组越界。 - 重复执行清空操作:
_gameObjects[currentX, currentY] = null;和MarkTileUnoccupied(currentX, currentY);功能完全重复,无需执行两次。
修复后的代码
修改MovableObject的MoveToRightTile方法
void MoveToRightTile(Transform protagonistTransform) { Debug.Log("Right"); float newX = transform.position.x + _gridManager._tileSize; float newY = transform.position.y; int targetGridX = _gridManager.GetGridX(newX); int targetGridY = _gridManager.GetGridY(newY); // 通过网格索引直接判断边界,更准确可靠 IsWithinGridBounds = targetGridX >= 0 && targetGridX < _gridManager._width && targetGridY >= 0 && targetGridY < _gridManager._height; if (IsWithinGridBounds && canMove && !_gridManager.IsTileOccupied(targetGridX, targetGridY)) { Vector2 newPosition = new Vector2(newX, newY); transform.position = newPosition; // 使用已计算的网格索引,避免重复计算 _gridManager.MoveGameObjectTo(this, targetGridX, targetGridY); // 正确更新对象记录的当前位置 currentX = newX; currentY = newY; } }
修改GridManager的MoveGameObjectTo方法
public void MoveGameObjectTo(MovableObject movableObject, int x, int y) { int currentX = GetGridX(movableObject.transform.position.x); int currentY = GetGridY(movableObject.transform.position.y); if (x >= 0 && x < _width && y >= 0 && y < _height && _gameObjects[x, y] == null) { // 清空原网格位置 _gameObjects[currentX, currentY] = null; // 关键步骤:将对象注册到目标网格位置 _gameObjects[x, y] = movableObject.gameObject; // 更新对象自身的网格位置记录 movableObject.UpdateGridPosition(); } }
额外注意事项
- 确保GridManager初始化时已创建
_gameObjects数组,例如在Awake方法中执行_gameObjects = new GameObject[_width, _height];,否则会触发空引用异常。 - 验证
GetGridX和GetGridY方法的坐标转换逻辑是否正确,这是网格判断的基础,转换错误会导致所有格子状态判断失效。
内容的提问来源于stack exchange,提问作者Mr. Slime
相关产品推荐
相关产品推荐

