- Home /
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.
The most efficient method I can think of would to dynamically modify/add to a mesh. Each quad would be a footsteps. The different footsteps (left and right as well as the different kinds for different terrain) would come from a texture atlas. So the mesh would draw in a single draw call. You could have 1000s of steps without seriously impacting performance. But between the mesh manipulation and the UV/texture atlas code, it would be a fairly major coding effort
There's a AAA game that recently did something like this, and only for snow (it did manage to run reasonably well on PS3).. Definitely a large coding effort.
Aye, my first instinct as a game programmer was to simply have some sort of mesh that i would modify. I didn't mention it here, but the surface I'm rendering on is flat in the current design so that removes some complexity from the issue :)
Answer by AlucardJay · Jun 19, 2013 at 03:24 AM
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
Answer by SinisterRainbow · Jun 17, 2013 at 04:14 AM
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.
The textures are shared/material is shared, so even in the worst case, the footsteps should batch. But if you do each step with a separate game object, you will drag the game down.
That could be, I'm assu$$anonymous$$g they are not batched but very well may be wrong.
Alright, I'll try to look it up. Leaving trails is really a core mechanic in my idea so it is necessary that they are permanent. However, I doubt that there will be a huge amount of them. That being said, I can't have each footstep as a separate game object (and definitely don't want to).
I am actually not sure how heavy operation it would be to render a square for every 2 footsteps or so, but yeah - I don't really have a vision of how this would work in Unity, I know how I could just have some sort of vertex array if doing it from scratch as having a trail of footsteps would probably not be too many triangles added to the scene.
Anyway, I'll look up the footstep script and see if it is efficient enough for my purposes, it might well be. Thanks. :)
You should maybe try just using the footsteps script and check the unity stats in the game window to see if new draw calls are being added. In CryEngine they don't seem to batch (and I don't see this in any game, AAA or otherwise) so I assume you cannot, but worth a try. Usually if it's represented as a decal, it's projecting onto a surface (not creating say a square plane with a texture affixed to it), and not creating a separate mesh that can be batched. This is my experience, but I don't know how Unity does it. I know unity can batch particle systems, but you'll have to check out the specifics.
Even if it proves to not batch, you could make a simple game mesh plane & using transparency it will dynamically batch and set it to terrain level.
But this will have it's own problems on curves, footsteps will hang in mid-air or sink into the ground if not at the perfect angle. (if you want to make the rest of it transparent and just have say a foot print). The better option in this case, imo, is to make a model with a foot print, or use models (of say dirt clods when trampling through mud).. you could have a few models for mud/snow/etc that randomly pop out per step. They could even collide with the terrain, then maybe delete their scripts and never move again when settled (else this will definitely be a performance problem). This would use dynamic batching, see: http://docs.unity3d.com/Documentation/$$anonymous$$anual/DrawCallBatching.html
It still has come CPU overhead, how much I dunno.
You could also limit the amount spread. For example stepping in paint, you can drag it, but paint is finite...you may want to limit it to X amount before it 'dries'.
If you can post back when you discover your solution it would be good knowledge I'd like to know.
Your answer
Follow this Question
Related Questions
Tutorial on Photorealism for unity games? 0 Answers
terrain rendering in pink 0 Answers
Rendering a Camera in an Editor window displays its RenderTexture automatically? 1 Answer
Seam on Procedural Mesh 1 Answer
My PNG Sprites have Leprosy! 1 Answer