Unity3D —— 實時PVP小地圖實現
阿新 • • 發佈:2019-02-06
前言
在很多實時PVP對戰遊戲(如:英雄聯盟、王者榮耀等)的戰鬥場景中,都會有一個小地圖,用於實時地顯示一些比較重要因素,例如:隊友和對手位置、存活炮塔位置、Boss出生死亡情況等。
一、方案分析:
實現小地圖的方案一般可以分成兩種:
- 直接加一個子相機,對映當前場景中所有的物體,簡單粗暴;
- 用UI建立一個假地圖,然後將需要顯示在小地圖上的物體,經過位置換算得到的小地圖座標,然後在小地圖中建立與每個物體對應的圖示,並實時更新每個圖示的狀態和位置;
方案一:
第一種方案其實很簡單,只需要在場景中加多一個相機和一個Render Texture即可實現,具體的實現步驟可以參考這個案例: Unity3d中使用攝像機制作實時顯示小地圖
方案二:
這個方案顯然要更加複雜一些,但是更加符合需求,因為有時候我們通過小地圖不是想看到當前地圖的所有物體,而只是想看到一些關鍵的資訊,所以通過UI平面簡化顯示的方式其實更為直觀,例如:英雄都只用一個圓形的頭像來代表,而炮臺也只是一個圖示,對手的位置只有在特定條件下才會顯示等。
二、可用外掛:
Unity有許多功能強大的外掛,關於小地圖的實現也有一些外掛:KGFMapSystem
和NJG MiniMap
,都能夠快速開發出一個可用的地圖,具體使用方式可以參考:
三、從0實現小地圖:
1.思路:
小地圖說到底,其實就是一張背景圖片,上邊有一些代表不同遊戲物體的小點或者是圖示,然後根據當前個個點所代表物體的變化改變這些點的狀態。
2.實現步驟:
- 建立每個型別物體對應的點預設,最好使用一個預設體
MapPoint.prefab
可以相容建立所有型別的點(因為通常只是UISprite圖示在變化),這裡我以只帶一個UISprite的為例;
- 遍歷需要顯示在小地圖上的物體,並在小地圖中使用
MapPoint.prefab
預設建立對應的點,用列舉列出所有型別:
public enum PointType
{
MySoldier, //我方特種兵
OppoSoldier,//對手特種兵
Boss, //野怪
LeftTower, //左邊塔
RightTower, //右邊塔
MyAISoldier,//我方AI小兵
OppoAISoldier//地方AI小兵
}
這裡需要按照型別,進行分類建立和設定:
/// <summary>
/// 建立不同型別的點
/// </summary>
/// <param name="pos"></param>
/// <param name="name"></param>
/// <param name="_type"></param>
/// <returns></returns>
public GameObject BuildPointByType(Vector3 pos,string name,PointType _type)
{
GameObject item = GameObject.Instantiate(mapPointPrefab) as GameObject;
item.SetActive(true);
item.transform.SetParent(transform);
item.transform.localPosition = GetMapPositionByWorldV3(pos);
item.transform.localScale = new Vector3(1, 1, 1);
item.transform.localRotation = Quaternion.Euler(0f, 180f, 135f);
item.name = name;
UISprite sprite = item.GetComponent<UISprite>();
switch (_type)
{
case PointType.Boss:
sprite.spriteName = "hpring";
break;
case PointType.LeftTower:
sprite.spriteName = "TurretLeft";
break;
case PointType.RightTower:
sprite.spriteName = "TurretRight";
break;
case PointType.MySoldier:
sprite.spriteName = "SoldierLeft";
break;
case PointType.OppoSoldier:
sprite.spriteName = "SoldierRight";
break;
case PointType.MyAISoldier:
sprite.spriteName = "point";
item.transform.localScale = new Vector3(0.3f, 0.3f, 0.3f);
sprite.color = BattleDataCenter.Instance.GetColor(BattleDataCenter.Instance.CtrledPlayerFactionId);
break;
case PointType.OppoAISoldier:
sprite.spriteName = "point";
item.transform.localScale = new Vector3(0.3f, 0.3f, 0.3f);
sprite.color = ColorTool.GetColorFromIndex(4);
break;
}
return item;
}
這裡有一個比較關鍵的方法,就是座標轉換方法GetMapPositionByWorldV3
,這是將一個3D真實地圖中的一個Vector3的3維座標,對映得到小地圖中的一個Vector2二維座標點,通常只是要做等比縮放即可:
/// <summary>
/// 將大地圖上的座標轉化為小地圖上的座標
/// </summary>
/// <param name="pos"></param>
/// <returns></returns>
public Vector2 GetMapPositionByWorldV3(Vector3 pos)
{
return new Vector2(pos.x*0.8f, -pos.z*0.8f);
}
- 使用一個字典
Dictionary<string,GameObject>
來儲存已經創建出來的點(GameOject)
/// <summary>
/// 將點新增到字典中方便管理
/// </summary>
/// <param name="go"></param>
private void AddPointToDic(GameObject go)
{
if (!pointDic.ContainsKey(go.name))
{
pointDic.Add(go.name, go);
}else{
pointDic[go.name] = go;
}
}
如果要更新一個點的位置:
/// <summary>
/// 通過位置和string更新點位置
/// </summary>
/// <param name="pos"></param>
/// <param name="name"></param>
public void UpdatePoint(Vector3 pos,strig name,PointType _type)
{
if (pointDic.ContainsKey(name))
{
pointDic[name].transform.localPosition = GetMapPositionByWorldV3(pos);
}
else
{
AddPointToDic(BuildPointByType(pos, name, _type));
}
}
- 在
Update()
方法中對需要動態修改位置或者其他屬性的點進行重新整理,例如我們更新所有炮塔位置:
void Update()
{
//炮塔
for (int i = 0; i < towers.Count; i++)
{
if (i < towers.Count/2)
{
UpdatePoint(towers[i], MiniMapController.PointType.LeftTower);
}
else
{
UpdatePoint(towers[i], MiniMapController.PointType.RightTower);
}
}
}