计算机图形学笔记——Tilemap中获取点击的瓦片坐标
Posted on 周二 29 十月 2019 in 计算机图形学
Tilemap中获取点击的瓦片坐标
[TOC]
原理
通过摄像机和屏幕空间中的点画一条射线,该射线与Tilemap相交的点可以认为是点击到的点。我们将其转换为一个数学问题,如图:
\(C\)为射线的起点,\(\vec m\)为射线的单位向量,\(O\)为平面上的一个点,\(\vec n\)为平面的单位法向量,求射线与平面的交点\(P\)。
推导过程:
$$
\vec {CP} = d \ast \vec m \tag 1
$$
因为\(\vec n\)为平面法向量,有:
$$
\vec {OP} \cdot \vec n = 0 \tag 2
$$
其中:
$$
\vec {OP} = \vec {OC} + \vec {CP} \tag 3
$$
结合上面三个等式可推导:
$$
(\vec {OC} + \vec {CP})\cdot \vec n = 0 \\
\vec {OC} \cdot \vec n + \vec {CP} \cdot \vec n = 0\\
\vec {OC} \cdot \vec n + d \ast \vec m \cdot \vec n = 0\\
d = - \frac{\vec {OC} \cdot \vec n}{\vec m \cdot \vec n}
$$
可得:
$$
P = \vec {OP} + O = \vec {OC} + \vec {CP} + O= \vec {OC} + d \ast \vec m + O= C -\frac{\vec {OC} \cdot \vec n}{\vec m \cdot \vec n} \cdot \vec m
$$
或:
$$
P = \vec {CP} + C = d \ast \vec m + C = C -\frac{\vec {OC} \cdot \vec n}{\vec m \cdot \vec n} \cdot \vec m
$$
代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class TestTileMap : MonoBehaviour
{
public Camera renderCamera;
public GameObject zero;
//tileMap的法向量
public Vector3 tileMapNormal;
private Grid _grid = null;
//tilemap上某个点的世界坐标, 设为O点
private Vector3 _O;
private Vector3 _C;
private Vector3 _n; //tilemap的法向量
private void Start()
{
_grid = GetComponent<Grid>();
}
void Update()
{
if (Input.GetMouseButtonDown(0))
{
//O点使用tilemap的世界坐标
_O = transform.position;
Debug.Log("O: " + _O);
//C点为摄像机
_C = renderCamera.transform.position;
Debug.Log("C: " + _C);
//手动设置的法向量
_n = tileMapNormal;
Debug.Log("n: " + _n);
Vector3 nearClipScreenPos = new Vector3(Input.mousePosition.x, Input.mousePosition.y, renderCamera.nearClipPlane);
Vector3 nearClipWorldPos = Camera.main.ScreenToWorldPoint(nearClipScreenPos);
//摄像机到点击位置的射线
Vector3 m = (nearClipWorldPos - _C).normalized;
Debug.Log("m: " + m);
//向量OC
Vector3 oc = _C - _O;
Debug.Log("oc: " + oc);
float d = Vector3.Dot(oc, _n) / Vector3.Dot(m, _n);
Debug.Log("d: " + d);
//向量OP
Vector3 op = oc - d * m;
Debug.Log("op: " + op);
//P点
Vector3 p = _O + op;
Debug.Log("p: " + p);
// 将世界坐标转换为瓦片坐标
Vector3Int cellPos = _grid.WorldToCell(p);
Debug.Log("cellPos:" + cellPos);
}
}
}