跟之前使用LineRenderer的方式很像,首先由起始點跟終點取得原始線段,由這條線的資訊可以計算出Theta角,使用這個角度資訊再加上每個節點的偏移資訊(dir1、dir2)就可以計算出調整位置後各個節點的座標(綠色Target)。
接著使用這個座標點(綠色Target)再加上線段寬度的資訊(使用AnimationCurve取得漸變的線段起點跟終點的寬度)即可以取得方形的四個角點的座標,最後再使用這四的角點座標去製作Mesh就可以得到這條線段。
其實如果使用這個方法來做線段Mesh會發現一個問題,就是在轉角的地方,兩條線段並沒有密合,當然這個部份調整一下Mesh製作的部分就可以解決,不過就單純的使用上來說,目前這個方法已經堪用了。
在使用上來說,還需要注意有些Shader無法調整顏色跟Alpha值,同時因為這個是我用在2D遊戲上的部分,如果要在3D場景中使用還需要注意Mesh的Normal,要就是使用Shader來讓正反面都有材質,不然就是使用Billboard的方式讓這線段的Mesh一直朝著Camera之類的來避免Mesh背向攝影機看不到線段的狀況。
--
實作測試:拿我之前貓貓遊戲畫的背景圖來用,點擊滑鼠放電流,背景樹狀閃電跟前景電流都只用1個draw call,不過因為是用Mesh來做所以面數會多很多,如果有美術資源可用的話也就不必用這種方式。
--
使用自訂Mesh線段的電流
public class CustomLine : MonoBehaviour { public Material mat; //貼圖材質 public GameObject startPos; //線段起點,使用物件主要是方便可以在編輯視窗中拖曳,或自己調整為Vector3 public GameObject targetPos; //線段終點 public int count = 15; //節點數量 //使用AnimationCurve來設定線段的寬度、顏色(須注意多數Shader並不支援這顏色的改變,自己寫或是姑且使用Unity內建的Sprites/Diffuse之類的) //比起LineRenderer的優點在於,不限定只能設定線段開始跟結束的寬度跟顏色 public AnimationCurve width = AnimationCurve.Linear(0f,0.2f,1f,0.2f); public AnimationCurve colorR = AnimationCurve.Linear(0f,1f,1f,1f); public AnimationCurve colorG = AnimationCurve.Linear(0f,1f,1f,1f); public AnimationCurve colorB = AnimationCurve.Linear(0f,1f,1f,1f); public AnimationCurve colorA = AnimationCurve.Linear(0f,1f,1f,1f); private MeshFilter lineMeshFilter; private MeshRenderer lineMeshRenderer; private GameObject customLine; //建立一個物件來乘載MeshFilter跟MeshRenderer,避免跟Component掛載物件上的衝突 void Update () { Lightning(); } private void Lightning() { Vector3[] points = CalculatePoints (startPos, targetPos, count); if(customLine == null) { customLine = new GameObject ("CustomLine"); customLine.transform.parent = this.transform; lineMeshFilter = customLine.AddComponent(typeof(MeshFilter)) as MeshFilter; lineMeshRenderer = customLine.AddComponent (typeof(MeshRenderer)) as MeshRenderer; lineMeshRenderer.material = mat; CreateMesh (points); } ModifyMesh (points); } private void ModifyMesh(Vector3[] points) { //取得新的verts座標 Vector3[] verts = GetVerts (points); //取得顏色資訊 Color[] colors = GetColor (verts.Length); //使用新的verts跟顏色資訊調整現有的Mesh Mesh lineMesh = lineMeshFilter.mesh; lineMesh.vertices = verts; lineMesh.colors = colors; } //計算新的線段座標點 private Vector3[] CalculatePoints(GameObject from, GameObject to, int points) { Vector3[] result = new Vector3[points]; Vector3 dir = to.transform.position - from.transform.position; Vector3 dirDelta = dir / (points - 1); float theda = Mathf.Atan2 (dirDelta.y, dirDelta.x); for(int i = 0; i < count; ++i) { //每條原始線段的起始座標點 Vector3 pos = from.transform.position + dirDelta * i; int mid = count / 2; float ratio = (count % 2 == 0) ? (i - (i/mid) * ((i % mid + 1)*2 - 1)) : (i - (i/(mid+1)) * ((i % (mid+1) + 1)*2)); float r1 = Random.Range(-0.06f, 0.06f) * ratio; float r2 = Random.Range(-0.12f, 0.12f) * ratio; Vector3 dir1 = new Vector3(Mathf.Cos (theda) * r1, Mathf.Sin(theda) * r1, 0); Vector3 dir2 = new Vector3(Mathf.Sin (theda) * r2, -Mathf.Cos(theda) * r2, 0); //段數調整位置後的最終座標點 result[i] = pos + dir1 + dir2; } return result; } //建立Mesh private void CreateMesh(Vector3[] points) { Vector3[] verts = GetVerts (points); //取得vertices資訊 Vector2[] UVs = new Vector2[(points.Length-1)*4]; int[] tris = new int[(points.Length-1)*6]; for(int i = 0; i < points.Length-1; ++i) { //建立UV資訊 UVs[i*4] = new Vector2(0, 0); UVs[i*4 + 1] = new Vector2(0, 1); UVs[i*4 + 2] = new Vector2(1, 0); UVs[i*4 + 3] = new Vector2(1, 1); //建立三角面資訊 tris[i*6] = i*4 + 0; tris[i*6 + 1] = i*4 + 2; tris[i*6 + 2] = i*4 + 1; tris[i*6 + 3] = i*4 + 1; tris[i*6 + 4] = i*4 + 2; tris[i*6 + 5] = i*4 + 3; } //取得顏色資訊 Color[] colors = GetColor (verts.Length); //建立新的Mesh Mesh lineMesh = lineMeshFilter.mesh; lineMesh.vertices = verts; lineMesh.triangles = tris; lineMesh.uv = UVs; lineMesh.colors = colors; lineMesh.RecalculateBounds (); lineMesh.RecalculateNormals (); } //使用AnimationCurve取得漸變顏色的資訊 private Color[] GetColor(int count) { Color[] colors = new Color[count]; for(int i = 0; i < colors.Length; ++i) { float time = (float)i / (float)(colors.Length-1); colors[i] = new Color(colorR.Evaluate(time), colorG.Evaluate(time), colorB.Evaluate(time), colorA.Evaluate(time)); } return colors; } private Vector3[] GetVerts(Vector3[] points) { Vector3[] verts = new Vector3[(points.Length-1)*4]; for(int i = 0; i < points.Length-1; ++i) { Vector3 start = points[i]; Vector3 end = points[i+1]; //計算線段起始點跟終點的寬 float time = (float)i/(float)(points.Length-1); float timeNext = (float)(i+1)/(float)(points.Length-1); float startPosWidth = width.Evaluate(time)/2; float endPosWidth = width.Evaluate(timeNext)/2; //計算偏移的向量 Vector3 dir = end - start; float theda = Mathf.Atan2 (dir.y, dir.x); Vector3 widthStartDir = new Vector3(Mathf.Sin (theda) * startPosWidth, -Mathf.Cos(theda) * startPosWidth, 0); Vector3 widthEndDir = new Vector3(Mathf.Sin (theda) * endPosWidth, -Mathf.Cos(theda) * endPosWidth, 0); //使用向量取得這線段Mesh的四個角的座標點 Vector3 leftStartPos = start - widthStartDir; Vector3 rightStartPos = start + widthStartDir; Vector3 leftEndPos = end - widthEndDir; Vector3 rightEndPos = end + widthEndDir; verts[i*4] = leftStartPos; verts[i*4 + 1] = rightStartPos; verts[i*4 + 2] = leftEndPos; verts[i*4 + 3] = rightEndPos; } return verts; } }
No comments:
Post a Comment