# 前言
前些天做大地图用到过一次,要求点大地图哪儿,就把玩家传送至点击的相应世界位置。而且这张大地图是事先截取好的图片。
要说来的话,这功能也还是 “前任” 留下来的,新公司之前的程序走掉之后,项目就留在那儿了,我来之后,才又开工的样子。
本来以为这地图应该稍稍改改就好了,结果鼠标点上去图标乱飞,人也不见了踪影....
定睛一看,不但地图用的是 UE4 项目中截取的(这项目之前公司还用 UE4 做了一版,不过说是因为有些功能实现不了,才又换到 Unity 的),其中代码注意了下,也是 UE4 中类似的算法提过来的样子。
本以为应该很简单,很快就改好了.... 不过之后大概地点没问题,但是无论怎么改,地图上都有些许误差,看起来就是点了地图上某个位置,但是实际上却偏移了一段。
然后新公司也就我一个程序,只好自个儿琢磨了。
后来折腾了些时候,考虑着:先不说 UE4 中截取的地图跟 Unity 中单位兼不兼容,单单是之前截取地图的比例我也不知道。
便思虑着是否确实是地图比例 (长宽,是否对得上本身的分辨率?) 不对?
于是就想到还是用 Unity 本身来截取大地图试试。
把之前代码里边定义了许多固定坐标、世界长度 (貌似其实不对) 之类的都删了,自个儿重新来,我采用了最简单的一种按比例计算的方式实现,这儿就稍作记录吧。
# 功能
# 描述
我所做的大地图,是事先使用一个 orthographic 相机对场景进行截图,然后保存为一张 PNG 格式的图片作为大地图。
所以在此之前,我们必须要明白一点:orthographicSize。
在 Unity 正交相机模式下,orthographicSize 一个单位等于 Unity 世界 2 个单位,不过仅限于纵向 —— 也就是说纵向的可以显示的范围 = orthographicSize*2。
比如说 orthographicSize=360,那么相机实际可以显示的场景中 720 米的范围 (纵向)。
而横向显示长度,则是据此并与屏幕长宽比例相关。
知道了这一点的话,那么就可以根据这个比例关系,对使用 orthographic 相机截取的地图进行定位了(稍微说一下:因此,若想保持比例正确,且少费功夫,其纵向长度必须保持上述比例,而宽度,则可根据比例 (地图分辨率、屏幕分辨率) 自行换算)。
之前就说了,我是用的最简单的方式 —— 所以不考虑更多的比例关系,主要就是将鼠标点击的位置转换至 UI 中,因为图片、UI 比例一致,所以鼠标在 UI 中的坐标,就可以直接被当做大地图中的实际坐标了。
不大清楚的话,可以这样考虑:UI 的纵向长度为 720,同时代表了 720 米的距离,UI 横向长度为 1024,代表了 1024 米的距离。
地图如下:
# 实现
在我做的项目中,UI 显示的大小为 1024x720,所以截图也要根据这个比例来,为了增加清晰度,截图放大了一倍,即 2048x1440。
orthographicSize 则以实际 UI 的比例为主,所以 orthographicSize=720/2=360.
照上述所言,之前就说了是很简单的方法了,所以定位就很简单了。
主要就是将鼠标点击的位置转至以屏幕中心为准,这样子的话,那么地图比例依照上述,点击位置距离中心点有多远,便可依样转换至世界坐标距离原点 Vector3 (0,0,0) 的坐标。
代码:
// 将点击位置映射至中点 | |
_clickTargetPosition = new Vector2(Screen.width / 2.0f, Screen.height / 2.0f) - _clickMousePos; | |
// 三维转二维 | |
_clickTargetPosition.z = _clickTargetPosition.y; | |
//UI 比例缩放 | |
_clickTargetPosition /= _canvas.scaleFactor; | |
_clickTargetPosition = new Vector3(_clickTargetPosition.x, 70, _clickTargetPosition.z); |
接下来是截图。
截图代码:
[MenuItem("Tools/Capture BigMap")] | |
private static void CaptureBigMap() | |
{ | |
Camera ca = new GameObject("[Camera]").AddComponent<Camera>(); | |
ca.orthographic = true; | |
ca.orthographicSize = 360; | |
ca.cullingMask &= ~(1 << LayerMask.NameToLayer("Pipe")); | |
ca.backgroundColor = Color.black; | |
ca.clearFlags = CameraClearFlags.SolidColor; | |
ca.transform.position = new Vector3(0, 500, 0); | |
ca.transform.eulerAngles = new Vector3(90, 180, 0); | |
CaptureCamera(ca, new Rect(0, 0, 2048, 1440), Application.dataPath + "/../BigMap.png"); | |
DestroyImmediate(ca.gameObject); | |
} | |
private static void CaptureCamera(Camera camera, Rect rect, string fileName) | |
{ | |
RenderTexture rt = new RenderTexture((int)rect.width, (int)rect.height, -1); | |
RenderTexture.active = rt; | |
camera.targetTexture = rt; | |
camera.Render(); | |
Texture2D screenShot = new Texture2D((int)rect.width, (int)rect.height, TextureFormat.RGB24, false); | |
screenShot.ReadPixels(rect, 0, 0); | |
screenShot.Apply(); | |
RenderTexture.active = null; | |
byte[] bytes = screenShot.EncodeToPNG(); | |
System.IO.File.WriteAllBytes(fileName, bytes); | |
Debug.Log("截图完成:" + fileName); | |
} |
需要注意的一点是,截图大小是多少,同时也必须把屏幕的比例设置相同,当然最好、或者说最简单的方法就是直接将屏幕的分辨率设置为截图大小。
如图:
# 结语
这样,按照比例做的图片地图就好了。
因为地图与世界坐标比例基本一致,点哪儿传送至哪儿完全没问题。