Unity的程序化刀光插件:X-WeaponTrail
X-WeaponTrail是一個很古老的程序化刀光插件齿诞,很早以前買了一直放在倉庫沒用上雨膨。最近不知道特效同學抽什么瘋支救,突然不肯自己做刀光特效了幢泼,所以就把這個插件丟給他了渡嚣。
用法
該插件的用法很簡單渣淳,找到你的武器脾还,把X-WeaponTrail預設拖到武器節(jié)點下,調(diào)整StartPoint和EndPoint入愧,使其和武器對齊即可鄙漏。
查看X Weapon Trail腳本,參數(shù)都比較直觀棺蛛,不過美術(shù)比較難理解的是Max Frame怔蚌,Granularity這2個參數(shù),作者在他的主頁給了如下說明:
MaxFrame可以理解為刀光拖尾的生命周期(長度)旁赊,MaxFrame越大桦踊,拖尾越長。
Granularity可以理解為拖尾Mesh的頂點粒度终畅,Granularity越大籍胯,頂點數(shù)越多,越平滑离福。
實現(xiàn)細節(jié)
趁著給美術(shù)介紹參數(shù)杖狼,我把這個刀光的實現(xiàn)代碼看了一遍,有一些地方還是值得說一下妖爷。
首先蝶涩,作者維護了一個隊列mSnapshotList,用來保存刀光拖尾的位置信息赠涮。隊列的index越小子寓,則越接近當前武器的位置,index越大則越接近刀光的尾部笋除,隊列的長度由MaxFrame決定斜友。
void RecordCurElem()
{
//TODO: use element pool to avoid gc alloc.
//Element elem = new Element(PointStart.position, PointEnd.position);
Element elem = mElemPool.Get();
elem.PointStart = PointStart.position;
elem.PointEnd = PointEnd.position;
if (mSnapshotList.Count < MaxFrame)
{
mSnapshotList.Insert(1, elem);
}
else
{
mElemPool.Release(mSnapshotList[mSnapshotList.Count - 1]);
mSnapshotList.RemoveAt(mSnapshotList.Count - 1);
mSnapshotList.Insert(1, elem);
}
}
上面代碼中的Element記錄了一幀的位置信息,包括一開始我們設置的StartPoint和EndPoint垃它,以及中間位置Pos鲜屏。
public class Element
{
public Vector3 PointStart;
public Vector3 PointEnd;
public Vector3 Pos
{
get
{
return (PointStart + PointEnd) / 2f;
}
}
public Element(Vector3 start, Vector3 end)
{
PointStart = start;
PointEnd = end;
}
public Element()
{
}
}
有了MaxFrame幀的位置信息后烹看,我們就可以生成Mesh了。假設我們沒有做任何平滑處理洛史,只是簡單的根據(jù)每一幀的位置(StartPoint惯殊,Pos,EndPoint)補充頂點也殖,已經(jīng)可以生成刀光的Mesh了土思,但是會出現(xiàn)明顯的折痕:
這個時候,插值平滑就非常必要了忆嗜。作者采用的插值方式是CatmullRom己儒,關(guān)于CatmullRom的說明,網(wǎng)上很多捆毫,這里直接貼代碼:
public static Vector3 CatmulRom(Vector3 T0, Vector3 P0, Vector3 P1, Vector3 T1, float f)
{
double DT1 = -0.5;
double DT2 = 1.5;
double DT3 = -1.5;
double DT4 = 0.5;
double DE2 = -2.5;
double DE3 = 2;
double DE4 = -0.5;
double DV1 = -0.5;
double DV3 = 0.5;
double FAX = DT1 * T0.x + DT2 * P0.x + DT3 * P1.x + DT4 * T1.x;
double FBX = T0.x + DE2 * P0.x + DE3 * P1.x + DE4 * T1.x;
double FCX = DV1 * T0.x + DV3 * P1.x;
double FDX = P0.x;
double FAY = DT1 * T0.y + DT2 * P0.y + DT3 * P1.y + DT4 * T1.y;
double FBY = T0.y + DE2 * P0.y + DE3 * P1.y + DE4 * T1.y;
double FCY = DV1 * T0.y + DV3 * P1.y;
double FDY = P0.y;
double FAZ = DT1 * T0.z + DT2 * P0.z + DT3 * P1.z + DT4 * T1.z;
double FBZ = T0.z + DE2 * P0.z + DE3 * P1.z + DE4 * T1.z;
double FCZ = DV1 * T0.z + DV3 * P1.z;
double FDZ = P0.z;
float FX = (float)(((FAX * f + FBX) * f + FCX) * f + FDX);
float FY = (float)(((FAY * f + FBY) * f + FCY) * f + FDY);
float FZ = (float)(((FAZ * f + FBZ) * f + FCZ) * f + FDZ);
return new Vector3(FX, FY, FZ);
}
調(diào)大Granularity后闪湾,之前的折痕平滑了很多。
關(guān)于頂點的UV映射
最后想再說一下頂點的UV映射問題绩卤。如果不考慮特效貼圖途样,單純的看頂點生成,效果如下:
注意上圖的紅色射線濒憋,射線的原點即StartPoint和EndPoint的中間點Pos何暇,射線方向從StartPoint指向EndPoint。
查看頂點更新的代碼凛驮,我們發(fā)現(xiàn)頂點的UV設定如下:StartPoint的U是1赖晶,EndPoint的U是0,Pos的U是0.5辐烂。越接近刀光尾部的頂點,V越接近1捂贿。
Vector3 pos = mSpline.InterpolateByLen(fadeT);
Vector3 up = mSpline.InterpolateNormalByLen(fadeT);
Vector3 pos0 = pos + (up.normalized * mTrailWidth * 0.5f);
Vector3 pos1 = pos - (up.normalized * mTrailWidth * 0.5f);
Debug.DrawRay(pos, up * 3, Color.red);
// pos0
pool.Vertices[baseIdx] = pos0;
pool.Colors[baseIdx] = MyColor;
uvCoord.x = 0f;
uvCoord.y = uvSegment;
pool.UVs[baseIdx] = uvCoord;
//pos
pool.Vertices[baseIdx + 1] = pos;
pool.Colors[baseIdx + 1] = MyColor;
uvCoord.x = 0.5f;
uvCoord.y = uvSegment;
pool.UVs[baseIdx + 1] = uvCoord;
//pos1
pool.Vertices[baseIdx + 2] = pos1;
pool.Colors[baseIdx + 2] = MyColor;
uvCoord.x = 1f;
uvCoord.y = uvSegment;
pool.UVs[baseIdx + 2] = uvCoord;
結(jié)合特效貼圖
越接近刀光頭部(武器)的頂點纠修,其UV映射的區(qū)域越接近貼圖的底部:
越接近刀光尾部的頂點,其UV映射的區(qū)域越接近貼圖的上部:
結(jié)合實際效果查看刀光的強弱厂僧,就更清晰了:
好了扣草,拜拜。