少年修仙传客户端代码仓库
client_Wu Xijin
2018-09-21 d46c551a2a828eb73bb82fae659a03603ee9f638
3335 添加拖尾插件
20个文件已添加
1691 ■■■■■ 已修改文件
Plugins.meta 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugins/Trails.meta 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugins/Trails/CircularBuffer.cs 287 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugins/Trails/CircularBuffer.cs.meta 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugins/Trails/GizmosExtra.cs 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugins/Trails/GizmosExtra.cs.meta 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugins/Trails/Range.cs 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugins/Trails/Range.cs.meta 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugins/Trails/SmokePlume.cs 74 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugins/Trails/SmokePlume.cs.meta 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugins/Trails/SmokeTrail.cs 76 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugins/Trails/SmokeTrail.cs.meta 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugins/Trails/SmoothTrail.cs 313 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugins/Trails/SmoothTrail.cs.meta 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugins/Trails/Trail.cs 60 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugins/Trails/Trail.cs.meta 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugins/Trails/TrailRenderer_Base.cs 666 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugins/Trails/TrailRenderer_Base.cs.meta 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugins/Trails/VersionInformation.cs 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugins/Trails/VersionInformation.cs.meta 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugins.meta
New file
@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: d463e02633381cd46902c21b8d51f83e
folderAsset: yes
timeCreated: 1537519875
licenseType: Pro
DefaultImporter:
  userData:
  assetBundleName:
  assetBundleVariant:
Plugins/Trails.meta
New file
@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 77d1032b5eb62ce4da86e600bc8884c8
folderAsset: yes
timeCreated: 1537519883
licenseType: Pro
DefaultImporter:
  userData:
  assetBundleName:
  assetBundleVariant:
Plugins/Trails/CircularBuffer.cs
New file
@@ -0,0 +1,287 @@
using System;
using System.Collections;
using System.Collections.Generic;
namespace PigeonCoopToolkit.Utillities
{
    public class CircularBuffer<T> : IList<T>, ICollection<T>,
                                IEnumerable<T>, IEnumerable
    {
      /// <summary>
      /// Creates a new instance of a <see cref="RingBuffer&lt;T&gt;"/> with a
      /// specified cache size.
      // http://florianreischl.blogspot.com/2010/01/generic-c-ringbuffer.html
      /// </summary>
      /// <param name="capacity">The maximal count of items to be stored within
      /// the ring buffer.</param>
        public CircularBuffer(int capacity)
        {
         // validate capacity
         if (capacity <= 0)
            throw new ArgumentException("Must be greater than zero", "capacity");
         // set capacity and init the cache
         Capacity = capacity;
         _buffer = new T[capacity];
      }
      /// <summary>
      /// the internal buffer
      /// </summary>
      T[] _buffer;
      /// <summary>
      /// The all-over position within the ring buffer. The position
      /// increases continously by adding new items to the buffer. This
      /// value is needed to calculate the current relative position within the
      /// buffer.
      /// </summary>
      int _position;
      /// <summary>
      /// The current version of the buffer, this is required for a correct
      /// exception handling while enumerating over the items of the buffer.
      /// </summary>
      long _version;
      /// <summary>
      /// Gets or sets an item for a specified position within the ring buffer.
      /// </summary>
      /// <param name="index">The position to get or set an item.</param>
      /// <returns>The fond item at the specified position within the ring buffer.
      /// </returns>
      /// <exception cref="IndexOutOfRangeException"></exception>
      public T this[int index] {
         get {
            // validate the index
            if (index < 0 || index >= Count)
               throw new IndexOutOfRangeException();
            // calculate the relative position within the rolling base array
            int index2 = (_position - Count + index) % Capacity;
            return _buffer[index2];
         }
         set { Insert(index, value); }
      }
      /// <summary>
      /// Gets the maximal count of items within the ring buffer.
      /// </summary>
      public int Capacity { get; private set; }
      /// <summary>
      /// Get the current count of items within the ring buffer.
      /// </summary>
      public int Count { get; private set; }
      /// <summary>
      /// Adds a new item to the buffer.
      /// </summary>
      /// <param name="item">The item to be added to the buffer.</param>
      public void Add(T item) {
         // add a new item to the current relative position within the
         // buffer and increase the position
         _buffer[_position++ % Capacity] = item;
         // increase the count if capacity is not yet reached
         if (Count < Capacity) Count++;
         // buffer changed; next version
         _version++;
      }
      /// <summary>
      /// Clears the whole buffer and releases all referenced objects
      /// currently stored within the buffer.
      /// </summary>
      public void Clear() {
         for (int i = 0; i < Count; i++)
            _buffer[i] = default(T);
         _position = 0;
         Count = 0;
         _version++;
      }
      /// <summary>
      /// Determines if a specified item is currently present within
      /// the buffer.
      /// </summary>
      /// <param name="item">The item to search for within the current
      /// buffer.</param>
      /// <returns>True if the specified item is currently present within
      /// the buffer; otherwise false.</returns>
      public bool Contains(T item) {
         int index = IndexOf(item);
         return index != -1;
      }
      /// <summary>
      /// Copies the current items within the buffer to a specified array.
      /// </summary>
      /// <param name="array">The target array to copy the items of
      /// the buffer to.</param>
      /// <param name="arrayIndex">The start position witihn the target
      /// array to start copying.</param>
      public void CopyTo(T[] array, int arrayIndex) {
         for (int i = 0; i < Count; i++) {
            array[i + arrayIndex] = _buffer[(_position - Count + i) % Capacity];
         }
      }
      /// <summary>
      /// Gets an enumerator over the current items within the buffer.
      /// </summary>
      /// <returns>An enumerator over the current items within the buffer.
      /// </returns>
      public IEnumerator<T> GetEnumerator() {
         long version = _version;
         for (int i = 0; i < Count; i++) {
            if (version != _version)
               throw new InvalidOperationException("Collection changed");
            yield return this[i];
         }
      }
      /// <summary>
      /// Gets the position of a specied item within the ring buffer.
      /// </summary>
      /// <param name="item">The item to get the current position for.</param>
      /// <returns>The zero based index of the found item within the
      /// buffer. If the item was not present within the buffer, this
      /// method returns -1.</returns>
      public int IndexOf(T item) {
         // loop over the current count of items
         for (int i = 0; i < Count; i++) {
            // get the item at the relative position within the internal array
            T item2 = _buffer[(_position - Count + i) % Capacity];
            // if both items are null, return true
            if (null == item && null == item2)
               return i;
            // if equal return the position
            if (item != null && item.Equals(item2))
               return i;
         }
         // nothing found
         return -1;
      }
      /// <summary>
      /// Inserts an item at a specified position into the buffer.
      /// </summary>
      /// <param name="index">The position within the buffer to add
      /// the new item.</param>
      /// <param name="item">The new item to be added to the buffer.</param>
      /// <exception cref="IndexOutOfRangeException"></exception>
      /// <remarks>
      /// If the specified index is equal to the current count of items
      /// within the buffer, the specified item will be added.
      ///
      /// <b>Warning</b>
      /// Frequent usage of this method might become a bad idea if you are
      /// working with a large buffer capacity. The insertion of an item
      /// at a specified position within the buffer causes causes all present
      /// items below the specified position to be moved one position.
      /// </remarks>
      public void Insert(int index, T item) {
         // validate index
         if (index < 0 || index > Count)
            throw new IndexOutOfRangeException();
         // add if index equals to count
         if (index == Count) {
            Add(item);
            return;
         }
         // get the maximal count of items to be moved
         int count = Math.Min(Count, Capacity - 1) - index;
         // get the relative position of the new item within the buffer
         int index2 = (_position - Count + index) % Capacity;
         // move all items below the specified position
         for (int i = index2 + count; i > index2; i--) {
            int to = i % Capacity;
            int from = (i - 1) % Capacity;
            _buffer[to] = _buffer[from];
         }
         // set the new item
         _buffer[index2] = item;
         // adjust storage information
         if (Count < Capacity) {
            Count++;
            _position++;
         }
         // buffer changed; next version
         _version++;
      }
      /// <summary>
      /// Removes a specified item from the current buffer.
      /// </summary>
      /// <param name="item">The item to be removed.</param>
      /// <returns>True if the specified item was successfully removed
      /// from the buffer; otherwise false.</returns>
      /// <remarks>
      /// <b>Warning</b>
      /// Frequent usage of this method might become a bad idea if you are
      /// working with a large buffer capacity. The removing of an item
      /// requires a scan of the buffer to get the position of the specified
      /// item. If the item was found, the deletion requires a move of all
      /// items stored abouve the found position.
      /// </remarks>
      public bool Remove(T item) {
         // find the position of the specified item
         int index = IndexOf(item);
         // item was not found; return false
         if (index == -1)
            return false;
         // remove the item at the specified position
         RemoveAt(index);
         return true;
      }
      /// <summary>
      /// Removes an item at a specified position within the buffer.
      /// </summary>
      /// <param name="index">The position of the item to be removed.</param>
      /// <exception cref="IndexOutOfRangeException"></exception>
      /// <remarks>
      /// <b>Warning</b>
      /// Frequent usage of this method might become a bad idea if you are
      /// working with a large buffer capacity. The deletion requires a move
      /// of all items stored abouve the found position.
      /// </remarks>
      public void RemoveAt(int index) {
         // validate the index
         if (index < 0 || index >= Count)
            throw new IndexOutOfRangeException();
         // move all items above the specified position one step
         // closer to zeri
         for (int i = index; i < Count - 1; i++) {
            // get the next relative target position of the item
            int to = (_position - Count + i) % Capacity;
            // get the next relative source position of the item
            int from = (_position - Count + i + 1) % Capacity;
            // move the item
            _buffer[to] = _buffer[from];
         }
         // get the relative position of the last item, which becomes empty
         // after deletion and set the item as empty
         int last = (_position - 1) % Capacity;
         _buffer[last] = default(T);
         // adjust storage information
         _position--;
         Count--;
         // buffer changed; next version
         _version++;
      }
      /// <summary>
      /// Gets if the buffer is read-only. This method always returns false.
      /// </summary>
      bool ICollection<T>.IsReadOnly { get { return false; } }
      /// <summary>
      /// See generic implementation of <see cref="GetEnumerator"/>.
      /// </summary>
      /// <returns>See generic implementation of <see cref="GetEnumerator"/>.
      /// </returns>
      IEnumerator IEnumerable.GetEnumerator() {
         return this.GetEnumerator();
      }
   }
}
Plugins/Trails/CircularBuffer.cs.meta
New file
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 51a19be087405654c8b4b50c67fa9200
MonoImporter:
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
Plugins/Trails/GizmosExtra.cs
New file
@@ -0,0 +1,33 @@
using UnityEngine;
namespace PigeonCoopToolkit.Utillities
{
    public static class GizmosExtra
    {
        public static void GizmosDrawCircle(Vector3 position, Vector3 up, float size, int divisions)
        {
            Vector3 offset = (Quaternion.Euler(90,0,0) * (up* size)) ;
            for (int i = 0; i < divisions; i++)
            {
                Vector3 newOffset = Quaternion.AngleAxis(360f / divisions, up) * offset;
                Gizmos.DrawLine(position + offset, position + newOffset);
                offset = newOffset;
            }
        }
        public static void GizmosDrawArrow(Vector3 from, Vector3 to, float arrowSize)
        {
            Gizmos.DrawLine(from, to);
            Vector3 dir = to - from;
            dir = dir.normalized*arrowSize;
            Gizmos.DrawLine(to, to - Quaternion.Euler(0, 0, 45)*dir);
            Gizmos.DrawLine(to, to - Quaternion.Euler(0, 0, -45)*dir);
        }
    }
}
Plugins/Trails/GizmosExtra.cs.meta
New file
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: ac8be081485ba1141bd28334fe9eabec
MonoImporter:
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
Plugins/Trails/Range.cs
New file
@@ -0,0 +1,15 @@
namespace PigeonCoopToolkit.Utillities
{
    [System.Serializable]
    public class Range
    {
        public float Min;
        public float Max;
        public bool WithinRange(float value)
        {
            return Min <= value && Max >= value;
        }
    }
}
Plugins/Trails/Range.cs.meta
New file
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: db4c92c149ebf004a83b3776345ca8d4
MonoImporter:
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
Plugins/Trails/SmokePlume.cs
New file
@@ -0,0 +1,74 @@
using UnityEngine;
namespace PigeonCoopToolkit.Effects.Trails
{
    [AddComponentMenu("Pigeon Coop Toolkit/Effects/Smoke Plume")]
    public class SmokePlume : TrailRenderer_Base
    {
        public float TimeBetweenPoints = 0.1f;
        public Vector3 ConstantForce = Vector3.up * 0.5f;
        public float RandomForceScale = 0.05f;
        public int MaxNumberOfPoints = 50;
        private float _timeSincePoint;
        protected override void Start()
        {
            base.Start();
            _timeSincePoint = 0;
        }
        protected override void OnStartEmit()
        {
            _timeSincePoint = 0;
        }
        protected override void Reset()
        {
            base.Reset();
            TrailData.SizeOverLife = new AnimationCurve(new Keyframe(0, 0), new Keyframe(0.5f, 0.2f), new Keyframe(1, 0.2f));
            TrailData.Lifetime = 6f;
            ConstantForce = Vector3.up*0.5f;
            TimeBetweenPoints = 0.1f;
            RandomForceScale = 0.05f;
            MaxNumberOfPoints = 50;
        }
        protected override void Update()
        {
            if (_emit)
            {
                _timeSincePoint += _noDecay ? 0 : Time.deltaTime;
                if (_timeSincePoint >= TimeBetweenPoints)
                {
                    AddPoint(new SmokeTrailPoint(), _t.position);
                    _timeSincePoint = 0;
                }
            }
            base.Update();
        }
        protected override void InitialiseNewPoint(PCTrailPoint newPoint)
        {
            ((SmokeTrailPoint)newPoint).RandomVec = Random.onUnitSphere * RandomForceScale;
        }
        protected override void UpdateTrail(PCTrail trail, float deltaTime)
        {
            if (_noDecay)
                return;
            foreach (PCTrailPoint point in trail.Points)
            {
                point.Position += ConstantForce * deltaTime;
            }
        }
        protected override int GetMaxNumberOfPoints()
        {
            return MaxNumberOfPoints;
        }
    }
}
Plugins/Trails/SmokePlume.cs.meta
New file
@@ -0,0 +1,15 @@
fileFormatVersion: 2
guid: b5748bb800508004a81afe3d7526bcb1
labels:
- BetterTrails
- TrailRenderer
- Smoke
- Trail
- Effects
- skidmarks
MonoImporter:
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 1000
  icon: {fileID: 2800000, guid: 9c8d55a9e91d98e4e925cd09f877adc5, type: 3}
  userData:
Plugins/Trails/SmokeTrail.cs
New file
@@ -0,0 +1,76 @@
using UnityEngine;
namespace PigeonCoopToolkit.Effects.Trails
{
    [AddComponentMenu("Pigeon Coop Toolkit/Effects/Smoke Trail")]
    public class SmokeTrail : TrailRenderer_Base
    {
        public float MinVertexDistance = 0.1f;
        public int MaxNumberOfPoints = 50;
        private Vector3 _lastPosition;
        private float _distanceMoved;
        public float RandomForceScale = 1;
        protected override void Start()
        {
            base.Start();
            _lastPosition = _t.position;
        }
        protected override void Update()
        {
            if (_emit)
            {
                _distanceMoved += Vector3.Distance(_t.position, _lastPosition);
                if (_distanceMoved != 0 && _distanceMoved >= MinVertexDistance)
                {
                    AddPoint(new SmokeTrailPoint(), _t.position);
                    _distanceMoved = 0;
                }
                _lastPosition = _t.position;
            }
            base.Update();
        }
        protected override void OnStartEmit()
        {
            _lastPosition = _t.position;
            _distanceMoved = 0;
        }
        protected override void Reset()
        {
            base.Reset();
            MinVertexDistance = 0.1f;
            RandomForceScale = 1;
        }
        protected override void InitialiseNewPoint(PCTrailPoint newPoint)
        {
            ((SmokeTrailPoint)newPoint).RandomVec = Random.onUnitSphere * RandomForceScale;
        }
        protected override void OnTranslate(Vector3 t)
        {
            _lastPosition += t;
        }
        protected override int GetMaxNumberOfPoints()
        {
            return MaxNumberOfPoints;
        }
    }
    public class SmokeTrailPoint : PCTrailPoint
    {
        public Vector3 RandomVec;
        public override void Update(float deltaTime)
        {
            base.Update(deltaTime);
            Position += RandomVec * deltaTime;
        }
    }
}
Plugins/Trails/SmokeTrail.cs.meta
New file
@@ -0,0 +1,15 @@
fileFormatVersion: 2
guid: 98335f20237e3de42a9f0b6415312e23
labels:
- BetterTrails
- TrailRenderer
- Smoke
- Trail
- Effects
- skidmarks
MonoImporter:
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 1000
  icon: {fileID: 2800000, guid: f99224b2104474d47b91284e88f8af87, type: 3}
  userData:
Plugins/Trails/SmoothTrail.cs
New file
@@ -0,0 +1,313 @@
using PigeonCoopToolkit.Utillities;
using UnityEngine;
namespace PigeonCoopToolkit.Effects.Trails
{
    [AddComponentMenu("Pigeon Coop Toolkit/Effects/Smooth Trail")]
    public class SmoothTrail : TrailRenderer_Base
    {
        public float MinControlPointDistance = 0.1f;
        public int MaxControlPoints = 15;
        public int PointsBetweenControlPoints = 4;
        private Vector3 _lastPosition;
        private float _distanceMoved;
        private CircularBuffer<ControlPoint> _controlPoints;
        private class ControlPoint
        {
            public Vector3 p;
            public Vector3 forward;
        }
        protected override void Start()
        {
            base.Start();
            _lastPosition = _t.position;
        }
        protected override void Update()
        {
            if (_emit)
            {
                _distanceMoved += Vector3.Distance(_t.position, _lastPosition);
                if (!Mathf.Approximately(_distanceMoved, 0) && _distanceMoved >= MinControlPointDistance)
                {
                    AddControlPoint(_t.position);
                    _distanceMoved = 0;
                }
                else
                {
                    _controlPoints[_controlPoints.Count - 1].p = _t.position;
                    if (TrailData.UseForwardOverride)
                    {
                        _controlPoints[_controlPoints.Count - 1].forward = TrailData.ForwardOverrideRelative
                                       ? _t.TransformDirection(TrailData.ForwardOverride.normalized)
                                       : TrailData.ForwardOverride.normalized;
                    }
                }
                _lastPosition = _t.position;
            }
            base.Update();
        }
        /*
        void OnDrawGizmos()
        {
            if (Application.isPlaying && _controlPoints != null)
            {
                for (int i = 0; i < _controlPoints.Count; i++)
                {
                    Gizmos.color = Color.red;
                    Gizmos.DrawSphere(_controlPoints[i].p, 0.01f);
                    if (i < _controlPoints.Count - 1)
                    {
                        Vector3 Handle1, Handle2;
                        float distanceBetween = Vector3.Distance(_controlPoints[i].p, _controlPoints[i + 1].p) / 2;
                        if (i == 0)
                        {
                            Handle1 = _controlPoints[i].p + (_controlPoints[i + 1].p - _controlPoints[i].p).normalized * distanceBetween;
                        }
                        else
                        {
                            Handle1 = _controlPoints[i].p + (_controlPoints[i + 1].p - _controlPoints[i - 1].p).normalized * distanceBetween;
                        }
                        int nextI = i + 1;
                        if (nextI == _controlPoints.Count - 1)
                        {
                            Handle2 = _controlPoints[nextI].p + (_controlPoints[nextI - 1].p - _controlPoints[nextI].p).normalized * distanceBetween;
                        }
                        else
                        {
                            Handle2 = _controlPoints[nextI].p + (_controlPoints[nextI - 1].p - _controlPoints[nextI + 1].p).normalized * distanceBetween;
                        }
                        Gizmos.color = Color.green;
                        Gizmos.DrawSphere(Handle1, 0.01f);
                        Gizmos.DrawLine(_controlPoints[i].p, Handle1);
                        Gizmos.color = Color.blue;
                        Gizmos.DrawSphere(Handle2, 0.01f);
                        Gizmos.DrawLine(_controlPoints[nextI].p, Handle2);
                        Vector3 current = _controlPoints[i].p;
                        for (int pointBetween = 0; pointBetween < PointsBetweenControlPoints; pointBetween++)
                        {
                            Vector3 next = GetPointAlongCurve(_controlPoints[i].p, Handle1, _controlPoints[i + 1].p, Handle2, ((pointBetween + 1) / ((float)PointsBetweenControlPoints + 1f)), 0.3f);
                            Gizmos.DrawLine(current, next);
                            Gizmos.color = Color.yellow;
                            Gizmos.DrawSphere(next, 0.01f);
                            current = next;
                        }
                        Gizmos.color = Color.blue;
                        Gizmos.DrawLine(current, _controlPoints[i + 1].p);
                    }
                }
            }
        }
        */
        protected override void OnStartEmit()
        {
            _lastPosition = _t.position;
            _distanceMoved = 0;
            _controlPoints = new CircularBuffer<ControlPoint>(MaxControlPoints);
            _controlPoints.Add(new ControlPoint { p = _lastPosition });
            if (TrailData.UseForwardOverride)
            {
                _controlPoints[0].forward = TrailData.ForwardOverrideRelative
                               ? _t.TransformDirection(TrailData.ForwardOverride.normalized)
                               : TrailData.ForwardOverride.normalized;
            }
            AddPoint(new PCTrailPoint(), _lastPosition);
            AddControlPoint(_lastPosition);
        }
        protected override void UpdateTrail(PCTrail trail, float deltaTime)
        {
            if (trail.IsActiveTrail == false)
                return;
            //TODO Must optimize this further, I don't need to keep recalculating point positions unless we are
            //dealng with the points between the last and 2nd last CP (+ the last CP itself)
            int trailPointIndex = 0;
            for (int i = 0; i < _controlPoints.Count; i++)
            {
                trail.Points[trailPointIndex].Position = _controlPoints[i].p;
                if (TrailData.UseForwardOverride)
                {
                    trail.Points[trailPointIndex].Forward = _controlPoints[i].forward;
                }
                trailPointIndex++;
                if (i < _controlPoints.Count - 1)
                {
                    Vector3 Handle1, Handle2;
                    float distanceBetween = Vector3.Distance(_controlPoints[i].p, _controlPoints[i + 1].p) / 2;
                    if (i == 0)
                    {
                        Handle1 = _controlPoints[i].p + (_controlPoints[i + 1].p - _controlPoints[i].p).normalized * distanceBetween;
                    }
                    else
                    {
                        Handle1 = _controlPoints[i].p + (_controlPoints[i + 1].p - _controlPoints[i - 1].p).normalized * distanceBetween;
                    }
                    int nextI = i + 1;
                    if (nextI == _controlPoints.Count - 1)
                    {
                        Handle2 = _controlPoints[nextI].p + (_controlPoints[nextI - 1].p - _controlPoints[nextI].p).normalized * distanceBetween;
                    }
                    else
                    {
                        Handle2 = _controlPoints[nextI].p + (_controlPoints[nextI - 1].p - _controlPoints[nextI + 1].p).normalized * distanceBetween;
                    }
                    PCTrailPoint currentHandle = trail.Points[trailPointIndex-1];
                    PCTrailPoint nextHandle = trail.Points[(trailPointIndex-1) + PointsBetweenControlPoints+1];
                    for (int pointBetween = 0; pointBetween < PointsBetweenControlPoints; pointBetween++)
                    {
                        float t= (((float)pointBetween + 1f) / ((float)PointsBetweenControlPoints + 1f));
                        trail.Points[trailPointIndex].Position = GetPointAlongCurve(_controlPoints[i].p, Handle1, _controlPoints[i + 1].p, Handle2, t, 0.3f);
                        trail.Points[trailPointIndex].SetTimeActive(Mathf.Lerp(currentHandle.TimeActive(), nextHandle.TimeActive(), t));
                        if (TrailData.UseForwardOverride)
                        {
                            trail.Points[trailPointIndex].Forward = Vector3.Lerp(currentHandle.Forward, nextHandle.Forward, t);
                        }
                        trailPointIndex++;
                    }
                }
            }
            int lastControlPointPointIndex = ((_controlPoints.Count - 1) + ((_controlPoints.Count - 1) * PointsBetweenControlPoints));
            int prevControlPointPointIndex = lastControlPointPointIndex - PointsBetweenControlPoints - 1;
            int activePointCount = lastControlPointPointIndex + 1;
            /*
             *
             * This is that optimisation mentioned above..sort of works, but smoothing isn't working right so i am leaving it out for now
             *
             *
            int lastControlPointIndex = _controlPoints.Count - 1;
            int prevControlPointIndex = _controlPoints.Count - 2;
            Vector3 Handle1, Handle2;
            float distanceBetween = Vector3.Distance(_controlPoints[lastControlPointIndex].p, _controlPoints[prevControlPointIndex].p) / 2;
            if (prevControlPointIndex == 0)
                Handle1 = _controlPoints[prevControlPointIndex].p + (_controlPoints[lastControlPointIndex].p - _controlPoints[prevControlPointIndex].p).normalized * distanceBetween;
            else
                Handle1 = _controlPoints[prevControlPointIndex].p + (_controlPoints[lastControlPointIndex].p - _controlPoints[prevControlPointIndex - 1].p).normalized * distanceBetween;
            Handle2 = _controlPoints[lastControlPointIndex].p + (_controlPoints[prevControlPointIndex].p - _controlPoints[lastControlPointIndex].p).normalized * distanceBetween;
            float timeActiveLastControlPoint = trail.Points[lastControlPointPointIndex].TimeActive();
            float timeActivePrevControlPoint = trail.Points[prevControlPointPointIndex].TimeActive();
            for (int pointBetween = 0; pointBetween < PointsBetweenControlPoints; pointBetween++)
            {
                float t= (((float)pointBetween + 1f) / ((float)PointsBetweenControlPoints + 1f));
                trail.Points[prevControlPointPointIndex + pointBetween + 1].Position = GetPointAlongCurve(_controlPoints[prevControlPointIndex].p, Handle1, _controlPoints[lastControlPointIndex].p, Handle2, t, 0.3f);
                trail.Points[prevControlPointPointIndex + pointBetween + 1].SetTimeActive(Mathf.Lerp(timeActivePrevControlPoint, timeActiveLastControlPoint, t));
            }
            trail.Points[lastControlPointPointIndex].Position = _controlPoints[lastControlPointIndex].p;
            */
            float distanceFromStart = trail.Points[prevControlPointPointIndex].GetDistanceFromStart();
            for (int i = prevControlPointPointIndex + 1; i < activePointCount; i++)
            {
                distanceFromStart += Vector3.Distance(trail.Points[i - 1].Position, trail.Points[i].Position);
                trail.Points[i].SetDistanceFromStart(distanceFromStart);
            }
        }
        protected override void Reset()
        {
            base.Reset();
            MinControlPointDistance = 0.1f;
            MaxControlPoints = 15;
            PointsBetweenControlPoints = 4;
        }
        protected override void OnTranslate(Vector3 t)
        {
            _lastPosition += t;
            for (int i = 0; i < _controlPoints.Count; i++)
                _controlPoints[i].p += t;
        }
        private void AddControlPoint(Vector3 position)
        {
            for (int i = 0; i < PointsBetweenControlPoints; i++)
            {
                AddPoint(new PCTrailPoint(), position);
            }
            AddPoint(new PCTrailPoint(), position);
            ControlPoint newCP = new ControlPoint { p = position };
            if (TrailData.UseForwardOverride)
            {
                newCP.forward = TrailData.ForwardOverrideRelative
                               ? _t.TransformDirection(TrailData.ForwardOverride.normalized)
                               : TrailData.ForwardOverride.normalized;
            }
            _controlPoints.Add(newCP);
        }
        protected override int GetMaxNumberOfPoints()
        {
            return ((MaxControlPoints) + ((MaxControlPoints) * PointsBetweenControlPoints));
        }
        public Vector3 GetPointAlongCurve(Vector3 curveStart, Vector3 curveStartHandle, Vector3 curveEnd, Vector3 curveEndHandle, float t, float crease)
        {
            float oneMinT = 1 - t;
            float oneMinTPow3 = Mathf.Pow(oneMinT, 3);
            float oneMinTPow2 = Mathf.Pow(oneMinT, 2);
            float oneMinCrease = 1 - crease;
            return ((oneMinTPow3 * curveStart * oneMinCrease) + (3 * oneMinTPow2 * t * curveStartHandle * crease) + (3 * oneMinT * Mathf.Pow(t, 2) * curveEndHandle * crease) +
                   (Mathf.Pow(t, 3) * curveEnd * oneMinCrease))
                   /
                   ((oneMinTPow3 * oneMinCrease) + (3 * oneMinTPow2 * t * crease) + (3 * oneMinT * Mathf.Pow(t, 2) * crease) +
                   (Mathf.Pow(t, 3) * oneMinCrease));
        }
    }
}
Plugins/Trails/SmoothTrail.cs.meta
New file
@@ -0,0 +1,15 @@
fileFormatVersion: 2
guid: 62cd7e0b3a08cf84e82658d9a3eaeb32
labels:
- BetterTrails
- TrailRenderer
- Smoke
- Trail
- Effects
- skidmarks
MonoImporter:
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 1000
  icon: {fileID: 2800000, guid: 8d56c6f37ee95884baa3b4f7e4837399, type: 3}
  userData:
Plugins/Trails/Trail.cs
New file
@@ -0,0 +1,60 @@
using UnityEngine;
namespace PigeonCoopToolkit.Effects.Trails
{
    [AddComponentMenu("Pigeon Coop Toolkit/Effects/Trail")]
    public class Trail : TrailRenderer_Base
    {
        public float MinVertexDistance = 0.1f;
        public int MaxNumberOfPoints = 50;
        private Vector3 _lastPosition;
        private float _distanceMoved;
        protected override void Start()
        {
            base.Start();
            _lastPosition = _t.position;
        }
        protected override void Update()
        {
            if(_emit)
            {
                _distanceMoved += Vector3.Distance(_t.position, _lastPosition);
                if (_distanceMoved != 0 && _distanceMoved >= MinVertexDistance)
                {
                    AddPoint(new PCTrailPoint(), _t.position);
                    _distanceMoved = 0;
                }
                _lastPosition = _t.position;
            }
            base.Update();
        }
        protected override void OnStartEmit()
        {
            _lastPosition = _t.position;
            _distanceMoved = 0;
        }
        protected override void Reset()
        {
            base.Reset();
            MinVertexDistance = 0.1f;
        }
        protected override void OnTranslate(Vector3 t)
        {
            _lastPosition += t;
        }
        protected override int GetMaxNumberOfPoints()
        {
            return MaxNumberOfPoints;
        }
    }
}
Plugins/Trails/Trail.cs.meta
New file
@@ -0,0 +1,15 @@
fileFormatVersion: 2
guid: 5f742ccbd25717e438dd6fdea4d66bad
labels:
- BetterTrails
- TrailRenderer
- Smoke
- Trail
- Effects
- skidmarks
MonoImporter:
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 1000
  icon: {fileID: 2800000, guid: 888165c9c85ad6b429824b89f1f4f698, type: 3}
  userData:
Plugins/Trails/TrailRenderer_Base.cs
New file
@@ -0,0 +1,666 @@
using System.Collections.Generic;
using System.Linq;
using PigeonCoopToolkit.Utillities;
using UnityEngine;
using System;
namespace PigeonCoopToolkit.Effects.Trails
{
    public abstract class TrailRenderer_Base : MonoBehaviour
    {
        public PCTrailRendererData TrailData;
        public bool Emit = false;
        protected bool _emit;
        protected bool _noDecay;
        private PCTrail _activeTrail;
        private List<PCTrail> _fadingTrails;
        protected Transform _t;
        private static Dictionary<Material, List<PCTrail>> _matToTrailList;
        private static List<Mesh> _toClean;
        private static bool _hasRenderer = false;
        private static int GlobalTrailRendererCount = 0;
        protected virtual void Awake()
        {
            GlobalTrailRendererCount++;
            if(GlobalTrailRendererCount == 1)
            {
                _matToTrailList = new Dictionary<Material, List<PCTrail>>();
                _toClean = new List<Mesh>();
            }
            _fadingTrails = new List<PCTrail>();
            _t = transform;
            _emit = Emit;
            if (_emit)
            {
                _activeTrail = new PCTrail(GetMaxNumberOfPoints());
                _activeTrail.IsActiveTrail = true;
                OnStartEmit();
            }
        }
        protected virtual void Start()
        {
        }
        protected virtual void LateUpdate()
        {
            if(_hasRenderer)
                return;
            _hasRenderer = true;
            foreach (KeyValuePair<Material, List<PCTrail>> keyValuePair in _matToTrailList)
            {
                CombineInstance[] combineInstances = new CombineInstance[keyValuePair.Value.Count];
                for (int i = 0; i < keyValuePair.Value.Count; i++)
                {
                    combineInstances[i] = new CombineInstance
                    {
                        mesh = keyValuePair.Value[i].Mesh,
                        subMeshIndex = 0,
                        transform = Matrix4x4.identity
                    };
                }
                Mesh combinedMesh = new Mesh();
                combinedMesh.CombineMeshes(combineInstances, true, false);
                _toClean.Add(combinedMesh);
                DrawMesh(combinedMesh, keyValuePair.Key);
                keyValuePair.Value.Clear();
            }
        }
        protected virtual void Update()
        {
            if (_hasRenderer)
            {
                _hasRenderer = false;
                if (_toClean.Count > 0)
                {
                    foreach (Mesh mesh in _toClean)
                    {
                        if (Application.isEditor)
                            DestroyImmediate(mesh, true);
                        else
                            Destroy(mesh);
                    }
                }
                _toClean.Clear();
            }
            if (_matToTrailList.ContainsKey(TrailData.TrailMaterial) == false)
            {
                _matToTrailList.Add(TrailData.TrailMaterial, new List<PCTrail>());
            }
            if(_activeTrail != null)
            {
                UpdatePoints(_activeTrail, Time.deltaTime);
                UpdateTrail(_activeTrail, Time.deltaTime);
                GenerateMesh(_activeTrail);
                _matToTrailList[TrailData.TrailMaterial].Add(_activeTrail);
            }
            for (int i = _fadingTrails.Count-1; i >= 0; i--)
            {
                if (_fadingTrails[i] == null || _fadingTrails[i].Points.Any(a => a.TimeActive() < TrailData.Lifetime) == false)
                {
                    if (_fadingTrails[i] != null)
                        _fadingTrails[i].Dispose();
                    _fadingTrails.RemoveAt(i);
                    continue;
                }
                UpdatePoints(_fadingTrails[i], Time.deltaTime);
                UpdateTrail(_fadingTrails[i], Time.deltaTime);
                GenerateMesh(_fadingTrails[i]);
                _matToTrailList[TrailData.TrailMaterial].Add(_fadingTrails[i]);
            }
            CheckEmitChange();
        }
        protected virtual void OnDestroy()
        {
            GlobalTrailRendererCount--;
            if(GlobalTrailRendererCount == 0)
            {
                if(_toClean != null && _toClean.Count > 0)
                {
                    foreach (Mesh mesh in _toClean)
                    {
                        if (Application.isEditor)
                            DestroyImmediate(mesh, true);
                        else
                            Destroy(mesh);
                    }
                }
                _toClean = null;
                _matToTrailList.Clear();
                _matToTrailList = null;
            }
            if (_activeTrail != null)
            {
                _activeTrail.Dispose();
                _activeTrail = null;
            }
            if (_fadingTrails != null)
            {
                foreach (PCTrail fadingTrail in _fadingTrails)
                {
                    if (fadingTrail != null)
                        fadingTrail.Dispose();
                }
                _fadingTrails.Clear();
            }
        }
        protected virtual void OnStopEmit()
        {
        }
        protected virtual void OnStartEmit()
        {
        }
        protected virtual void OnTranslate(Vector3 t)
        {
        }
        protected abstract int GetMaxNumberOfPoints();
        protected virtual void Reset()
        {
            if(TrailData == null)
                TrailData = new PCTrailRendererData();
            TrailData.Lifetime = 1;
            TrailData.UsingSimpleColor = false;
            TrailData.UsingSimpleSize = false;
            TrailData.ColorOverLife = new Gradient();
            TrailData.SimpleColorOverLifeStart = Color.white;
            TrailData.SimpleColorOverLifeEnd = new Color(1, 1, 1, 0);
            TrailData.SizeOverLife = new AnimationCurve(new Keyframe(0, 1), new Keyframe(1, 0));
            TrailData.SimpleSizeOverLifeStart = 1;
            TrailData.SimpleSizeOverLifeEnd = 0;
        }
        protected virtual void InitialiseNewPoint(PCTrailPoint newPoint)
        {
        }
        protected virtual void UpdateTrail(PCTrail trail, float deltaTime)
        {
        }
        protected void AddPoint(PCTrailPoint newPoint, Vector3 pos)
        {
            if (_activeTrail == null)
                return;
            newPoint.Position = pos;
            newPoint.PointNumber = _activeTrail.Points.Count == 0 ? 0 : _activeTrail.Points[_activeTrail.Points.Count - 1].PointNumber + 1;
            InitialiseNewPoint(newPoint);
            newPoint.SetDistanceFromStart(_activeTrail.Points.Count == 0
                                              ? 0
                                              : _activeTrail.Points[_activeTrail.Points.Count - 1].GetDistanceFromStart() + Vector3.Distance(_activeTrail.Points[_activeTrail.Points.Count - 1].Position, pos));
            if(TrailData.UseForwardOverride)
            {
                newPoint.Forward = TrailData.ForwardOverrideRelative
                                       ? _t.TransformDirection(TrailData.ForwardOverride.normalized)
                                       : TrailData.ForwardOverride.normalized;
            }
            _activeTrail.Points.Add(newPoint);
        }
        private void GenerateMesh(PCTrail trail)
        {
            trail.Mesh.Clear(false);
            Vector3 camForward = Camera.main != null ? Camera.main.transform.forward : Vector3.forward;
            if(TrailData.UseForwardOverride)
            {
                camForward = TrailData.ForwardOverride.normalized;
            }
            trail.activePointCount = NumberOfActivePoints(trail);
            if (trail.activePointCount < 2)
                return;
            int vertIndex = 0;
            for (int i = 0; i < trail.Points.Count; i++)
            {
                PCTrailPoint p = trail.Points[i];
                float timeAlong = p.TimeActive()/TrailData.Lifetime;
                if(p.TimeActive() > TrailData.Lifetime)
                {
                    continue;
                }
                if (TrailData.UseForwardOverride && TrailData.ForwardOverrideRelative)
                    camForward = p.Forward;
                Vector3 cross = Vector3.zero;
                if (i < trail.Points.Count - 1)
                {
                    cross =
                        Vector3.Cross((trail.Points[i + 1].Position - p.Position).normalized, camForward).
                            normalized;
                }
                else
                {
                    cross =
                        Vector3.Cross((p.Position - trail.Points[i - 1].Position).normalized, camForward).
                            normalized;
                }
                //yuck! lets move these into their own functions some time
                Color c = TrailData.StretchColorToFit ?
                    (TrailData.UsingSimpleColor ? Color.Lerp(TrailData.SimpleColorOverLifeStart, TrailData.SimpleColorOverLifeEnd, 1 - ((float)vertIndex / (float)trail.activePointCount / 2f)) : TrailData.ColorOverLife.Evaluate(1 - ((float)vertIndex / (float)trail.activePointCount / 2f))) :
                    (TrailData.UsingSimpleColor ? Color.Lerp(TrailData.SimpleColorOverLifeStart,TrailData.SimpleColorOverLifeEnd,timeAlong) : TrailData.ColorOverLife.Evaluate(timeAlong));
                float s = TrailData.StretchSizeToFit ?
                    (TrailData.UsingSimpleSize ? Mathf.Lerp(TrailData.SimpleSizeOverLifeStart,TrailData.SimpleSizeOverLifeEnd,1 - ((float)vertIndex / (float)trail.activePointCount / 2f)) : TrailData.SizeOverLife.Evaluate(1 - ((float)vertIndex / (float)trail.activePointCount / 2f))) :
                    (TrailData.UsingSimpleSize ? Mathf.Lerp(TrailData.SimpleSizeOverLifeStart,TrailData.SimpleSizeOverLifeEnd, timeAlong) : TrailData.SizeOverLife.Evaluate(timeAlong));
                trail.verticies[vertIndex] = p.Position + cross * s;
                if(TrailData.MaterialTileLength <= 0)
                {
                    trail.uvs[vertIndex] = new Vector2((float)vertIndex / (float)trail.activePointCount / 2f, 0);
                }
                else
                {
                    trail.uvs[vertIndex] = new Vector2(p.GetDistanceFromStart() / TrailData.MaterialTileLength, 0);
                }
                trail.normals[vertIndex] = camForward;
                trail.colors[vertIndex] = c;
                vertIndex++;
                trail.verticies[vertIndex] = p.Position - cross * s;
                if (TrailData.MaterialTileLength <= 0)
                {
                    trail.uvs[vertIndex] = new Vector2((float)vertIndex / (float)trail.activePointCount / 2f, 1);
                }
                else
                {
                    trail.uvs[vertIndex] = new Vector2(p.GetDistanceFromStart() / TrailData.MaterialTileLength, 1);
                }
                trail.normals[vertIndex] = camForward;
                trail.colors[vertIndex] = c;
                vertIndex++;
            }
            Vector2 finalPosition = trail.verticies[vertIndex-1];
            for(int i = vertIndex; i < trail.verticies.Length; i++)
            {
                trail.verticies[i] = finalPosition;
            }
            int indIndex = 0;
            for (int pointIndex = 0; pointIndex < 2 * (trail.activePointCount - 1); pointIndex++)
            {
                if(pointIndex%2==0)
                {
                    trail.indicies[indIndex] = pointIndex;
                    indIndex++;
                    trail.indicies[indIndex] = pointIndex + 1;
                    indIndex++;
                    trail.indicies[indIndex] = pointIndex + 2;
                }
                else
                {
                    trail.indicies[indIndex] = pointIndex + 2;
                    indIndex++;
                    trail.indicies[indIndex] = pointIndex + 1;
                    indIndex++;
                    trail.indicies[indIndex] = pointIndex;
                }
                indIndex++;
            }
            int finalIndex = trail.indicies[indIndex-1];
            for (int i = indIndex; i < trail.indicies.Length; i++)
            {
                trail.indicies[i] = finalIndex;
            }
            trail.Mesh.vertices = trail.verticies;
            trail.Mesh.SetIndices(trail.indicies, MeshTopology.Triangles, 0);
            trail.Mesh.uv = trail.uvs;
            trail.Mesh.normals = trail.normals;
            trail.Mesh.colors = trail.colors;
        }
        private void DrawMesh(Mesh trailMesh, Material trailMaterial)
        {
            Graphics.DrawMesh(trailMesh, Matrix4x4.identity, trailMaterial, gameObject.layer);
        }
        private void UpdatePoints(PCTrail line, float deltaTime)
        {
            for (int i = 0; i < line.Points.Count; i++)
            {
                line.Points[i].Update(_noDecay ? 0 : deltaTime);
            }
        }
        [Obsolete("UpdatePoint is deprecated, you should instead override UpdateTrail and loop through the individual points yourself (See Smoke or Smoke Plume scripts for how to do this).", true)]
        protected virtual void UpdatePoint(PCTrailPoint pCTrailPoint, float deltaTime)
        {
        }
        private void CheckEmitChange()
        {
            if (_emit != Emit)
            {
                _emit = Emit;
                if (_emit)
                {
                    _activeTrail = new PCTrail(GetMaxNumberOfPoints());
                    _activeTrail.IsActiveTrail = true;
                    OnStartEmit();
                }
                else
                {
                    OnStopEmit();
                    _activeTrail.IsActiveTrail = false;
                    _fadingTrails.Add(_activeTrail);
                    _activeTrail = null;
                }
            }
        }
        private int NumberOfActivePoints(PCTrail line)
        {
            int count = 0;
            for (int index = 0; index < line.Points.Count; index++)
            {
                if (line.Points[index].TimeActive() < TrailData.Lifetime) count++;
            }
            return count;
        }
        [UnityEngine.ContextMenu("Toggle inspector size input method")]
        protected void ToggleSizeInputStyle()
        {
            TrailData.UsingSimpleSize = !TrailData.UsingSimpleSize;
        }
        [UnityEngine.ContextMenu("Toggle inspector color input method")]
        protected void ToggleColorInputStyle()
        {
            TrailData.UsingSimpleColor = !TrailData.UsingSimpleColor;
        }
        public void LifeDecayEnabled(bool enabled)
        {
            _noDecay = !enabled;
        }
        /// <summary>
        /// Translates every point in the vector t
        /// </summary>
        public void Translate(Vector3 t)
        {
            if (_activeTrail != null)
            {
                for (int i = 0; i < _activeTrail.Points.Count; i++)
                {
                    _activeTrail.Points[i].Position += t;
                }
            }
            if (_fadingTrails != null)
            {
                foreach (PCTrail fadingTrail in _fadingTrails)
                {
                    for (int i = 0; i < fadingTrail.Points.Count; i++)
                    {
                        fadingTrail.Points[i].Position += t;
                    }
                }
            }
            OnTranslate(t);
        }
        /// <summary>
        /// Insert a trail into this trail renderer.
        /// </summary>
        /// <param name="from">The start position of the trail.</param>
        /// <param name="to">The end position of the trail.</param>
        /// <param name="distanceBetweenPoints">Distance between each point on the trail</param>
        public void CreateTrail(Vector3 from, Vector3 to, float distanceBetweenPoints)
        {
            float distanceBetween = Vector3.Distance(from, to);
            Vector3 dirVector = to - from;
            dirVector = dirVector.normalized;
            float currentLength = 0;
            CircularBuffer<PCTrailPoint> newLine = new CircularBuffer<PCTrailPoint>(GetMaxNumberOfPoints());
            int pointNumber = 0;
            while (currentLength < distanceBetween)
            {
                PCTrailPoint newPoint = new PCTrailPoint();
                newPoint.PointNumber = pointNumber;
                newPoint.Position = from + dirVector*currentLength;
                newLine.Add(newPoint);
                InitialiseNewPoint(newPoint);
                pointNumber++;
                if (distanceBetweenPoints <= 0)
                    break;
                else
                    currentLength += distanceBetweenPoints;
            }
            PCTrailPoint lastPoint = new PCTrailPoint();
            lastPoint.PointNumber = pointNumber;
            lastPoint.Position = to;
            newLine.Add(lastPoint);
            InitialiseNewPoint(lastPoint);
            PCTrail newTrail = new PCTrail(GetMaxNumberOfPoints());
            newTrail.Points = newLine;
            _fadingTrails.Add(newTrail);
        }
        /// <summary>
        /// Clears all active trails from the system.
        /// </summary>
        /// <param name="emitState">Desired emit state after clearing</param>
        public void ClearSystem(bool emitState)
        {
            if(_activeTrail != null)
            {
                _activeTrail.Dispose();
                _activeTrail = null;
            }
            if (_fadingTrails != null)
            {
                foreach (PCTrail fadingTrail in _fadingTrails)
                {
                    if (fadingTrail != null)
                        fadingTrail.Dispose();
                }
                _fadingTrails.Clear();
            }
            Emit = emitState;
            _emit = !emitState;
            CheckEmitChange();
        }
        /// <summary>
        /// Get the number of active seperate trail segments.
        /// </summary>
        public int NumSegments()
        {
            int num = 0;
            if (_activeTrail != null && NumberOfActivePoints(_activeTrail) != 0)
                num++;
            num += _fadingTrails.Count;
            return num;
        }
    }
    public class PCTrail : System.IDisposable
    {
        public CircularBuffer<PCTrailPoint> Points;
        public Mesh Mesh;
        public Vector3[] verticies;
        public Vector3[] normals;
        public Vector2[] uvs;
        public Color[] colors;
        public int[] indicies;
        public int activePointCount;
        public bool IsActiveTrail = false;
        public PCTrail(int numPoints)
        {
            Mesh = new Mesh();
            Mesh.MarkDynamic();
            verticies = new Vector3[2 * numPoints];
            normals = new Vector3[2 * numPoints];
            uvs = new Vector2[2 * numPoints];
            colors = new Color[2 * numPoints];
            indicies = new int[2 * (numPoints) * 3];
            Points = new CircularBuffer<PCTrailPoint>(numPoints);
        }
        #region Implementation of IDisposable
        public void Dispose()
        {
            if(Mesh != null)
            {
                if(Application.isEditor)
                    UnityEngine.Object.DestroyImmediate(Mesh, true);
                else
                    UnityEngine.Object.Destroy(Mesh);
            }
            Points.Clear();
            Points = null;
        }
        #endregion
    }
    public class PCTrailPoint
    {
        public Vector3 Forward;
        public Vector3 Position;
        public int PointNumber;
        private float _timeActive = 0;
        private float _distance;
        public virtual void Update(float deltaTime)
        {
            _timeActive += deltaTime;
        }
        public float TimeActive()
        {
            return _timeActive;
        }
        public void SetTimeActive(float time)
        {
            _timeActive = time;
        }
        public void SetDistanceFromStart(float distance)
        {
            _distance = distance;
        }
        public float GetDistanceFromStart()
        {
            return _distance;
        }
    }
    [System.Serializable]
    public class PCTrailRendererData
    {
        public Material TrailMaterial;
        public float Lifetime = 1;
        public bool UsingSimpleSize = false;
        public float SimpleSizeOverLifeStart;
        public float SimpleSizeOverLifeEnd;
        public AnimationCurve SizeOverLife = new AnimationCurve();
        public bool UsingSimpleColor = false;
        public Color SimpleColorOverLifeStart;
        public Color SimpleColorOverLifeEnd;
        public Gradient ColorOverLife;
        public bool StretchSizeToFit;
        public bool StretchColorToFit;
        public float MaterialTileLength = 0;
        public bool UseForwardOverride;
        public Vector3 ForwardOverride;
        public bool ForwardOverrideRelative;
    }
}
Plugins/Trails/TrailRenderer_Base.cs.meta
New file
@@ -0,0 +1,15 @@
fileFormatVersion: 2
guid: 87716fa8801130a4994e129a3b6b2fa6
labels:
- BetterTrails
- TrailRenderer
- Smoke
- Trail
- Effects
- skidmarks
MonoImporter:
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 1000
  icon: {instanceID: 0}
  userData:
Plugins/Trails/VersionInformation.cs
New file
@@ -0,0 +1,42 @@
namespace PigeonCoopToolkit.Generic
{
    [System.Serializable]
    public class VersionInformation
    {
        public string Name;
        public int Major = 1;
        public int Minor = 0;
        public int Patch = 0;
        public VersionInformation(string name, int major, int minor, int patch)
        {
            Name = name;
            Major = major;
            Minor = minor;
            Patch = patch;
        }
        public override string ToString()
        {
            return string.Format("{0} {1}.{2}.{3}", Name, Major, Minor, Patch);
        }
        public bool Match(VersionInformation other, bool looseMatch)
        {
            if(looseMatch)
            {
                return other.Name == Name &&
                       other.Major == Major &&
                       other.Minor == Minor;
            }
            else
            {
                return other.Name == Name &&
                       other.Major == Major &&
                       other.Minor == Minor &&
                       other.Patch == Patch;
            }
        }
    }
}
Plugins/Trails/VersionInformation.cs.meta
New file
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 1d58a7e753bea0e4c84779d092563363
MonoImporter:
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData: