You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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;
}

问题分析

  1. GridManager核心逻辑缺失:MoveGameObjectTo方法只清空了方块的原网格位置,但未将方块注册到目标网格位置,导致_gameObjects[x,y]始终为null,IsTileOccupied无法正确判断目标格子状态。
  2. MovableObject位置更新错误:newX = currentX; newY = currentY;是将旧值覆盖新变量,应该反过来把新位置赋值给currentX和currentY,否则对象的当前位置记录完全错误。
  3. 冗余计算与边界判断不严谨:MoveToRightTile中调用MoveGameObjectTo时重复计算网格索引,且仅通过世界坐标判断边界,未验证网格索引是否在有效范围内,可能触发数组越界。
  4. 重复执行清空操作:_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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 22:57:43