what is the best way to restrict player from an area?

Let’s say I have a pond (irregular shape) that I want to keep the player out of. I have been placing planes around the pond with the mesh renderer disabled (so they are invisible.) This can sometimes cause some problems at the plane intersections (player getting stuck, or able to “climb” up the intersections.)

Is there a better way?

I’ll also need to do this along a path on a cliff to keep the player from falling off. Again–it will have an irregular edge.

thank you

One solution might be to create the irregular shape in a 3D program instead of using planes. You’d probably want to create a very rough model not one high in polygons.

that might be a little difficult to match the terrain irregularities while modeling the “fence” over in Maya, etc.

I made a version that uses “fence post” prefabs. You place the fence posts, and at run time the script creates an invisible wall from one to the next, connect-the-dots fashion.

Make a tall, skinny cylinder or cube. Attach the script (below) onto it. Turn that into a prefab.

To use the fence post prefabs, position them along the edge of the path, sunk into the ground a little bit, and taller than the character can jump/fly/whatever. Each post’s script has an inspector variable for the “next” post; after you’ve placed the posts, drag the “next” post object from the hierarchy into this inspector variable.

During the Awake() of each post, an invisible wall is created between this post and the next one. The post is also made invisible. It seems to work very well (unless you have multiple CharacterController.Move calls per Update()) and it’s a whole lot easier than placing planes, stretching them, and rotating them.

public var go_next_fence_post : GameObject; //in the Inspector, drag in the next fence post that this should "attach" to

function Awake () {	
	var fl_bottom : float; //the lowest bottom position of either post
	var fl_distancebetween : float; //distance between fence posts
	var fl_other_bottom : float; //the bottom of go_next_fence_post
	var fl_other_top : float; //the top of go_next_fence_post
	var fl_this_bottom : float; //the bottom of this fence post
	var fl_this_top : float; //the top of this fence post
	var fl_top : float; //the lowest top position of either post
	var go_wall : GameObject; //the invisible wall between fence posts
	var mr_meshrenderer : MeshRenderer; //the mesh renderer attached to this fence post (if there is one)
	var v3_thispost_xz : Vector3; //the x and z position of this fence post; used to calculate horizontal distance
	var v3_otherpost_xz : Vector3; //the x and z position of the other fence post; used to calculate horizontal distance
	
	//hide this fence post
	Destroy(gameObject.GetComponent(MeshRenderer));

	if (go_next_fence_post != null) {
		//get the top and bottom (y-axis) values of this fence post and the next one
		fl_this_bottom = transform.position.y - (transform.localScale.y / 2);
		fl_this_top = transform.position.y + (transform.localScale.y / 2);
		fl_other_bottom = go_next_fence_post.transform.position.y - (go_next_fence_post.transform.localScale.y / 2);
		fl_other_top = go_next_fence_post.transform.position.y + (go_next_fence_post.transform.localScale.y / 2);
		
		//calculate the height of the wall (from the bottom of the lowest post to the top of the highest post)
		if (fl_this_bottom < fl_other_bottom) {
			fl_bottom = fl_this_bottom;
		} else {
			fl_bottom = fl_other_bottom;
		}
		if (fl_this_top < fl_other_top) {
			fl_top = fl_other_top;
		} else {
			fl_top = fl_this_top;
		}
		
		//calculate the distance (x and z only) from one post to the next
		v3_thispost_xz = Vector3(transform.position.x, 0, transform.position.z);
		v3_otherpost_xz = Vector3(go_next_fence_post.transform.position.x, 0, go_next_fence_post.transform.position.z);
		fl_distancebetween = Vector3.Distance(v3_thispost_xz, v3_otherpost_xz);
		
		//get the direction (rotation) from this post to the next
		transform.LookAt(go_next_fence_post.transform);

		//remove any x and z rotation; the fence post has to be upright
		transform.rotation.eulerAngles.x = 0;
		transform.rotation.eulerAngles.z = 0;
		
		//create the wall
		go_wall = GameObject.CreatePrimitive(PrimitiveType.Cube);
		go_wall.name = "wall_piece";
		go_wall.transform.position = transform.position; //temporary position
		go_wall.transform.rotation = transform.rotation;

		//set the size of the wall piece, stretching from one fence post to the next
		go_wall.transform.localScale.x = 0.1;
		go_wall.transform.localScale.y = Mathf.Abs(fl_top - fl_bottom);
		go_wall.transform.localScale.z = fl_distancebetween;

		//position the wall piece halfway between the two fence posts
		go_wall.transform.position.x = (transform.position.x + go_next_fence_post.transform.position.x) / 2;
		go_wall.transform.position.y = (transform.position.y + go_next_fence_post.transform.position.y) / 2;
		go_wall.transform.position.z = (transform.position.z + go_next_fence_post.transform.position.z) / 2;
		
		//hide the wall piece; it's an INVISIBLE fence!
		Destroy(go_wall.gameObject.GetComponent(MeshRenderer));
		
		//make sure these don't interfere with OnMouseOver
		go_wall.layer = LayerMask.NameToLayer("Ignore Raycast");
		gameObject.layer = LayerMask.NameToLayer("Ignore Raycast");
		
		//make this fence post small so characters don't get stuck on it
		transform.localScale.x = .2;
		transform.localScale.z = .2;
	}
}

“that might be a little difficult to match the terrain irregularities while modeling the “fence” over in Maya, etc.”

Yeah, I see your point, though you might be able to get it done using an overhead screenshot and using that as a guide in your 3D program.
The script sounds pretty cool, though I haven’t tried it.

Thanks for the suggestions so far, everyone.

Can anyone else add any other suggestions? Is there a standard protocol for the procedure that most of you use?

It’s me again. I improved the script above so the fence posts don’t have to be vertical. That means you can create invisible ceilings! (There’s a “glass ceiling” joke in there somewhere.) Here’s the new code, lightly tested:

/*
Object scale is important.
   x = small
   y = BIG
   z = small
(The object is shaped like a pole, standing up.)

It can be rotated any direction.

In the Inspector, drag in the next fence post that this post should connect to.

*/

//PUBLIC INSPECTOR VARIABLES

public var go_next_fence_post : GameObject;


//PRIVATE VARIABLES

private var v3_distance : Vector3;
private var v3_nextend1 : Vector3;
private var v3_nextend2 : Vector3;
private var v3_nexthalflength : Vector3;
private var v3_thisend1 : Vector3;
private var v3_thisend2 : Vector3;
private var v3_thishalflength : Vector3;


//MONOBEHAVIOUR FUNCTIONS

function Awake() {
	//hide this fence post
	Destroy(GetComponent(MeshRenderer));

	//is there another fence post?
	if (go_next_fence_post != null) {

		//get Vector3's that are half the length of the fence pole heights, with the same rotations
		v3_thishalflength = transform.TransformDirection(Vector3.up * transform.localScale.y / 2);
		v3_nexthalflength = go_next_fence_post.transform.TransformDirection(Vector3.up * go_next_fence_post.transform.localScale.y / 2);

		//get the Vector3 that's the distance  direction between these two fence posts
		v3_distance = go_next_fence_post.transform.position - transform.position;
	}
}

function Start() {
	//connect it to another fence post?
	if (go_next_fence_post != null) {
		//normalize the scale of this object and set its rotation to nothin'
		transform.localScale = Vector3(1,1,1);
		transform.rotation.eulerAngles = Vector3(0,0,0);
		
		//create the mesh filter
		if (GetComponent("MeshFilter") == null) {
			gameObject.AddComponent("MeshFilter");
		}
		var ms_mesh : Mesh = GetComponent(MeshFilter).mesh;
		
		//create the 4 vertices
		ms_mesh.Clear();
		ms_mesh.vertices = [v3_thishalflength, -v3_thishalflength, v3_distance + v3_nexthalflength, v3_distance - v3_nexthalflength];
	
		//create 4 triangles between the 2 fence posts, to cover the space between them thoroughly.  Create each triangle in 2 ways (like 0,1,2 and 0,2,1) so they're impenetrable from both sides
		ms_mesh.triangles = [0,1,2, 0,2,1, 0,1,3, 1,3,1, 0,2,3, 0,3,2, 1,2,3, 1,3,2]; 
		
		//remove all colliders
		Destroy(GetComponent(BoxCollider));
		Destroy(GetComponent(SphereCollider));
		Destroy(GetComponent(CapsuleCollider));
		Destroy(GetComponent(MeshCollider));
		Destroy(GetComponent(WheelCollider));
		Destroy(GetComponent(RaycastCollider));
	
		//create a mesh collider in this shape
		gameObject.AddComponent("MeshCollider");
		GetComponent("MeshCollider").mesh = ms_mesh;

		//make sure these doen't interfere with OnMouseOver
		gameObject.layer = LayerMask.NameToLayer("Ignore Raycast");
	}
}

I modified it because my character could climb up a terrain cliff wall by jumping a few units upwards at a time. Defeated the purpose of my game. So I placed an invisible ceiling just above the player’s maximum jump height; problem solved!

Brett, Nice JOB!

I didn’t know that could be done. This was very informative for me. Great solution! It creates very low poly meshes as walls, so it shouldn’t use a lot of memory. Also, it avoids needing to make an asset in Maya to import, which shortens load time. And it’s all done procedurally, so you can move around the posts at will.

Very cool! Thanks for sharing this!

Vimalakirti, you are very welcome! Thank you so much for taking the time to write!

The only thing I’m a little unhappy about is when 2 “fence posts” are significantly out of parallel with each other and are not in the same plane. For example, two horizontal “rods”, one with “y” rotation of 0 degrees and one with “y” rotation of 90 degrees. My code will create a tetrahedron (pyramid) between them. That takes up volume. Walls ought to be flat and have a constant thickness! Ah well. My workaround is to create a couple more fence posts between them, one with a “y” rotation of 30 degrees and one with a “y” rotation of 60 degrees. That makes a wall much more similar to what I want.

If you get the error “cleaning the mesh failed”, just check if you have 2 fence posts that are in exactly the same position rotation, with one “connected” to the other. That creates a mesh with scale=0 and Unity doesn’t like those. (See http://forum.unity3d.com/viewtopic.php?p=173022.)

I improved this to work automatically in craters: just place one object in the center of the crater and at runtime it will create a wall that follows the interior of the crater. Here’s the link: http://forum.unity3d.com/viewtopic.php?t=48009.

It probably works fine on cliff edges too, and will definitely speed up your design time!

This thread has been continued at http://forum.unity3d.com/viewtopic.php?t=52338. And there is some much-cooler code there too!