Align fencing with undulating terrain

Hi,
I’m wondering whether there is something clever and easy that can be done to align a fence with a sloping and undulating terrain. I’d like to create a very long narrow winding pathway which has a fence model on either side. I could do it with lots and lots of fence panels but I was hoping there might be an easier way. For instance I tried adding a fence model as a tree in the terrain tool and then used the smallest brush size & density and was able to place an irregular line of fence panels but they don’t join up and all face in one direction, so it didn’t really work.
Any ideas?
Cheers,
Mark

Well, this is reasonably complex, actually. The best way I can think to do this is to split the fence model into 2 parts - a post and a joining fence. You might then be able to create a script that mass places these between two mouse clicks, or similar. The first step of doing this is to place the posts. To do this, you can simply place a post at regular intervals between your mouse clicks (projected into 3D space onto the terrain). You can also use the vector between the two points to find the correct rotation. Next, each of these should raycast a reasonable distance straight up and down, and alter its position using hit.point and rotation using hit.normal. This way, the posts will follow the shape of the terrain. Now comes the difficult part. Between each pair of posts, you will need to place one fence mesh. You will need to rotate these to match the rotation of one of the posts.

Then, there are two possibilities, neither of them pretty. For the first, you need to dive into the mesh data. First, you’ll want to make sure you use mesh not sharedMesh, as that will ensure each fence can be different (which it needs to be to match the terrain). You will then need to find a way to position the vertices so that one side lines up with one post, and the other side lines up with the other post. You can do this using TransformPoint on positions relative to the posts. This method becomes more awkward for more complex meshes, but should be fine for simple ones. The second method for doing this is also awkward, and less accurate, but will be just as complex for simple meshes as for complex ones. It will require a bit of mathematics, but you will need to place the fence mesh into an empty game object, and then tweak the rotation of the mesh itself, followed by the scale of the parent object. This way, you can skew the object to point in roughly the right direction. You’ll probably need some matrix maths for this method. Also, it’s unlikely to work well if your posts are thin.

Finally, you can use CombineMeshes to stick all the different meshes together into one fence mesh. This method will only allow you to draw straight lines, but you could easily tweak it to allow multiple lines of fence to be connected. Sorry I couldn’t give you a satisfactorily simple answer, but unfortunately, there isn’t one, unless an asset exists on the asset store for this purpose.

I ran across question this when I had a similar need. I wound up creating a simple script that dynamically generates the mesh for the fence. In my case, simple quads for the fence panels were fine, and I didn’t need posts or anything fancy like that or even a back side. I did use a bump-mapped texture to give the illusion of some depth to the fence. I’m sure with a little elbow grease this script could be tweaked to generate a more complex model, configurable height, etc. The key is in the GetFloorHeight() function to allow the fenceposts to track the floor.

using UnityEngine;
using System.Collections.Generic;

public class FenceManager : MonoBehaviour 
{
	public float length;
	public float panelLength;
	public LayerMask layerMask;
	
	private void BuildFenceMesh()
	{
		var verts = new List<Vector3>();
		var uvs = new List<Vector2>();
		var norms = new List<Vector3>();
		var tans = new List<Vector4>();
		var tris = new List<int>();
		var totalLength = 0f;
		var numSegments = 0;
		
		while(totalLength <= length)
		{
			var postOffset = transform.localRotation * Vector3.forward * totalLength;
			var offset = GetFloorPosition(transform.position + postOffset + (Vector3.up * 100f));
			
			verts.Add(Vector3.forward * totalLength + Vector3.up * (offset.y - transform.position.y));
			verts.Add(Vector3.forward * totalLength + Vector3.up * (offset.y - transform.position.y + 1.5f));
			
			uvs.Add(new Vector2((float)numSegments, 0f));
			uvs.Add(new Vector2((float)numSegments, 1f));
			
			norms.Add(transform.localRotation * Vector3.right);
			norms.Add(transform.localRotation * Vector3.right);
			
			tans.Add(Vector3.right);
			tans.Add(Vector3.right);
			
			if(numSegments > 0)
			{
				tris.Add(numSegments * 2 - 2);
				tris.Add(numSegments * 2 - 1);
				tris.Add(numSegments * 2);
				tris.Add(numSegments * 2 + 1);
				tris.Add(numSegments * 2);
				tris.Add(numSegments * 2 - 1);
			}
			
			numSegments++;
			totalLength += panelLength;
		}
		
		var mesh = new Mesh();
		mesh.vertices = verts.ToArray();
		mesh.uv = uvs.ToArray();
		mesh.normals = norms.ToArray();
		mesh.tangents = tans.ToArray();
		mesh.triangles = tris.ToArray();
		var meshFilter = GetComponent<MeshFilter>();
		meshFilter.mesh = mesh;
	}
	
	private Vector3 GetFloorPosition(Vector3 testPosition)
	{
		var ray = new Ray(testPosition, Vector3.down);
		RaycastHit hit;
		if(Physics.Raycast(ray, out hit, 200f, layerMask))
		{
			testPosition = new Vector3(testPosition.x, hit.point.y, testPosition.z);
		}
		
		return testPosition;
	}

	void Start ()
	{
        BuildFenceMesh();
	}
}