Unity使用shader实现高性能小地图

您所在的位置:网站首页 lol小地图很模糊 Unity使用shader实现高性能小地图

Unity使用shader实现高性能小地图

2024-07-11 23:49| 来源: 网络整理| 查看: 265

这是结果图和对应的网格图,网格图把上面多于的字体和路径删了,只留下背景地图和小图标,只使用了一张image就可以实现地图和多个小图标

在写之前查了好多小地图的写法,这里有三种

在地图下面铺一张图片,每个需要显示的角色身下再来一张icon的image,正交相机从上面照着角色。好处是不用控制相机的位置和icon的位置,npc monster脚下自带,主角移动到周围就自动显示,但是需要新加相机,每个需要显示icon的物体都要新加一张图片。ugui原生图片,通过计算移动地图的位置和生成icon,上面加一个mask,类似下面这样,底图会很大,势必会增加OverDraw。上面方法改进版,通过更改uv来显示图片的不同部分。不过icon的多个image也会增加多一层DC。最后就是我现在使用的一种方法,手动画mesh,指定不同uv,一个DC

 

下面上实现方法:

自定义Image

using System; using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; public class MinimapImage : Image { public List Icons = new List(); private UIVertex GetVertex(float x, float y, Vector2? iconTag = null, Color? color = null) { UIVertex uiVertex = new UIVertex(); uiVertex.color = color ?? this.color; uiVertex.position.x = rectTransform.rect.width * (x - 0.5f); uiVertex.position.y = rectTransform.rect.height * (y - 0.5f); uiVertex.uv0 = new Vector2(x, y); uiVertex.uv1 = iconTag != null ? iconTag.Value / 2 + 0.5f * Vector2.one : Vector2.zero; return uiVertex; } protected override void OnPopulateMesh(VertexHelper toFill) { toFill.Clear(); toFill.AddUIVertexQuad(new[] {GetVertex(0, 0), GetVertex(0, 1), GetVertex(1, 1), GetVertex(1, 0)}); lock (Icons) { for (int i = 0; i < Icons.Count; i++) { AddIconVertexArr(toFill, Icons[i]); } } } private void AddIconVertexArr(VertexHelper vh, Icon icon) { UIVertex[] vertices = new UIVertex[4]; Vector2[] poss = { icon.LBPos, icon.LTPos, icon.RTPos, icon.RBPos, }; vertices[0] = GetVertex(poss[0].x,poss[0].y, icon.UVLeftBottom); vertices[1] = GetVertex(poss[1].x,poss[1].y, new Vector2(icon.UVLeftBottom.x, icon.UVLeftBottom.y + icon.UVWidthLength.y)); vertices[2] = GetVertex(poss[2].x,poss[2].y, new Vector2(icon.UVLeftBottom.x + icon.UVWidthLength.x, icon.UVLeftBottom.y + icon.UVWidthLength.y)); vertices[3] = GetVertex(poss[3].x,poss[3].y, new Vector2(icon.UVLeftBottom.x + icon.UVWidthLength.x, icon.UVLeftBottom.y)); vh.AddUIVertexQuad(vertices); } public class Icon { public Vector2 PosCenter; public Vector2 LBPos; public Vector2 LTPos; public Vector2 RTPos; public Vector2 RBPos; public float Angle; public Vector2 UVLeftBottom; public Vector2 UVWidthLength; public Icon(Vector2 posCenter, Vector2 lbPos, Vector2 ltPos, Vector2 rtPos, Vector2 rbPos, float angle, Vector2 uvLeftBottom, Vector2 uvWidthLength) { PosCenter = posCenter; LBPos = lbPos; LTPos = ltPos; RTPos = rtPos; RBPos = rbPos; Angle = angle; UVLeftBottom = uvLeftBottom; UVWidthLength = uvWidthLength; } } }

shader:从unity自带的sprite shader稍加修改过来的

Shader "Custom/minimap" { Properties { [PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {} _IconTex("Icon Texture", 2D) = "white" {} _Color ("Tint", Color) = (1,1,1,1) _StencilComp ("Stencil Comparison", Float) = 8 _Stencil ("Stencil ID", Float) = 0 _StencilOp ("Stencil Operation", Float) = 0 _StencilWriteMask ("Stencil Write Mask", Float) = 255 _StencilReadMask ("Stencil Read Mask", Float) = 255 _ColorMask ("Color Mask", Float) = 15 [Toggle(UNITY_UI_ALPHACLIP)] _UseUIAlphaClip ("Use Alpha Clip", Float) = 0 } SubShader { Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" "PreviewType"="Plane" "CanUseSpriteAtlas"="True" } Stencil { Ref [_Stencil] Comp [_StencilComp] Pass [_StencilOp] ReadMask [_StencilReadMask] WriteMask [_StencilWriteMask] } Cull Off Lighting Off ZWrite Off ZTest [unity_GUIZTestMode] Blend SrcAlpha OneMinusSrcAlpha ColorMask [_ColorMask] Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #pragma target 2.0 #include "UnityCG.cginc" #include "UnityUI.cginc" #pragma multi_compile_local _ UNITY_UI_CLIP_RECT #pragma multi_compile_local _ UNITY_UI_ALPHACLIP struct appdata_t { float4 vertex : POSITION; float4 color : COLOR; float2 texcoord : TEXCOORD0; UNITY_VERTEX_INPUT_INSTANCE_ID fixed2 texcoord1 : TEXCOORD1; }; struct v2f { float4 vertex : SV_POSITION; fixed4 color : COLOR; float2 texcoord : TEXCOORD0; float4 worldPosition : TEXCOORD1; UNITY_VERTEX_OUTPUT_STEREO fixed2 texcoord1 : TEXCOORD2; }; fixed4 _Color; sampler2D _MainTex; fixed4 _MainTex_ST; sampler2D _IconTex; fixed4 _IconTex_ST; fixed4 _TextureSampleAdd; float4 _ClipRect; v2f vert(appdata_t v) { v2f OUT; UNITY_SETUP_INSTANCE_ID(v); UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(OUT); OUT.worldPosition = v.vertex; OUT.vertex = UnityObjectToClipPos(OUT.worldPosition); OUT.texcoord1 = v.texcoord1; OUT.texcoord = TRANSFORM_TEX(v.texcoord, _MainTex); OUT.color = v.color * _Color; return OUT; } fixed4 frag(v2f IN) : SV_Target { fixed4 c; if(IN.texcoord1.x >= 0.5) { c = tex2D(_IconTex, (IN.texcoord1 - 0.5)*2); }else{ c = tex2D(_MainTex, IN.texcoord); } c * IN.color; #ifdef UNITY_UI_CLIP_RECT c.a *= UnityGet2DClipping(IN.worldPosition.xy, _ClipRect); #endif #ifdef UNITY_UI_ALPHACLIP clip (c.a - 0.001); #endif return c; } ENDCG } } }

cs代码

using System; using System.Collections; using System.Collections.Generic; using System.IO; using System.Linq; using Framework; using UnityEngine; public class MiniMap : MonoBehaviour { public enum MapType { Radar, Map, } public MapType Type; private MinimapImage MinimapImage; private Vector2 imgMapLB { get { Vector2 lb = new Vector2(playerPos.x - uiMapToRealWidth / 2, playerPos.z - uiMapToRealHeight / 2); if (playerPos.x + uiMapToRealWidth / 2 > mapWidth) { lb.x = mapWidth - uiMapToRealWidth; } else if (playerPos.x - uiMapToRealWidth / 2 < 0) { lb.x = uiMapToRealWidth / 2; } if (playerPos.z + uiMapToRealHeight / 2 > mapHeight) { lb.y = mapHeight - uiMapToRealHeight; } else if (playerPos.z - uiMapToRealHeight / 2 < 0) { lb.y = uiMapToRealHeight / 2; } return lb; } } private Vector3 originMapCenter; private Vector3 playerPos; //UI map的宽高 private float uiMapHeight => MinimapImage.rectTransform.rect.height; private float uiMapWidth => MinimapImage.rectTransform.rect.width; //对应真是地图的宽 高 private float uiMapToRealHeight => uiMapToRealWidth * uiMapHeight / uiMapWidth; private float uiMapToRealWidth; private float mapWidth; private float mapHeight; private Vector2 uvScale; private UiSpriteMultiAsset spriteAsset; private bool isSpriteLoaded; private List Icons = new List(); private void Awake() { MinimapImage = GetComponent(); } public void SetMapWidthHeight(float width, float height) { mapWidth = width; mapHeight = height; } public void SetUI2RealScale(float widthScale) { uiMapToRealWidth = widthScale; uvScale = new Vector2(uiMapToRealWidth / mapWidth, uiMapToRealHeight / mapHeight); } public void SetOriginMapCenter(Vector3 mapCenter) { originMapCenter = mapCenter; } public void SetPlayerPos(Vector3 pos) { playerPos = pos; } public void UpdateIcons(List icons) { Icons.Clear(); Icons.AddRange(icons); MinimapImage.Icons.Clear(); foreach (var icon in icons) { AddIcon(icon.Pos, icon.Radius, icon.Dir, icon.SpritePath, false); } MinimapImage.SetVerticesDirty(); } public void UpdateMapUV() { Vector2 lb = imgMapLB; Vector2 playerOffset = new Vector2(lb.x + uiMapToRealWidth / 2, lb.y + uiMapToRealHeight / 2); float widthRate = playerOffset.x / mapWidth; float heightRate = playerOffset.y / mapHeight; SetMapUV(new Vector4(uvScale.x, uvScale.y, widthRate - uvScale.x / 2, heightRate - uvScale.y / 2)); } private Vector2 GetIconPos(Vector3 pos) { float widthRate = 1; float heightRate = 1; switch (Type) { case MapType.Radar: var offsetPos = new Vector2(pos.x, pos.z) - imgMapLB; widthRate = offsetPos.x / uiMapToRealWidth; heightRate = 1 - offsetPos.y / uiMapToRealHeight; break; case MapType.Map: var offset = pos - originMapCenter; widthRate = offset.x / mapWidth; heightRate = offset.z / mapHeight; break; } return new Vector2(widthRate, heightRate); } private void AddIcon(Vector3 pos, float radius, Vector3 dir, string spritePath, bool isUpdateVertices = true) { if (spriteAsset == null) { InitSpriteAsset(spritePath); } Icons.Add(new MinimapIcon(pos, radius, dir, spritePath)); if(!isSpriteLoaded) return; var (uvLb, wl) = CalcUV(spriteAsset.GetSprite(GetSpriteName(spritePath))); var iconPos = GetIconPos(pos); if(Type == MapType.Radar && (iconPos.x > 1 || iconPos.y > 1)) return; float angle = Vector3.Angle(Vector3.forward, dir); Vector3 normal = Vector3.Cross (dir, Vector3.forward); angle *= Mathf.Sign (Vector3.Dot(normal,Vector3.up)); MinimapImage.Icons.Add(AdjustIconPos(iconPos, radius, angle, uvLb, wl)); if (isUpdateVertices) MinimapImage.SetVerticesDirty(); } /// /// 调整icon的旋转,还有因为地图宽高不一致造成的icon拉伸 /// private MinimapImage.Icon AdjustIconPos(Vector2 center, float radius, float angle, Vector2 uvLb, Vector2 wl) { Matrix4x4 rotateMat = Matrix4x4.Translate(center) * Matrix4x4.Rotate(Quaternion.Euler(0, 0, angle)) * Matrix4x4.Translate(-center); Vector2 lb = rotateMat * new Vector4(center.x - radius / 2, center.y - radius / 2,1,1); Vector2 lt = rotateMat * new Vector4(center.x - radius / 2, center.y + radius / 2,1,1); Vector2 rt = rotateMat * new Vector4(center.x + radius / 2, center.y + radius / 2,1,1); Vector2 rb = rotateMat * new Vector4(center.x + radius / 2, center.y - radius / 2,1,1); float rate = uiMapWidth / uiMapHeight; lb.y = center.y + (lb.y - center.y) * rate; lt.y = center.y + (lt.y - center.y) * rate; rt.y = center.y + (rt.y - center.y) * rate; rb.y = center.y + (rb.y - center.y) * rate; var icon = new MinimapImage.Icon(center, lb, lt, rt, rb, angle, uvLb, wl); return icon; } private void InitSpriteAsset(string path) { path = GetAtlasPath(path); spriteAsset = new UiSpriteMultiAsset(); spriteAsset.Load(path, () => { List tempIcons = new List(Icons); Icons.Clear(); MinimapImage.material.SetTexture("_IconTex", spriteAsset.assets.First().Value.texture); isSpriteLoaded = true; UpdateIcons(tempIcons); }); } private string GetAtlasPath(string path) { int index = path.LastIndexOf("/"); path = path.Substring(0, index); return $"{path}.png"; } private string GetSpriteName(string path) { return Path.GetFileNameWithoutExtension(path); } private (Vector2 uvLb, Vector2 wl) CalcUV(Sprite sprite) { Rect UVs = sprite.rect; UVs.x /= sprite.texture.width; UVs.width /= sprite.texture.width; UVs.y /= sprite.texture.height; UVs.height /= sprite.texture.height; return (UVs.position, UVs.size); } private void SetMapUV(Vector4 st) { MinimapImage.material.SetVector("_MainTex_ST", st); } private void OnDestroy() { spriteAsset?.Release(); } public struct MinimapIcon { public Vector3 Pos; public float Radius; public Vector3 Dir; public string SpritePath; public MinimapIcon(Vector3 pos, float radius, Vector3 dir, string spritePath) { Pos = pos; Radius = radius; Dir = dir; SpritePath = spritePath; } } }

整体思想就是背景地图图片更改uv来显示,其他icon画到背景的image上,通过指定不同的uv来显示icon的图标,在游戏过程中不断更改mesh和uv来达到小地图的效果。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3