[Unity]利用Mesh绘制简单的可被遮挡,可以探测的攻击指示器
最近做一个小游戏的Demo,最终的效果是这样的
主要是利用Mesh绘制三角形作为显示,然后使用后处理来制作探灯,注意,性能一般,仅仅适合小游戏
分为3步
1:利用mesh绘制三角形,原理很简单,利用三角函数Tan,给定一个角度计算三角形左或者右一个顶点,最后绘制即可
1 void DrawIt() 2 { 3 if (distance > 0 && angle > 0)//angle角度的一半,distance指三角形起点到另外两点间的距离 4 { 5 Mesh mesh = new Mesh(); 6 float pointY = Mathf.Tan(angle * Mathf.Deg2Rad) * distance;// 7 Vector3 p1 = new Vector3(distance, transform.localPosition.y, pointY); 8 Vector3 p2 = new Vector3(distance, transform.localPosition.y, -pointY); 9 mesh.vertices = new[] { transform.localPosition, p1, p2 }; 10 mesh.triangles = new[] { 0, 1, 2 }; 11 transform.GetComponent<MeshFilter>().mesh = mesh; 12 } 13 }
2:为了制作出被墙壁阻挡得效果,用了最笨得方法,即按照每一度都计算一个点,最后连成三角形,上面得三角形只有三个点,这步制作得可能有N个点,然后从每个起点向终点发射射线,如果有碰撞就将顶点修改为碰撞点,为了让三角形每个点足够平滑,还是用了一个缩放来增加顶点数
1 void DrawItMore() 2 { 3 if (distance > 0 && angle > 0)//distance:三角形起点到某点的距离,用在Tan函数,angle,三角形角度的一半 4 { 5 Mesh mesh = new Mesh(); 6 List<Vector3> point = new List<Vector3>(); 7 point.Add(transform.localPosition); 8 List<Vector3> pointLeft = new List<Vector3>(); 9 List<Vector3> pointRight = new List<Vector3>(); 10 RaycastHit hit; 11 for (int i = angle * LineScale; i > 0; i--)//LineScale,没一度都发射射线还不够细,一般这个值为5比较合适(缩放1的时候),性能消耗主要在这 12 { 13 float index = i / (float)LineScale; 14 float pointY = Mathf.Tan(index * Mathf.Deg2Rad) * angle; 15 Vector3 p1 = new Vector3(distance, transform.localPosition.y, pointY); 16 Vector3 world = transform.TransformPoint(p1); 17 if (Physics.Linecast(transform.position, world, out hit, 1 << LayerMask.NameToLayer("Wall")))//只和Wall Layer进行检测 18 { 19 p1 = transform.InverseTransformPoint(hit.point); 20 } 21 Vector3 p2 = new Vector3(GameModel.Ins.MainRoleData.AtkRange, transform.localPosition.y, -pointY); 22 if (Physics.Linecast(transform.position, transform.TransformPoint(p2), out hit, 1 << LayerMask.NameToLayer("Wall"))) 23 { 24 p2 = transform.InverseTransformPoint(hit.point); 25 } 26 pointLeft.Add(p1); 27 pointRight.Insert(0, p2); 28 } 29 point.AddRange(pointLeft); 30 point.AddRange(pointRight); 31 List<int> triangles = new List<int>();//简单设置顶点连接顺序,注意unity顺时针才是正面 32 for (int i = 1; i < point.Count - 1; i++) 33 { 34 triangles.Add(i); 35 triangles.Add(i + 1); 36 triangles.Add(0); 37 } 38 mesh.vertices = point.ToArray(); 39 mesh.triangles = triangles.ToArray(); 40 transform.GetComponent<MeshFilter>().mesh = mesh; 41 } 42 }
3:遮挡完成了,最后一步就是制作三角形探测,大概原理是新建一个相机仅仅渲染绘制的三角形,并且修改相机设置为Don't Clear,然后将此相机渲染到纹理,最后和主相机利用屏幕后处理进行混合
3.1:新建相机设置,主要是ClearFlags和CullingMask(裁剪设置,只渲染绘制的三角形),主相机叫相机A,新相机就是相机B
3.2:将此相机渲染到纹理,并为混合Shader赋值
1 void Start() 2 { 3 var t = new RenderTexture(Screen.width, Screen.height, 16); 4 GetComponent<Camera>().targetTexture = t; 5 Mat.SetTexture("_LOSMaskTexture", t);//Mat是后面用到的Shader,这里仅仅为Shader图片赋值,也可以不用代码创建RenderTexture,随意 6 }
3.3:编写混合Shader,就是单纯的根据某个通道值叠加
1 Shader "Custom/LOSMaskShader" 2 { 3 Properties 4 { 5 _MainTex("MainTex",2D) = "white"{} 6 _LOSMaskTexture("LOSMaskTexture",2D) = "white"{}//相机B生成的图 7 _MaskScale("MaskScale", float) = 0.5//混合比例 8 } 9 SubShader 10 { 11 // No culling or depth 12 Cull Off ZWrite Off ZTest Always//屏幕后处理的标配 13 14 Pass 15 { 16 CGPROGRAM 17 #pragma vertex vert 18 #pragma fragment frag 19 20 #include "UnityCG.cginc" 21 22 struct appdata 23 { 24 float4 vertex : POSITION; 25 float2 uv : TEXCOORD0; 26 }; 27 28 struct v2f 29 { 30 float2 uv : TEXCOORD0; 31 float4 vertex : SV_POSITION; 32 }; 33 34 v2f vert (appdata v) 35 { 36 v2f o; 37 o.vertex = UnityObjectToClipPos(v.vertex); 38 o.uv = v.uv; 39 return o; 40 } 41 42 sampler2D _LOSMaskTexture; 43 sampler2D _MainTex; 44 float _MaskScale; 45 46 fixed4 frag (v2f i) : SV_Target 47 { 48 float4 mask = tex2D(_LOSMaskTexture,i.uv); 49 float4 main = tex2D(_MainTex,i.uv); 50 51 main = main * saturate(mask.r + _MaskScale); //这里提取相机B的R值作为混合,MsakScale越高自然整体越亮 52 return main; 53 } 54 ENDCG 55 } 56 } 57 }
3.4:最后一步,利用OnRenderImage应用屏幕后处理效果
1 private void OnRenderImage(RenderTexture source, RenderTexture destination) 2 { 3 Graphics.Blit(source, destination, LOSMaskMaterial);//Mat是上面Shader的Mat 4 }
Over,对Shader不熟悉的可以看看资料,高性能的等以后有机会了专门研究下,完结撒花