Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 13 Next capture
2021 2022 2023
1 capture
13 Jun 22 - 13 Jun 22
sparklines
Close Help
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
  • Asset Store
  • Get Unity

UNITY ACCOUNT

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account
  • Blog
  • Forums
  • Answers
  • Evangelists
  • User Groups
  • Beta Program
  • Advisory Panel

Navigation

  • Home
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
    • Blog
    • Forums
    • Answers
    • Evangelists
    • User Groups
    • Beta Program
    • Advisory Panel

Unity account

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account

Language

  • Chinese
  • Spanish
  • Japanese
  • Korean
  • Portuguese
  • Ask a question
  • Spaces
    • Default
    • Help Room
    • META
    • Moderators
    • Topics
    • Questions
    • Users
    • Badges
  • Home /
avatar image
2
Question by Hoeloe · May 22, 2013 at 04:10 PM · carracingtrailskidmarks

Trail rendering for car skid marks

Hey there! I'm trying to create a racing game, and one effect I'd like to achieve is skid marks appearing on the ground when drifting. I've tried a few things with various levels of success. The first thing I tried was the built-in TrailRenderer. This had two problems for this application. The first issue is the "billboard" effect, which as far as I know can't be turned off, and the second was that there appeared to be no way to stop the trail emitting without removing the entire trail, which is an absolute necessity.

I then found this code, which did a slightly better job, in that it didn't render the trail as a billboard, and, with a bit of tweaking, could be made to fade the trail out. My problem with this approach, though, was that it was built to only support one trail. This caused weird artefacts to occur if you drifted more than once in quick succession, as it tried to join the trails together. I attempted to build a two-class system, one of which was a trail emitter, and one of which was a trail itself, the emitter keeping track of the trails it has spawned, but this failed spectacularly. I'm just trying to see if anyone knows of a way I could achieve this? Essentially all I need is something that emits trails, and will allow me to stop emitting them, and let them fade out, but also let me start creating a new trail without affecting the old one. Does anyone have any tips on how to go about this?

Comment
Add comment
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users

2 Replies

· Add your reply
  • Sort: 
avatar image
4
Best Answer

Answer by Hoeloe · May 23, 2013 at 02:52 PM

I solved this one myself. I thought I should post the code here. It uses two classes, Trail and TrailEmitter. To use the code, you attach the TrailEmitter to some object, and when you call "NewTrail()" on it, it will start emitting a trail. Calling "EndTrail()" will stop the trail, and allow you to start a new one. The Trail class is used by the TrailEmitter to actually render the trail.

Here is Trail.cs:

 using UnityEngine;
 using System.Collections;
 using System.Collections.Generic;
 
 namespace Trails
 {
     // Created by Edward Kay-Coles a.k.a Hoeloe
     public class Trail {
     
         //Properties of the trail
         private float width;
         private float decay;
         private Material m;
         private int rough;
         private int maxRough;
         private bool softSource;
         
         //Parent object
         private Transform par;
         
         //Pieces for the mesh generation
         private GameObject trail;
         private MeshFilter filter;
         private MeshRenderer render;
         private Mesh mesh;
         
         //Lists storing the mesh data
         private LinkedList<Vector3> verts = new LinkedList<Vector3>();
         private LinkedList<Vector2> uvs = new LinkedList<Vector2>();
         private LinkedList<int> tris = new LinkedList<int>();
         private LinkedList<Color> cols = new LinkedList<Color>();
         
         //Check if the trail is still being generated, and if it has completely faded
         private bool finished = false;
         private bool dead = false;
         
         //For registering if the object has been removed from the game (so you don't have to store it any more)
         public bool Dead
         {
             get { return dead; } 
             private set
             {
                 dead = true;
                 GameObject.Destroy(trail);
             }
         }
         
         //Set up the trail object, and parameters
         public Trail(Transform parent, Material material, float decayTime, int roughness, bool softSourceEdges, float wid = 0.1f)
         {
             softSource = softSourceEdges;
             maxRough = roughness;
             rough = 0;
             decay = decayTime;
             par = parent;
             width = wid;
             m = material;
             trail = new GameObject("Trail");
             filter = trail.AddComponent(typeof(MeshFilter)) as MeshFilter;
             render = trail.AddComponent(typeof(MeshRenderer)) as MeshRenderer;
             mesh = new Mesh();
             render.material = m;
             filter.mesh = mesh;
         }
         
         //Call this when the trail should stop emitting
         public void Finish()
         {
             finished = true;
         }
         
         //Tells you if the trail is emitting or not
         public bool Finished
         {
             get { return finished; }
         }
         
         // Updates the state of the trail - Note: this must be called manually
         public void Update () 
         {
             if(!finished) //Only add new segments if the trail is not being emitted
             {
                 //Decides how often to generate new segments. Smaller roughness values are smoother, but more expensive
                 if(rough > 0)
                     rough --;
                 else
                 {
                     rough = maxRough;
                     
                     //Checks in which order we should add vertices (to keep a consistent shape)
                     bool odd = !(verts.Count%4 == 0);
                     
                     //Add new vertices as the current position
                     verts.AddLast(par.position + (odd?-1:1)*par.up*width/2f);
                     verts.AddLast(par.position + (odd?1:-1)*par.up*width/2f);
                     
                     //Fades out the newest vertices if soft source edges is set to true
                     if(softSource)
                     {
                         if(cols.Count >= 4)
                         {
                             cols.Last.Value = Color.white;
                             cols.Last.Previous.Value = Color.white;
                         }
                         cols.AddLast(Color.clear);
                         cols.AddLast(Color.clear);
                     }
                     else //Sets the first vertices to fade out, but leaves the rest solid
                     {
                         if(cols.Count >= 2)
                         {
                             cols.AddLast(Color.white);
                             cols.AddLast(Color.white);
                         }
                         else
                         {
                             cols.AddLast(Color.clear);
                             cols.AddLast(Color.clear);
                         }
                     }
                     
                     if(!odd) //Set up uv mapping
                     {
                         uvs.AddLast(new Vector2(1,0));
                         uvs.AddLast(new Vector2(0,0));
                     }
                     else
                     {
                         uvs.AddLast(new Vector2(0,1));
                         uvs.AddLast(new Vector2(1,1));
                     }
                     
                     //Don't try to draw the trail unless we have at least a rectangle
                     if(verts.Count < 4) return;
                     
                     //Add new triangles to the mesh
                     int c = verts.Count;
                     tris.AddLast(c-4);
                     tris.AddLast(c-3);
                     tris.AddLast(c-2);
                     tris.AddLast(c-4);
                     tris.AddLast(c-2);
                     tris.AddLast(c-1);
                     
                     //Copy lists to arrays, ready to rebuild the mesh
                     Vector3[] v = new Vector3[c];
                     Vector2[] uv = new Vector2[c];
                     int[] t = new int[tris.Count];
                     verts.CopyTo(v,0);
                     uvs.CopyTo(uv,0);
                     tris.CopyTo(t,0);
                     
                     //Build the mesh
                     mesh.vertices = v;
                     mesh.triangles = t;
                     mesh.uv = uv;
                 }
             }
             //The next section updates the colours in the mesh
             int i = cols.Count;
             
             //If we have no vertices, don't bother trying to update
             if(i == 0)
                 return;
             
             //This is for checking if the trail has completely faded or not
             bool alive = false;
             
             //Essentially a foreach loop over the colours, but allowing editing to each node as it goes
             LinkedListNode<Color> d = cols.First;
             do
             {
                 if(d.Value.a > 0)
                 {
                     Color t = d.Value;
                     alive = true;
                     //Decrease the alpha value, to 0 if it would be decreased to negative
                     t.a -= Mathf.Min(Time.deltaTime/decay,t.a);
                     d.Value = t;
                 }
                 d = d.Next;
             }while(d != null);
             
             //Trail should be removed if it is not emitting and has faded out
             if(!alive && finished)
                 Dead = true;
             else
             {
                 //Doesn't set the colours if the number of vertices doesn't match up for whatever reason
                 if(i != mesh.vertices.Length)
                     return;
                 //Copy the colours to an array and build the mesh colours
                 Color[] cs = new Color[i];
                 cols.CopyTo(cs,0);
                 mesh.colors = cs;
             }
         }
     }
 }

And this is TrailEmitter.cs:

 using UnityEngine;
 using System.Collections;
 using System.Collections.Generic;
 using Trails;
 
 namespace Trails
 {
     // Created by Edward Kay-Coles a.k.a Hoeloe
     public class TrailEmitter : MonoBehaviour {
     
         //Stores all live trails
         private LinkedList<Trail> trails = new LinkedList<Trail>();
         
         //Parameters
         public float width = 0.1f;
         public float decayTime = 1f;
         public Material material;
         public int roughness = 0;
         public bool softSourceEnd = false;
         
         //Checks if the most recent trail is active or not
         public bool Active
         {
             get { return (trails.Count == 0?false:(!trails.Last.Value.Finished)); }
         }
         
         // Update is called once per frame
         void Update () 
         {
             //Don't update if there are no trails
             if(trails.Count == 0) return;
             
             //Essentially a foreach loop, allowing trails to be removed from the list if they are finished
             LinkedListNode<Trail> t = trails.First;
             LinkedListNode<Trail> n;
             do
             {
                 n = t.Next;
                 t.Value.Update();
                 if(t.Value.Dead)
                     trails.Remove(t);
                 t = n;
             }while(n != null);
         }
         
         /// <summary>
         /// Creates a new trail.
         /// </summary>
         public void NewTrail()
         {
             //Stops emitting the last trail and passes the parameters onto a new one
             EndTrail();
             trails.AddLast(new Trail(transform, material, decayTime, roughness, softSourceEnd, width));
         }
         
         /// <summary>
         /// Deactivate the last trail if it was already active.
         /// </summary>
         public void EndTrail()
         {
             if(!Active) return;
             trails.Last.Value.Finish();
         }
     }
 }

I thought I'd post this here because I had such trouble finding the solution, so I thought it would be a good idea to let others see the one I found.

Comment
Add comment · Show 1 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image boazlaurens · Sep 25, 2015 at 10:36 PM 0
Share

Sorry to dig up this old thread, but how do I actually call NewTrail() from another script on the same object that handles the car movement?

avatar image
0

Answer by BoontjieBoon · Dec 27, 2016 at 06:05 PM

Hi @Hoeloe, thanks for the code, it works great.

I had to tweak it a bit because it wasn't rendering continuously (probably winding of the triangles). I just changed it to only add new vertices after the parent moved a minimum distance and dropped the odd variable.

 ...         
           Vector3 currentPosition = par.transform.position + (Vector3)(par.transform.localToWorldMatrix * positionOffset);
     if (Vector3.Distance(previousPosition, currentPosition) > minimumSegmentLength) {
       previousPosition = currentPosition;
           //Add new vertices as the current position
           Vector3 offset = par.right * width / 2f;
           verts.AddLast(currentPosition - offset);
           verts.AddLast(currentPosition + offset);
 
 ...
 
           //Add new triangles to the mesh
           int c = verts.Count;
           tris.AddLast(c - 4);
           tris.AddLast(c - 2);
           tris.AddLast(c - 3);
           tris.AddLast(c - 3);
           tris.AddLast(c - 2);
           tris.AddLast(c - 1);


Comment
Add comment · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users

Your answer

Hint: You can notify a user about this post by typing @username

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Follow this Question

Answers Answers and Comments

17 People are following this question.

avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image

Related Questions

Does anyone have figured the WheelFrictionCurve struct ? 2 Answers

Applying Torque to wheelcollider 1 Answer

Realistically drivable car 0 Answers

How to make car accelerate/decelerate over time 2 Answers

Slotcar game 1 Answer


Enterprise
Social Q&A

Social
Subscribe on YouTube social-youtube Follow on LinkedIn social-linkedin Follow on Twitter social-twitter Follow on Facebook social-facebook Follow on Instagram social-instagram

Footer

  • Purchase
    • Products
    • Subscription
    • Asset Store
    • Unity Gear
    • Resellers
  • Education
    • Students
    • Educators
    • Certification
    • Learn
    • Center of Excellence
  • Download
    • Unity
    • Beta Program
  • Unity Labs
    • Labs
    • Publications
  • Resources
    • Learn platform
    • Community
    • Documentation
    • Unity QA
    • FAQ
    • Services Status
    • Connect
  • About Unity
    • About Us
    • Blog
    • Events
    • Careers
    • Contact
    • Press
    • Partners
    • Affiliates
    • Security
Copyright © 2020 Unity Technologies
  • Legal
  • Privacy Policy
  • Cookies
  • Do Not Sell My Personal Information
  • Cookies Settings
"Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere (more info here). Other names or brands are trademarks of their respective owners.
  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Spaces
  • Default
  • Help Room
  • META
  • Moderators
  • Explore
  • Topics
  • Questions
  • Users
  • Badges