跟之前使用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