Unity小地圖的實現(xiàn)
游戲好港,特別是RPG類型裁僧,基本都需要添加小地圖功能來輔助玩家铺坞。
實現(xiàn)小地圖需解決的問題:
- 小地圖(貼圖)從哪里來惯悠?
- 如何把場景中單位的坐標(biāo)轉(zhuǎn)換到小地圖上
- 小地圖視角怎么跟玩家視角一致
思路:
- 讓美術(shù)垂直90°方向以正交視角俯視整個場景,然后截取場景輸出成圖片捉邢,截取的大小讓美術(shù)設(shè)置脯丝,一般不超過2048*2048
- 保存截圖時的矩陣
- 通過第2點保存的矩陣,在游戲運行時把場景中單位的世界坐標(biāo)轉(zhuǎn)換到小地圖下的局部坐標(biāo)
實現(xiàn):
- 根據(jù)設(shè)置(這里寫死了800*600)伏伐,設(shè)定截圖窗口的大小宠进,同時Camera視角校準(zhǔn)為玩家視角,我這里是直接使用Scene窗口
if (GUILayout.Button("調(diào)整截圖窗口大小"))
{
var windowSize = Vector2(800,600);
var lastSceneView = SceneView.lastActiveSceneView;
// 場景的Main Camera
var mainCamera = UnityEngine.Camera.main;
// SmoothFollow是鏡頭跟蹤的組件藐翎,這里知道SmoothFollow里有個rotationAngle字段是記錄玩家視角即可
XS.SmoothFollow smoothFollow = null;
if (mainCamera)
{
smoothFollow = mainCamera.GetComponent<XS.SmoothFollow>();
}
if (smoothFollow == null)
{
return;
}
lastSceneView.position = new Rect(Vector3.zero, windowSize);
Quaternion currentRotation = Quaternion.Euler(90, smoothFollow.rotationAngle, 0);
lastSceneView.LookAt(Vector3.zero, currentRotation, lastSceneView.size, true);
lastSceneView.isRotationLocked = true;
}
- 截圖時材蹬,以Scene的大小為圖片寬高,并預(yù)先算出此時的world2Screen矩陣和screen2World矩陣吝镣,用于后面的邏輯運算
if (GUILayout.Button("截取小地圖"))
{
var lastSceneView = SceneView.lastActiveSceneView;
var sceneCamera = lastSceneView.camera;
if (!sceneCamera.orthographic)
{
return;
}
var width = sceneCamera.pixelWidth;
var height = sceneCamera.pixelHeight;
var cameraPos = sceneCamera.transform.position;
var camreaSize = sceneCamera.orthographicSize;
Matrix4x4 world2Screen = sceneCamera.projectionMatrix * sceneCamera.worldToCameraMatrix;
Matrix4x4 screen2World = world2Screen.inverse;
RenderTexture sceneCameraRT = RenderTexture.GetTemporary((int)width, (int)height, 32, RenderTextureFormat.ARGBFloat);
sceneCamera.targetTexture = sceneCameraRT;
sceneCamera.Render();
sceneCamera.targetTexture = null;
// targetData是用于保存數(shù)據(jù)組件堤器,掛載在場景的一個空Object上
string sceneName = targetData.gameObject.scene.name;
var path = EditorUtility.SaveFilePanel("Save SmallMap", "", sceneName, "png");
if (!string.IsNullOrEmpty(path))
{
if (SaveRenderTextureToPNG(sceneCameraRT, path))
{
targetData.smallMapCameraCenter = cameraPos;
targetData.smallMapCameraSize = camreaSize;
targetData.smallMapWorldToScreenMatrix = world2Screen;
targetData.smallMapScreenToWorldMatrix = screen2World;
targetData.smallMapCutSize = new Vector2(width, height);
UnityEditor.EditorUtility.SetDirty(targetData);
UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty(targetData.gameObject.scene);
}
else
{
Debug.LogError("Save RenderTexture to file error");
}
}
RenderTexture.ReleaseTemporary(sceneCameraRT);
}
- 根據(jù)第2點保存的兩個矩陣和圖片大小,通過以下接口(Lua實現(xiàn))即可完成坐標(biāo)轉(zhuǎn)換末贾,里面涉及的數(shù)學(xué)知識請自行查閱闸溃。
-- 世界坐標(biāo)轉(zhuǎn)圖片的局部坐標(biāo)
function manualWorldToScreenPoint(world2Screen, x, z, pixelWidth, pixelHeight)
local temp = world2Screen * UnityEngine.Vector4(x, 10, z, 1.0)
if temp.w == 0 then
return UnityEngine.Vector3.zero
else
temp.x = (temp.x/temp.w + 1)*0.5 * pixelWidth
temp.y = (temp.y/temp.w + 1)*0.5 * pixelHeight
return temp.x, temp.y
end
end
-- 圖片的局部坐標(biāo)轉(zhuǎn)世界坐標(biāo)
function manualScreenPointToWorld(screen2World, x, y, pixelWidth, pixelHeight)
local in_x = 2.0 * (x / pixelWidth) - 1.0
local in_y = 2.0 * (y / pixelHeight) - 1.0
local in_z = 10
local in_w = 1.0
local pos = screen2World * UnityEngine.Vector4(in_x,in_y,in_z,in_w)
if pos.w == 0 then
return UnityEngine.Vector3.zero
else
pos.w = 1.0 / pos.w
pos.x = pos.x*pos.w
pos.y = pos.y*pos.w
pos.z = pos.z*pos.w
return pos.x, pos.z
end
end
額外:
小地圖一般需要添加小地圖尋路功能,這里我使用的方案是給小地圖添加PointerClick事件拱撵,根據(jù)點擊的坐標(biāo)點轉(zhuǎn)換成世界坐標(biāo)點辉川,然后從在一點(當(dāng)然是上移到場景上空)通過射線往下檢測,得到與路面(路面需要設(shè)置特定Layer)的交點拴测,這個交點就是目標(biāo)點乓旗,接下來就是UnityEngine.AI.NavMeshAgent的事情了。