前言
在很多实时PVP对战游戏(如:英雄联盟、王者荣耀等)的战斗场景中,都会有一个小地图,用于实时地显示一些比较重要因素,例如:队友和对手位置、存活炮塔位置、Boss出生死亡情况等。
一、方案分析:
实现小地图的方案一般可以分成两种:
- 直接加一个子相机,映射当前场景中所有的物体,简单粗暴;
- 用UI创建一个假地图,然后将需要显示在小地图上的物体,经过位置换算得到的小地图坐标,然后在小地图中创建与每个物体对应的图标,并实时更新每个图标的状态和位置;
方案一:
第一种方案其实很简单,只需要在场景中加多一个相机和一个Render Texture即可实现,具体的实现步骤可以参考这个案例:Unity3d中使用摄像机制作实时显示小地图
方案二:
这个方案显然要更加复杂一些,但是更加符合需求,因为有时候我们通过小地图不是想看到当前地图的所有物体,而只是想看到一些关键的信息,所以通过UI平面简化显示的方式其实更为直观,例如:英雄都只用一个圆形的头像来代表,而炮台也只是一个图标,对手的位置只有在特定条件下才会显示等。
二、可用插件:
Unity有许多功能强大的插件,关于小地图的实现也有一些插件:KGFMapSystem
和NJG MiniMap
,都能够快速开发出一个可用的地图,具体使用方式可以参考:
- KGFMapSystem:[Unity3d插件KGFMapSystem]非常不错的小地图的制作,也可以参考官方指导:KGF官网
- NJG MiniMap:Unity3D —— 小地图制作插件NJG MiniMap
三、从0实现小地图:
1.思路:
小地图说到底,其实就是一张背景图片,上边有一些代表不同游戏物体的小点或者是图标,然后根据当前个个点所代表物体的变化改变这些点的状态。
2.实现步骤:
- 创建每个类型物体对应的点预设,最好使用一个预设体
MapPoint.prefab
可以兼容创建所有类型的点(因为通常只是UISprite图标在变化),这里我以只带一个UISprite的为例; - 遍历需要显示在小地图上的物体,并在小地图中使用
MapPoint.prefab
预设创建对应的点,用枚举列出所有类型:
public enum PointType
{
MySoldier, //我方特种兵
OppoSoldier,//对手特种兵
Boss, //野怪
LeftTower, //左边塔
RightTower, //右边塔
MyAISoldier,//我方AI小兵
OppoAISoldier//地方AI小兵
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
这里需要按照类型,进行分类创建和设置:
/// <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;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
这里有一个比较关键的方法,就是坐标转换方法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);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 使用一个字典
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;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
如果要更新一个点的位置:
/// <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));
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 在
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);
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15