Rendering permanent trails? (such as footsteps)

I’m new to Unity and have been experimenting around. Basically I have an idea for something that would require rendering “permanent” trails (or perhaps some that would fade over a given interval) like footsteps.

What I’m looking to do is to have a trigger (such as pressing breaks or stepping in to dog poo) that will start the trail process. The trail will then continue for distance X and fade away, but the trail left should be permanent. The trail should be scriptable in the sense that I want different types of trails (f.ex. depending on the material) to behave differently.

Furthermore, since I mentioned footsteps, it would be nice if it would be at least slightly footstep like, but I can “make do” with the trail texture being 2 foot steps if more fancy stuff is too complicated.

Any pointers on how I can achieve this? I found this “Trail Renderer” thing and some tutorial but I got the impression that it’s usually temporary.

This answer is based off the the method described by robertbu, an example can be found in the unity car tutorial in the skidmarks script. I have no idea and cannot find where this ‘Unity Footstep’ script is.

For my answer, here is a footprint gameObject with a mesh that gets updated by the player object. Player object tells the Footprints script where to place a footprint and what type of footprint, then the Footprints script calculates where that footprint goes, also if it is a left or right footprint, then updates the mesh information.

  • create a new scene, delete the camera (first person controller prefab has a camera)
  • add a terrain, set the layer to “Water”
  • add a first person controller (and a directional light)
  • create an empty gameObject
  • name it “Footprints”
  • set the position to 0,0,0
  • attach the Footprints script to the Footprints object
  • create a footprint material, attach it to the Footprints object
  • set the terrain layer on the Footprints object to “Water”
  • attach the PlayerFootprints script to the first person controller

Hit play, walk around, footprints should be left behind. These footprints are random, just to show how the player script adds the footstep, but also states what type of footstep to leave. The Footsteps script looks after the size and offset of the footprints.

Variables :

Footprints :

public var maxFootprints : int = 256; // Maximum number of footprints total handled by one instance of the script.
public var footprintSize : Vector2 = Vector2( 0.4, 0.8 ); // The size of the footprint. Should match the size of the footprint that it is used for. In meters.
public var footprintSpacing : float = 0.3; // the offset for the left or right footprint. In meters.
public var groundOffset : float = 0.02;	// The distance the footprints are places above the surface it is placed upon. In meters.
public var terrainLayer : LayerMask; // the layer of the terrain, so the footprint raycast is only hitting the terrain.

PlayerFootprints :

public var footprintSpacing : float = 2.0; // distance between each footprint

Now for the scripts :

Footprints :

//------------------------------//
//  Footprints.js               //
//  Written by Alucard Jay      //
//  6/19/2013                   //
//------------------------------//

#pragma strict
@script RequireComponent( MeshFilter, MeshRenderer )


public var maxFootprints : int = 256; // Maximum number of footprints total handled by one instance of the script.
public var footprintSize : Vector2 = Vector2( 0.4, 0.8 ); // The size of the footprint. Should match the size of the footprint that it is used for. In meters.
public var footprintSpacing : float = 0.3; // the offset for the left or right footprint. In meters.
public var groundOffset : float = 0.02;	// The distance the footprints are places above the surface it is placed upon. In meters.

public var terrainLayer : LayerMask; // the layer of the terrain, so the footprint raycast is only hitting the terrain.


private var mesh : Mesh;

private var vertices : Vector3[];
private var normals : Vector3[];
private var uvs : Vector2[];
private var triangles : int[];

private var footprintCount : int = 0;

private var isLeft : boolean = false;


// Initializes the array holding the footprint sections.
function Awake()
{
	// - Initialize Arrays -
	
	vertices = new Vector3[ maxFootprints * 4 ];
	normals = new Vector3[ maxFootprints * 4 ];
	uvs = new Vector2[ maxFootprints * 4 ];
	triangles = new int[ maxFootprints * 6 ];
	
	// - Initialize Mesh -
	
	if ( GetComponent( MeshFilter ).mesh == null )
	{
		GetComponent( MeshFilter ).mesh = new Mesh();
	}
	
	mesh = GetComponent( MeshFilter ).mesh;
	
	mesh.name = "Footprints_Mesh";
}


// Function called by the Player when adding a footprint. 
// Adds the information needed to create the mesh later. 
public function AddFootprint( pos : Vector3, fwd : Vector3, rht : Vector3, footprintType : int )
{
	// - Calculate the 4 corners -
	
	// foot offset
	var footOffset : float = footprintSpacing;
	
	if ( isLeft )
	{
		footOffset = -footprintSpacing;
	}
	
	var corners : Vector3[] = new Vector3[ 4 ];
	
	// corners = position + left/right offset + forward + right
	corners[ 0 ] = pos + ( rht * footOffset ) + ( fwd * footprintSize.y * 0.5 ) + ( -rht * footprintSize.x * 0.5 ); // Upper Left
	corners[ 1 ] = pos + ( rht * footOffset ) + ( fwd * footprintSize.y * 0.5 ) + ( rht * footprintSize.x * 0.5 ); // Upper Right
	corners[ 2 ] = pos + ( rht * footOffset ) + ( -fwd * footprintSize.y * 0.5 ) + ( -rht * footprintSize.x * 0.5 ); // Lower Left
	corners[ 3 ] = pos + ( rht * footOffset ) + ( -fwd * footprintSize.y * 0.5 ) + ( rht * footprintSize.x * 0.5 ); // Lower Right
	
	
	// raycast to get the position and normal for each corner
	var hit : RaycastHit;
	
	for ( var i : int = 0; i < 4; i ++ )
	{
		var rayPos : Vector3 = corners[ i ];
		rayPos.y = 1000.0;
		
		if ( Physics.Raycast( rayPos, -Vector3.up, hit, 2000.0, terrainLayer ) ) // also add a layermask for the terrain
		{
			var index : int = ( footprintCount * 4 ) + i;
			
			// - Vertex -
			vertices[ index ] = hit.point + ( hit.normal * groundOffset );
			
			// - Normal -
			normals[ index ] = hit.normal;
			
		}
	}
	
	
	// - UVs -
	
	// what type of footprint is being placed
	var uvOffset : Vector2;
	
	switch( footprintType )
	{
		case 1 :
			uvOffset = Vector2( 0.5, 1.0 );
		break;
		
		case 2 :
			uvOffset = Vector2( 0.0, 0.5 );
		break;
		
		case 3 :
			uvOffset = Vector2( 0.5, 0.0 );
		break;
		
		default :
			uvOffset = Vector2( 0.0, 1.0 );
		break;
	}
	
	// is this the left foot or the right foot
	switch( isLeft )
	{
		case true :
			uvs[ ( footprintCount * 4 ) + 0 ] = Vector2( uvOffset.x + 0.5, uvOffset.y );
			uvs[ ( footprintCount * 4 ) + 1 ] = Vector2( uvOffset.x, uvOffset.y );
			uvs[ ( footprintCount * 4 ) + 2 ] = Vector2( uvOffset.x + 0.5, uvOffset.y - 0.5);
			uvs[ ( footprintCount * 4 ) + 3 ] = Vector2( uvOffset.x, uvOffset.y - 0.5 );
			
			isLeft = false;
		break;
		
		case false :
			uvs[ ( footprintCount * 4 ) + 0 ] = Vector2( uvOffset.x, uvOffset.y );
			uvs[ ( footprintCount * 4 ) + 1 ] = Vector2( uvOffset.x + 0.5, uvOffset.y );
			uvs[ ( footprintCount * 4 ) + 2 ] = Vector2( uvOffset.x, uvOffset.y - 0.5 );
			uvs[ ( footprintCount * 4 ) + 3 ] = Vector2( uvOffset.x + 0.5, uvOffset.y - 0.5);
			
			isLeft = true;
		break;
	}
	
	
	
	// - Triangles -
	
	triangles[ ( footprintCount * 6 ) + 0 ] = ( footprintCount * 4 ) + 0;
	triangles[ ( footprintCount * 6 ) + 1 ] = ( footprintCount * 4 ) + 1;
	triangles[ ( footprintCount * 6 ) + 2 ] = ( footprintCount * 4 ) + 2;
	
	triangles[ ( footprintCount * 6 ) + 3 ] = ( footprintCount * 4 ) + 2;
	triangles[ ( footprintCount * 6 ) + 4 ] = ( footprintCount * 4 ) + 1;
	triangles[ ( footprintCount * 6 ) + 5 ] = ( footprintCount * 4 ) + 3;
	
	
	// - Increment counter -
	footprintCount ++;
	
	if ( footprintCount >= maxFootprints )
	{
		footprintCount = 0;
	}
	
	// - update mesh with new info -
	ConstructMesh();
}


function ConstructMesh() 
{
	mesh.Clear();
	
	mesh.vertices = vertices;
	mesh.normals = normals;
	mesh.triangles = triangles;
	mesh.uv = uvs;
}

PlayerFootprints :

//------------------------------//
//  PlayerFootprints.js         //
//  Written by Alucard Jay      //
//  6/19/2013                   //
//------------------------------//

#pragma strict

public var footprints : Footprints;

public var footprintSpacing : float = 2.0; // distance between each footprint

private var lastPos : Vector3 = Vector3.zero;


function Start() 
{
	lastPos = transform.position;
	
	if ( !footprints )
	{
		footprints = GameObject.Find( "Footprints" ).GetComponent( Footprints );
	}
}


function Update() 
{
	var distFromLastFootprint : float = ( lastPos - transform.position ).sqrMagnitude;
	
	if ( distFromLastFootprint > footprintSpacing * footprintSpacing )
	{
		// AddFootprint( pos : Vector3, fwd : Vector3, rht : Vector3, footprintType : int )
		//footprints.AddFootprint( transform.position, transform.forward, transform.right, 0 );
		footprints.AddFootprint( transform.position, transform.forward, transform.right, Random.Range( 0, 4 ) );
		
		lastPos = transform.position;
	}
}

Footprints source image : http://www.bytedust.com/free-shoe-sole-print-vector-design

Edited image : http://www.alucardj.net16.net/unityanswers2013/footprints_256x512.png

Complete package : http://www.alucardj.net16.net/unityanswers2013/Footprints.unitypackage


There’s a free script in Unity that does footsteps. It’s cool to walk through things and leave trails, I like that idea.

You can modify it quite easily when you come into contact with say dog shit, just make sure it has a collider with a trigger. At that point, you can set new particle effects for footsteps, using perhaps the foot position.

For permanent trails, I really don’t think you want to do this. It may sound cool, but there’s a reason other engines haven’t done it (using effects), it’s because it increases drawcalls, and could dramatically increase them to the point the game barely runs. (ie, it WILL for some players, if it can happen in a game, it will happen is the rule).

Even if they just walk through a pile creating footsteps in a small area, this will be a huge problem. You’re adding a new texture to the scene each footstep, with one or more drawcalls. It won’t take long before the game is running at 0.1 fps.