Unity武器拖尾插件:X-WeaponTrail

X-WeaponTrail是一个很古老的程序化刀光插件,很早以前买了一直放在仓库没用上。最近不知道特效同学抽什么疯,突然不肯自己做刀光特效了,所以就把这个插件丢给他了。

image

用法

该插件的用法很简单,找到你的武器,把X-WeaponTrail预设拖到武器节点下,调整StartPointEndPoint,使其和武器对齐即可。

image
image
image

查看X Weapon Trail脚本,参数都比较直观,不过美术比较难理解的是Max FrameGranularity这2个参数,作者在他的主页给了如下说明:

image

MaxFrame可以理解为刀光拖尾的生命周期(长度),MaxFrame越大,拖尾越长。
Granularity可以理解为拖尾Mesh的顶点粒度,Granularity越大,顶点数越多,越平滑。


实现细节

趁着给美术介绍参数,我把这个刀光的实现代码看了一遍,有一些地方还是值得说一下。

首先,作者维护了一个队列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记录了一帧的位置信息,包括一开始我们设置的StartPointEndPoint,以及中间位置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了。假设我们没有做任何平滑处理,只是简单的根据每一帧的位置(StartPoint,Pos,EndPoint)补充顶点,已经可以生成刀光的Mesh了,但是会出现明显的折痕:

image

这个时候,插值平滑就非常必要了。作者采用的插值方式是CatmullRom,关于CatmullRom的说明,网上很多,这里直接贴代码:

        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);
        }

调大Granularity后,之前的折痕平滑了很多。

image

关于顶点的UV映射

最后想再说一下顶点的UV映射问题。如果不考虑特效贴图,单纯的看顶点生成,效果如下:

image
image

注意上图的红色射线,射线的原点即StartPointEndPoint的中间点Pos,射线方向从StartPoint指向EndPoint

查看顶点更新的代码,我们发现顶点的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;

结合特效贴图

image

越接近刀光头部(武器)的顶点,其UV映射的区域越接近贴图的底部:

image

越接近刀光尾部的顶点,其UV映射的区域越接近贴图的上部:

image

结合实际效果查看刀光的强弱,就更清晰了:

image

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,558评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,002评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,036评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,024评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,144评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,255评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,295评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,068评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,478评论 1 305
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,789评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,965评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,649评论 4 336
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,267评论 3 318
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,982评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,223评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,800评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,847评论 2 351

推荐阅读更多精彩内容