1. 程式人生 > >Unity3D —— 實時PVP小地圖實現

Unity3D —— 實時PVP小地圖實現

前言

在很多實時PVP對戰遊戲(如:英雄聯盟、王者榮耀等)的戰鬥場景中,都會有一個小地圖,用於實時地顯示一些比較重要因素,例如:隊友和對手位置、存活炮塔位置、Boss出生死亡情況等。

一、方案分析:

實現小地圖的方案一般可以分成兩種:

  • 直接加一個子相機,對映當前場景中所有的物體,簡單粗暴;
  • 用UI建立一個假地圖,然後將需要顯示在小地圖上的物體,經過位置換算得到的小地圖座標,然後在小地圖中建立與每個物體對應的圖示,並實時更新每個圖示的狀態和位置;

方案一:

第一種方案其實很簡單,只需要在場景中加多一個相機和一個Render Texture即可實現,具體的實現步驟可以參考這個案例:

Unity3d中使用攝像機制作實時顯示小地圖

方案二:

這個方案顯然要更加複雜一些,但是更加符合需求,因為有時候我們通過小地圖不是想看到當前地圖的所有物體,而只是想看到一些關鍵的資訊,所以通過UI平面簡化顯示的方式其實更為直觀,例如:英雄都只用一個圓形的頭像來代表,而炮臺也只是一個圖示,對手的位置只有在特定條件下才會顯示等。

二、可用外掛:

Unity有許多功能強大的外掛,關於小地圖的實現也有一些外掛:KGFMapSystemNJG 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);
        }
    }
}

四、效果圖:

這裡寫圖片描述