Unity 3D中玩家网格定位与基于网格的移动实现问题
解决方案
1. 世界坐标与网格位置的转换逻辑
你的网格每个瓦片的间距是10单位(生成时用i*10和j*10计算世界坐标),所以可以通过简单的数学运算实现坐标互转:
世界坐标转网格位置
// 根据玩家世界坐标获取对应的网格(i,j) public Vector2Int GetGridPosition(Vector3 worldPos) { // 用RoundToInt取最近的网格坐标,若需严格判断当前所在网格可改用FloorToInt int gridI = Mathf.RoundToInt(worldPos.x / 10f); int gridJ = Mathf.RoundToInt(worldPos.z / 10f); // 确保坐标不超出网格范围 gridI = Mathf.Clamp(gridI, 0, height - 1); gridJ = Mathf.Clamp(gridJ, 0, width - 1); return new Vector2Int(gridI, gridJ); }
网格位置转世界坐标
// 根据网格(i,j)获取对应的世界坐标 public Vector3 GetWorldPosition(int i, int j) { return new Vector3(i * 10f, 0f, j * 10f); }
2. 整合到你的项目中
为了避免硬编码数值、方便脚本间共享网格数据,推荐给GridCreation添加单例模式,再编写玩家移动脚本实现WASD控制:
修改GridCreation脚本
using System.Collections; using System.Collections.Generic; using UnityEngine; public class GridCreation : MonoBehaviour { public static GridCreation Instance; // 单例,供其他脚本直接调用 public int height = 7; public int width = 7; public float tileSize = 10f; // 把瓦片间距设为变量,后续修改更方便 public GameObject prefabPlane; public GameObject prefabPlane2; void Awake() { Instance = this; } void Start() { GameObject newObject; for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) { newObject = Instantiate( (i + j) % 2 == 0 ? prefabPlane : prefabPlane2, GetWorldPosition(i, j), Quaternion.identity ); newObject.name = $"{i},{j}"; } } } // 对外暴露的坐标转换方法 public Vector3 GetWorldPosition(int i, int j) { return new Vector3(i * tileSize, 0f, j * tileSize); } public Vector2Int GetGridPosition(Vector3 worldPos) { int gridI = Mathf.RoundToInt(worldPos.x / tileSize); int gridJ = Mathf.RoundToInt(worldPos.z / tileSize); gridI = Mathf.Clamp(gridI, 0, height - 1); gridJ = Mathf.Clamp(gridJ, 0, width - 1); return new Vector2Int(gridI, gridJ); } }
玩家移动脚本示例
using UnityEngine; public class PlayerMovement : MonoBehaviour { public float moveSpeed = 6f; private Vector3 targetPos; private bool isMoving = false; void Start() { // 初始化玩家到当前所在网格的中心 Vector2Int startGrid = GridCreation.Instance.GetGridPosition(transform.position); targetPos = GridCreation.Instance.GetWorldPosition(startGrid.x, startGrid.y); transform.position = targetPos; } void Update() { if (isMoving) { // 平滑移动到目标位置 transform.position = Vector3.MoveTowards(transform.position, targetPos, moveSpeed * Time.deltaTime); // 到达目标后停止移动 if (Vector3.Distance(transform.position, targetPos) < 0.1f) { transform.position = targetPos; isMoving = false; } return; } // 处理WASD按键输入 if (Input.GetKeyDown(KeyCode.W)) MoveGrid(0, 1); else if (Input.GetKeyDown(KeyCode.S)) MoveGrid(0, -1); else if (Input.GetKeyDown(KeyCode.A)) MoveGrid(-1, 0); else if (Input.GetKeyDown(KeyCode.D)) MoveGrid(1, 0); } void MoveGrid(int deltaI, int deltaJ) { Vector2Int currentGrid = GridCreation.Instance.GetGridPosition(transform.position); int targetI = currentGrid.x + deltaI; int targetJ = currentGrid.y + deltaJ; // 边界检查:防止玩家移出网格 if (targetI >= 0 && targetI < GridCreation.Instance.height && targetJ >= 0 && targetJ < GridCreation.Instance.width) { targetPos = GridCreation.Instance.GetWorldPosition(targetI, targetJ); isMoving = true; } } }
关键说明
- 单例模式让
GridCreation的网格参数和转换方法可以被移动脚本直接调用,后续修改瓦片大小或网格尺寸时无需到处改代码。 - 移动时加入边界检查,避免玩家超出网格范围。
- 平滑移动用
Vector3.MoveTowards实现,若需瞬间移动可直接设置transform.position = targetPos。
内容的提问来源于stack exchange,提问作者Chris W.
相关产品推荐
相关产品推荐

