Description Alright, this is my last trail renderer I will post (wink). This trail renderer is designed specifically with melee trails in mind. It will track the path of its parent object and create a fluid arc between its saved points. You can set the distance between saved points, which gives you control over the accuracy of the interpolation. Increasing distance will produce more smoothed out arcs, where reducing distance will increase accuracy to the actual path. Features- Set the fade out time (lifetime) when the trail is done emitting
- Control when its done by simply turning off the "emit" property
- Set any number of colors
- Set any number of widths
- Autodestructs when faded out
- Set the interpolation distance
- Set whether it always faces the camera
- Set the number of segments to place between each interpolation distance
One-Time UseThis trail renderer is designed to be a one-time use component. There are several reasons for this, based on the way it is designed for optimization. When emitting is set to false, the trail renderer will stop adding new segments and fade out. The fade out time is set to the lifetime. Image
C# - TrailArc.cs
using UnityEngine;
using System.Collections;
public class TrailArc : MonoBehaviour
{
int savedIndex;
int pointIndex;
// Material - Particle Shader with "Tint Color" property
public Material material;
// Emit
public bool emit
{
get { return Emit; }
set { Emit = value; }
}
bool Emit = true;
bool emittingDone = false;
//Minimum velocity (script terminates)
public float minVel = 10;
// Facing
public bool faceCamera = true;
// Lifetime of each segment
public float lifetime = 1;
float lifeTimeRatio = 1;
float fadeOutRatio;
// Colors
public Color[] colors;
// Widths
public float[] widths;
// Optimization
public float pointDistance = 0.5f;
float pointSqrDistance = 0;
public int segmentsPerPoint = 4;
float tRatio;
// Print Output
public bool printResults = false;
public bool printSavedPoints = false;
public bool printSegmentPoints = false;
// Objects
GameObject trail = null;
Mesh mesh = null;
Material trailMaterial = null;
// Points
Vector3[] saved;
Vector3[] savedUp;
int savedCnt = 0;
Vector3[] points;
Vector3[] pointsUp;
int pointCnt = 0;
// Segment Appearance Normalization
int displayCnt = 0;
float lastPointCreationTime = 0;
float averageCreationTime = 0;
float averageInsertionTime = 0;
float elapsedInsertionTime = 0;
// Initialization
bool initialized = false;
void Start ()
{
if(gameObject.rigidbody.velocity.magnitude < minVel)
Destroy(this);
// Data Inititialization
saved = new Vector3[60];
savedUp = new Vector3[saved.Length];
points = new Vector3[saved.Length * segmentsPerPoint];
pointsUp = new Vector3[points.Length];
tRatio = 1f / (segmentsPerPoint);
pointSqrDistance = pointDistance * pointDistance;
// Create the mesh object
trail = new GameObject("Trail");
trail.transform.position = Vector3.zero;
trail.transform.rotation = Quaternion.identity;
trail.transform.localScale = Vector3.one;
MeshFilter meshFilter = (MeshFilter) trail.AddComponent(typeof(MeshFilter));
mesh = meshFilter.mesh;
trail.AddComponent(typeof(MeshRenderer));
trailMaterial = new Material(material);
fadeOutRatio = trailMaterial.GetColor("_TintColor").a;
trail.renderer.material = trailMaterial;
}
void printPoints()
{
if(savedCnt == 0)
return;
string s = "Saved Points at time " + Time.time + ":\n";
for(int i = 0; i < savedCnt; i++)
s += "Index: " + i + "\tPos: " + saved + "\n";
print(s);
}
void printAllPoints()
{
if(pointCnt == 0)
return;
string s = "Points at time " + Time.time + ":\n";
for(int i = 0; i < pointCnt; i++)
s += "Index: " + i + "\tPos: " + points + "\n";
print(s);
}
void findCoordinates(int index)
{
if(index == 0 || index >= savedCnt-2)
return;
Vector3 P0 = saved[index-1];
Vector3 P1 = saved[index];
Vector3 P2 = saved[index+1];
Vector3 P3 = saved[index+2];
Vector3 T1 = 0.5f * (P2 - P0);
Vector3 T2 = 0.5f * (P3 - P1);
int pointIndex = index * segmentsPerPoint;
for(int i = pointIndex; i < pointIndex+segmentsPerPoint; i++)
{
float t = (i-pointIndex) * tRatio;
float t2 = t*t;
float t3 = t2*t;
float blend1 = 2*t3 - 3*t2 + 1;
float blend2 = 3*t2 - 2*t3;
float blend3 = t3 - 2*t2 + t;
float blend4 = t3 - t2;
int pntInd = i - segmentsPerPoint;
points[pntInd] = blend1*P1 + blend2*P2 + blend3*T1 + blend4*T2;
pointsUp[pntInd] = Vector3.Lerp(savedUp[index], savedUp[index+1], t);
}
pointCnt = pointIndex;
}
void Update ()
{
try
{
Vector3 position = transform.position;
// Wait till the object is active (update called) and emitting
if( ! initialized && Emit)
{
// Place the first point behind this as a starter projected point
saved[savedCnt] = transform.TransformPoint(0,0,-pointDistance);
savedUp[savedCnt] = transform.up;
savedCnt++;
// Place the second point at the current position
saved[savedCnt] = position;
savedUp[savedCnt] = transform.up;
savedCnt++;
// Begin tracking the saved point creation time
lastPointCreationTime = Time.time;
initialized = true;
}
if(printSavedPoints)
printPoints();
if(printSegmentPoints)
printAllPoints();
// Emitting - Designed for one-time use
if( ! Emit )
{
if( ! emittingDone && pointCnt > 0 )
{
// Save two final points projected from the ending point
saved[savedCnt] = transform.TransformPoint(0,0,pointDistance);
savedUp[savedCnt] = transform.up;
savedCnt++;
findCoordinates(savedCnt-3);
// This makes the trail fill the actual entire path
saved[savedCnt] = transform.TransformPoint(0,0,pointDistance*2);
savedUp[savedCnt] = transform.up;
savedCnt++;
findCoordinates(savedCnt-3);
}
emittingDone = true;
}
if(emittingDone)
Emit = false;
if(Emit)
{
// Do we save a new point?
if( (saved[savedCnt-1] - position).sqrMagnitude > pointSqrDistance)
{
saved[savedCnt] = position;
savedUp[savedCnt] = transform.up;
savedCnt++;
// Calc the average point display time
if(averageCreationTime == 0)
averageCreationTime = Time.time - lastPointCreationTime;
else
{
float elapsedTime = Time.time - lastPointCreationTime;
averageCreationTime = (averageCreationTime + elapsedTime) * 0.5f;
}
averageInsertionTime = averageCreationTime * tRatio;
lastPointCreationTime = Time.time;
// Calc the last saved segment coordinates
if(savedCnt > 3)
findCoordinates(savedCnt-3);
}
}
// Do we fade it out?
if( ! Emit && displayCnt == pointCnt)
{
Color color = trailMaterial.GetColor("_TintColor");
color.a -= fadeOutRatio * lifeTimeRatio * Time.deltaTime;
if(color.a > 0)
trailMaterial.SetColor("_TintColor", color);
else
{
if(printResults)
print("Trail effect ending with a segment count of: " + pointCnt);
Destroy(trail);
Destroy(gameObject);
}
return;
}
// Do we display any new points?
if(displayCnt < pointCnt)
{
elapsedInsertionTime += Time.deltaTime;
while(elapsedInsertionTime > averageInsertionTime)
{
if(displayCnt < pointCnt)
displayCnt++;
elapsedInsertionTime -= averageInsertionTime;
}
}
// Do we render this?
if(displayCnt < 2)
{
trail.renderer.enabled = false;
return;
}
trail.renderer.enabled = true;
// Common data
lifeTimeRatio = 1f / lifetime;
Color[] meshColors;
// Rebuild the mesh
Vector3[] vertices = new Vector3[displayCnt * 2];
Vector2[] uvs = new Vector2[displayCnt * 2];
int[] triangles = new int[(displayCnt-1) * 6];
meshColors = new Color[displayCnt * 2];
float pointRatio = 1f / (displayCnt-1);
Vector3 cameraPos = Camera.main.transform.position;
for(int i = 0; i < displayCnt; i++)
{
Vector3 point = points;
float ratio = i * pointRatio;
// Color
Color color;
if(colors.Length == 0)
color = Color.Lerp(Color.clear, Color.white, ratio);
else if(colors.Length == 1)
color = Color.Lerp(Color.clear, colors[0], ratio);
else if(colors.Length == 2)
color = Color.Lerp(colors[1], colors[0], ratio);
else
{
float colorRatio = colors.Length - 1 - ratio * (colors.Length-1);
if(colorRatio == colors.Length-1)
color = colors[colors.Length-1];
else
{
int min = (int) Mathf.Floor(colorRatio);
float lerp = colorRatio - min;
color = Color.Lerp(colors[min+0], colors[min+1], lerp);
}
}
meshColors[i * 2] = color;
meshColors[(i * 2) + 1] = color;
// Width
float width;
if(widths.Length == 0)
width = 1;
else if(widths.Length == 1)
width = widths[0];
else if(widths.Length == 2)
width = Mathf.Lerp(widths[1], widths[0], ratio);
else
{
float widthRatio = widths.Length - 1 - ratio * (widths.Length-1);
if(widthRatio == widths.Length-1)
width = widths[widths.Length-1];
else
{
int min = (int) Mathf.Floor(widthRatio);
float lerp = widthRatio - min;
width = Mathf.Lerp(widths[min+0], widths[min+1], lerp);
}
}
// Vertices
if(faceCamera)
{
Vector3 from = i == displayCnt-1 ? points[i-1] : point;
Vector3 to = i == displayCnt-1 ? point : points[i+1];
Vector3 pointDir = to - from;
Vector3 vectorToCamera = cameraPos - point;
Vector3 perpendicular = Vector3.Cross(pointDir, vectorToCamera).normalized;
vertices[i * 2 + 0] = point + perpendicular * width * 0.5f;
vertices[i * 2 + 1] = point - perpendicular * width * 0.5f;
}
else
{
vertices[i * 2 + 0] = point + pointsUp * width * 0.5f;
vertices[i * 2 + 1] = point - pointsUp * width * 0.5f;
}
// UVs
uvs[i * 2 + 0] = new Vector2(ratio , 0);
uvs[i * 2 + 1] = new Vector2(ratio, 1);
if(i > 0)
{
// Triangles
int triIndex = (i - 1) * 6;
int vertIndex = i * 2;
triangles[triIndex+0] = vertIndex - 2;
triangles[triIndex+1] = vertIndex - 1;
triangles[triIndex+2] = vertIndex - 0;
triangles[triIndex+3] = vertIndex + 0;
triangles[triIndex+4] = vertIndex - 1;
triangles[triIndex+5] = vertIndex + 1;
}
}
trail.transform.position = Vector3.zero;
trail.transform.rotation = Quaternion.identity;
mesh.Clear();
mesh.vertices = vertices;
mesh.colors = meshColors;
mesh.uv = uvs;
mesh.triangles = triangles;
}
catch(System.Exception e)
{
print(e);
}
}
}
|