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

Unity中如何将屏幕坐标映射到旋转后的Tilemap单个Tile?

解决Tilemap旋转后光标到单元格定位失效的问题

问题回顾

你遇到的核心问题是:当Tilemap沿X轴旋转实现伪3D透视后,WorldToCell()无法正确将触摸/光标世界坐标转换为Tile单元格坐标,只有同步旋转相机才能恢复功能,但会丢失透视效果。再明确下你的测试场景:

  • 正常状态:相机旋转0°,Tilemap旋转0°,所有定位功能正常
  • 伪3D状态:相机保持0°,Tilemap沿X轴转60°,透视效果生效,但Tile定位完全失效
  • 特殊验证:相机同步旋转60°(物理位置不动),定位功能恢复,但透视效果消失、画面偏移

问题根源

你猜的完全正确——Unity的Tilemap.WorldToCell()确实不会自动考虑Tilemap自身的旋转、缩放等Transform变换。它的转换逻辑仅基于Tilemap的网格原点和单元格大小,直接使用世界空间坐标计算,完全忽略了Tilemap的旋转状态,这就是旋转后定位失效的核心原因。

正确解决方案

要解决这个问题,我们需要手动将世界空间坐标转换到Tilemap的本地空间,再进行单元格转换。具体步骤和实现代码如下:

核心思路

  1. 通过射线检测获取触摸/光标在Tilemap平面上的世界坐标(避免手动调整Z轴的误差)
  2. 将世界坐标转换为Tilemap的本地坐标(消除旋转、位移带来的坐标偏移)
  3. 使用本地坐标计算对应的单元格位置

实现代码(推荐方案)

var touchPosition = getScreenPosition();
var ray = Camera.main.ScreenPointToRay(touchPosition);

// 创建基于Tilemap自身朝向的检测平面,适配任意旋转角度
Plane tilemapPlane = new Plane(-tilemap.transform.forward, tilemap.transform.position);
if (tilemapPlane.Raycast(ray, out float hitDistance))
{
    Vector3 worldPoint = ray.GetPoint(hitDistance);
    // 将世界坐标转换为Tilemap本地坐标,消除旋转影响
    Vector3 localPoint = tilemap.transform.InverseTransformPoint(worldPoint);
    // 此时本地坐标与Tilemap网格的对应关系和无旋转时一致,直接调用WorldToCell即可
    Vector3Int cellPosition = tilemap.WorldToCell(localPoint);
}

备选方案:手动计算单元格坐标

如果你想完全脱离WorldToCell()的依赖,也可以直接用本地坐标计算,逻辑更直观:

var touchPosition = getScreenPosition();
var ray = Camera.main.ScreenPointToRay(touchPosition);

Plane tilemapPlane = new Plane(-tilemap.transform.forward, tilemap.transform.position);
if (tilemapPlane.Raycast(ray, out float hitDistance))
{
    Vector3 worldPoint = ray.GetPoint(hitDistance);
    Vector3 localPoint = tilemap.transform.InverseTransformPoint(worldPoint);
    
    Vector3 cellSize = tilemap.cellSize;
    // 取整逻辑和Unity默认的WorldToCell保持一致
    Vector3Int cellPosition = new Vector3Int(
        Mathf.FloorToInt(localPoint.x / cellSize.x + 0.5f),
        Mathf.FloorToInt(localPoint.y / cellSize.y + 0.5f),
        Mathf.FloorToInt(localPoint.z / cellSize.z + 0.5f)
    );
}

关键细节说明

  • 为什么要转换到本地空间?:Tilemap的网格系统是基于自身本地坐标系设计的,旋转后世界坐标系和本地坐标系产生了角度偏移,只有转换到本地空间,坐标和网格的对应关系才会恢复到和无旋转时一致的状态。
  • 自定义检测平面:不再使用固定的Vector3.back创建平面,而是基于Tilemap的transform.forward,这样无论Tilemap旋转多少度,都能准确检测到它所在的平面。
  • 取整逻辑对齐:手动计算时的Mathf.FloorToInt(x + 0.5f)是为了和Unity默认的WorldToCell()取整逻辑保持一致,确保结果完全匹配。

对之前测试现象的解释

你之前发现"相机和Tilemap同步旋转后恢复正常",本质是因为此时相机的旋转抵消了Tilemap的旋转,导致世界坐标到Tilemap本地坐标的转换等价于无旋转的情况,所以WorldToCell()能正常工作,但代价是丢失了伪3D透视效果。

内容的提问来源于stack exchange,提问作者Matthew W

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.28 23:02:37