Hello,
As the TrailRenderer did not suit my needs due to billboarding of the particles I wrote this Javascript that generates a trail mesh along a game object’s x-axis. The script does what I wanted it to, so I thought I’ll share it with you
How to use:
Create an empty game object, attach a MeshFilter and a MeshRenderer component plus this script.
The setting “Trail Width” is pretty self explanatory. The setting “Trail Length” is the maximum length of the trail in quad segments, where each segment consists of two triangles.
The “Generate” checkbox can be used to switch the trail on and off from an other script. It will gradually retract from its end when switched off.
The trail will stretch an assigned texture across its entire length.
I have put the code in a FixedUpdate function to generate the segments at fixed intervals, but you can rename the function to change that or to call the mesh generation from an other script.
This was written for Unity3, to use it in Unity iPhone 1.7 just remove the “# pragma implicit” and “# pragma downcast” at the top.
Disclaimer:
Please note that I am not a programmer, so there is definitely room for improvement. I also commented out the code for generating normals as I think it is flawed and I don’t need normals for the material I’m using. Maybe someone with a better understanding of dynamic mesh creation wants to take a look at it? Also, if you make improvements to this code I’d love to hear…
Enough of the talking, here is the code:
# pragma strict
# pragma implicit
# pragma downcast
@script RequireComponent (MeshFilter)
@script RequireComponent (MeshRenderer)
var trailWidth : float;
var trailLength : int;
var generate = false;
private var mesh : Mesh;
private var vertex0 = Vector3(trailWidth / 2, 0, 0);
private var vertex1 = Vector3(-trailWidth / 2, 0, 0);
private var vertices : Array;
private var verticesWorld : Vector3[];
private var uvs : Vector2[];
private var triangles : Array;
private var normals : Vector3[];
private var vector1 : Vector3;
private var vector2 : Vector3;
function Start () {
mesh = (GetComponent(MeshFilter) as MeshFilter).mesh;
vertices = new Array();
verticesWorld = new Array();
}
function FixedUpdate () {
// clear old mesh before calculating a new one
mesh.Clear();
if (generate) {
// set local position of the first 2 vertices
vertex0 = Vector3(trailWidth / 2, 0, 0);
vertex1 = Vector3(-trailWidth / 2, 0, 0);
// add these vertices at the top of the vertex array
vertices.Unshift(vertex0, vertex1);
// if max length has been reached discard the last two vertices
if (vertices.length > trailLength * 2 + 2) {
vertices.Pop();
vertices.Pop();
}
} else {
// if trail has been disabled and there are still vertices discard the last two vertices
if (vertices.length > 0) {
vertices.Pop();
vertices.Pop();
}
}
// if we have at least 4 vertices
if (vertices.length >= 4) {
// read saved world positions shifted by 2 verts as local positions
for (var i = 0; i < vertices.length - 2; i++) {
vertices[i+2] = transform.InverseTransformPoint(verticesWorld[i]);
}
// add vertices to mesh
mesh.vertices = vertices;
}
// save world positions of all vertices for reuse in next cycle
verticesWorld = new Vector3[vertices.length];
for (i = 0; i < vertices.length; i++) {
verticesWorld[i] = transform.TransformPoint(vertices[i]);
}
if (vertices.length >= 4) {
// calculate uvs
uvs = new Vector2[vertices.length];
uvs[0] = Vector2(0,0);
for (i = 1; i < vertices.length; i++) {
if (i%2) {
uvs[i] = Vector2(0, i / (vertices.length - 2));
} else {
uvs[i] = Vector2(1, (i - 1) / (vertices.length - 2));
}
}
mesh.uv = uvs;
// calculate triangles
triangles = new Array();
// first triangle
triangles.Add(0, 2, 1);
// subsequent triangles
for (i = 1; i < vertices.length - 2; i++) {
if (i%2) {
triangles.Add(i, i + 1, i + 2);
} else {
triangles.Add(i, i + 2, i + 1);
}
}
mesh.triangles = triangles;
// calculate normals
/*
normals = new Vector3[vertices.length];
// normal for vertex 0
vector1 = verticesWorld[1] - verticesWorld[0];
vector2 = verticesWorld[2] - verticesWorld[0];
if (Vector3.Angle(vector1, vector2) < 180) {
normals[0] = Vector3.Cross(vector1, vector2);
normals[0] = transform.InverseTransformDirection(normals[0].normalized);
} else {
normals[0] = Vector3.Cross(vector2, vector1);
normals[0] = transform.InverseTransformDirection(normals[0].normalized);
}
// normals for subsequent vertices
for (i = 1; i < vertices.length - 1; i++) {
vector1 = verticesWorld[i-1] - verticesWorld[i];
vector2 = verticesWorld[i+1] - verticesWorld[i];
if (Vector3.Angle(vector1, vector2) < 180) {
normals[i] = Vector3.Cross(vector1, vector2);
normals[i] = transform.InverseTransformDirection(normals[i].normalized);
} else {
normals[i] = Vector3.Cross(vector2, vector1);
normals[i] = transform.InverseTransformDirection(normals[i].normalized);
}
}
// normal for last vertex
vector1 = verticesWorld[vertices.length - 2] - verticesWorld[vertices.length - 1];
vector2 = verticesWorld[vertices.length - 3] - verticesWorld[vertices.length - 1];
if (Vector3.Angle(vector1, vector2) < 180) {
normals[vertices.length - 1] = Vector3.Cross(vector1, vector2);
normals[vertices.length - 1] = transform.InverseTransformDirection(normals[vertices.length - 1].normalized);
} else {
normals[vertices.length - 1] = Vector3.Cross(vector2, vector1);
normals[vertices.length - 1] = transform.InverseTransformDirection(normals[vertices.length - 1].normalized);
}
mesh.normals = normals;
*/
}
}