需求:制作一个正方形的小地图,如果小地图素材不是正方形,则美术同学填补成正方形
(据策划大佬说,王者荣耀的小地图就是这样的)
//小地图逻辑
public class MiniMap
{
//小地图素材
private string mPic;
public string Pic => mPic;
//场景左下角坐标起始点(小地图可能不是从场景的00点开始)
private Vector3 mSceneLeftDown;
public Vector3 SceneLeftDown => mSceneLeftDown;
//场景右上角坐标终点(小地图的终点可能不是场景终点)
private Vector3 mSceneRightTop;
public Vector3 SceneRightTop => mSceneRightTop;
//小地图素材左下角坐标点(小地图可能是美术补色)
private Vector3 mPngLeftDown;
public Vector3 PngLeftDown => mPngLeftDown;
//小地图素材右上角坐标点(小地图可能是美术补色)
private Vector3 mPngRightTop;
public Vector3 PngRightTop => mPngRightTop;
//小地图的展示角度有可能和场景角度不一致,这里的方案是计算完坐标点后
//旋转UI控件
private float mUIRotation;
public float UIRotation => mUIRotation;
//旋转后的UI控件,会有偏移值
private Vector3 mUIOffset;
public Vector3 UIOffset => mUIOffset;
//png和UI的缩放比,例如UI控件300,小地图素材是512,那么缩放比是0.59
private float mPngToUI = 0.59f;
//场景和png的缩放比,例如场景是100*100,小地图素材是512*512
//那么缩放比就是5.12
private Vector3 mSceneToPngScale;
Vector3 mPngToUIScale;
public MiniMap(int sn)
{
Vector3 deltaScene = mSceneRightTop - mSceneLeftDown;
Vector3 deltaPng = PngRightTop - PngLeftDown;
mPngToUIScale = Vector3.one * mPngToUI;
mSceneToPngScale = new Vector3(deltaPng.x / deltaScene.x, deltaPng.y / deltaScene.y, 1);
}
public Vector3 ScenePositionToUIPosition(Vector3 scenePosition)
{
Vector3 pngPos = ScenePositionToPngPosition(scenePosition);
return PngToUIPosition(pngPos);
}
private Vector3 PngToUIPosition(Vector3 pngPosition)
{
return new Vector3(pngPosition.x * mPngToUIScale.x, pngPosition.y * mPngToUIScale.y, 1);
}
private Vector3 ScenePositionToPngPosition(Vector3 scenePosition)
{
scenePosition = ScenePositionTo2DPosition(scenePosition);
Vector3 deltaScene = scenePosition - mSceneLeftDown;
return PngLeftDown + new Vector3(deltaScene.x * mSceneToPngScale.x, deltaScene.y * mSceneToPngScale.y, 1);
}
private Vector3 ScenePositionTo2DPosition(Vector3 scenePosition)
{
return new Vector3(scenePosition.x, scenePosition.z, 0);
}
}
//UI逻辑
public class MiniMapView : Monobehaviour
{
//地图可放大,可缩小
private enum EType
{
Small,
Big,
}
//地图上显示自己,其他玩家和Npc
private enum EMapKey
{
Self,
Other,
Npc,
}
//当前是大地图还是小地图
private EType mCurType = EType.Small;
//小地图图片的控件
private RawImage mImg;
//放大,缩小按钮
private GameObject mBtnBig, mBtnSmall;
//小地图逻辑
private MiniMap mMap;
//小地图父节点,用来播放放大缩小动画
private GameObject mImgGo;
private TweenScale mTween;
//玩家自己
private MapKey mSelf;
//其他玩家
private List<MapKey> mListOther = new List<MapKey>();
//需要在小地图显示标识的NPC
private List<MapKey> mListNpc = new List<MapKey>();
//自己,其他玩家,NPC标识的控件模板
private GameObject mSelfTemplate, mOtherTemplate, mNpcTemplate;
//点击小地图上的标识,显示玩家名字的tips
private GameObject mTipPanel;
private GameObject mTipName;
void OnInit()
{
//给控件赋值,注册点击事件
}
public void OnShow(object param = null)
{
mMap = new MiniMap((int)param);
mSelf = new MapKey(mSelfTemplate, mMap, EMapKey.Self);
mSelf.SetData(Role);
//给小地图的图片赋值,这里就不写了
SetImgRotAndPos();
mCurType = EType.Small;
RefreshBtn();
MakeOtherKey();
MakeNpc();
}
//设置图片的角度和便宜位置
private void SetImgRotAndPos()
{
if (mMap == null) return;
mImgGo.transform.rectTransform().anchoredPosition3D = mMap.UIOffset;
mImgGo.transform.localEulerAngles = new Vector3(0, 0, mMap.UIRotation);
}
//刷新标识位置
protected override void Update()
{
base.Update();
if (mMap == null) return;
UpdateSelfPos();
UpdateNpcPos();
UpdateOtherPos();
}
private void UpdateSelfPos()
{
mSelf?.UpdatePos();
}
private void UpdateOtherPos()
{
for(int i = 0;i < mListOther.Count;++i)
{
mListOther[i].UpdatePos();
}
}
private void UpdateNpcPos()
{
for (int i = 0; i < mListNpc.Count; ++i)
{
mListNpc[i].UpdatePos();
}
}
//当别的角色上下线,刷新其他玩家NPC标识
private void RefreshOther()
{
for(int i = 0;i < mListOther.Count;++i)
{
mListOther[i].RefreshOther();
}
}
//创建其他角色标识
private void MakeOtherKey()
{
List<Role> list;
if (list == null || list.Count <= 0)
{
ResetOther();
return;
}
int i = 0;
for(;i < list.Count;++i)
{
if (i >= mListOther.Count)
{
GameObject go = GameObject.Instantiate(mOtherTemplate);
go.transform.SetParent(mOtherTemplate.transform.parent, false);
MapKey mapKey = new MapKey(go,mMap, EMapKey.Other);
mListOther.Add(mapKey);
}
mListOther[i].SetData(list[i]);
mListOther[i].SetVisible(true);
}
for(;i < mListOther.Count;++i)
{
mListOther[i].SetVisible(false);
}
}
private void ResetOther()
{
for(int i = 0;i < mListOther.Count;++i)
{
mListOther[i].SetVisible(false);
}
}
//创建NPC标识
private void MakeNpc()
{
if(mMap == null)
{
ResetNpc();
return;
}
List<NpcInfo> list = ModelManager.Get<NpcModel>().GetListMapNpcByScene(mMap.Sn);
if(list == null || list.Count <= 0)
{
ResetNpc();
return;
}
int i = 0;
for(;i < list.Count;++i)
{
if(i >= mListNpc.Count)
{
GameObject go = GameObject.Instantiate(mNpcTemplate);
go.transform.SetParent(mNpcTemplate.transform.parent, false);
MapKey mapKey = new MapKey(go,mMap, EMapKey.Npc);
mListNpc.Add(mapKey);
}
mListNpc[i].SetData(list[i]);
mListNpc[i].SetVisible(true);
}
for(;i < mListNpc.Count;++i)
{
mListNpc[i].SetVisible(false);
}
}
private void ResetNpc()
{
for(int i = 0;i < mListNpc.Count;++i)
{
mListNpc[i].SetVisible(false);
}
}
//刷新放大,缩小按钮
private void RefreshBtn()
{
SetVisible(mBtnBig, mCurType == EType.Small && !mIsPlaying);
SetVisible(mBtnSmall, mCurType == EType.Big && !mIsPlaying);
}
//点击放大按钮
private void OnClickBig(GameObject obj)
{
mCurType = EType.Big;
PlayTween();
RefreshBtn();
}
//点击缩小按钮
private void OnClickSmall(GameObject obj)
{
mCurType = EType.Small;
PlayTween(true);
RefreshBtn();
}
//播放放大缩小动画,动画期间不能隐藏按钮,防止误点击
private bool mIsPlaying = false;
private void PlayTween(bool isReverse = false)
{
mIsPlaying = true;
if (mTween == null) return;
if(!isReverse)
{
mTween.Play();
}
else
{
mTween.PlayReverse();
}
TimerManager.Instance.AddTimer(0.15f, () =>
{
mIsPlaying = false;
RefreshBtn();
});
}
//点击标识,显示名字,三秒后隐藏
private void OnClickKey(CharacterBase info)
{
if (info == null) return;
string str = info.Name;
if(info is Role)
{
Role role = (Role)info;
if (role != null && !role.IsOnline)
{
str = str + "(离线)";
}
}
SetText(mTipName, str);
SetVisible(mTipPanel, true);
TimerManager.Instance.AddTimer(3f, () =>
{
SetVisible(mTipPanel, false);
});
}
//小地图上的标识
private class MapKey : WindowView
{
private EMapKey mKeyType = EMapKey.Self;
//其他玩家上下线的节点
private GameObject mOnline, mOffline;
//NPC的图标
private Image mNpcImg;
//角色信息
private CharacterBase mInfo;
private MiniMap mMap;
public MapKey(GameObject parent, MiniMap map, EMapKey keyType = EMapKey.Self)
{
mParent = parent;
mKeyType = keyType;
mMap = map;
if(mKeyType == EMapKey.Other)
{
mOnline;
mOffline;
}
else if (mKeyType == EMapKey.Npc)
{
mNpcImg ;
}
}
//设置NPC图标
public void SetData(Npc info)
{
if (info == null) return;
mInfo = info;
SetVisible(info.Visible);
}
//设置角色标识
public void SetData(Role info)
{
if (info == null) return;
mInfo = info;
if (mKeyType == EMapKey.Other)
{
RefreshOther(info);
}
}
//其他玩家上下线
public void RefreshOther(Role info = null)
{
if (info == null)
{
info = (Role)mInfo;
}
if (info == null) return;
SetVisible(mOnline, info.IsOnline);
SetVisible(mOffline, !info.IsOnline);
}
//设置标识位置
public void UpdatePos()
{
if (mMap == null || mInfo == null) return;
SetVisible(mInfo.Visible);
Parent.transform.localPosition = mMap.ScenePositionToUIPosition(mInfo.GetCurPos());
}
private void OnClickKey(GameObject obj)
{
//点击标识,显示名字tips
}
}
}
}