Limit a rigidbody axis to a range

I’m trying to make a 2d flying game, and I need to limit the main player rigidbody from exiting the scene, so far I’m using the following:

rigidbody.position.y = Mathf.Clamp(rigidbody.position.y, -5, 5);
if(rigidbody.position.y <= -5){
	rigidbody.velocity = Vector3(0,0.1,0);
}
if(rigidbody.position.y >= 5){
	rigidbody.velocity = Vector3(0,-0.1,0);
}

But using that makes it a bit jittery when you’re pushing along the edge, having a bounce effect, and sometimes the object gets stuck in them co-ordinates.

I understand why the bouncing is happening, I was just wondering if there’s a better way to handle it?

Thanks in advance for any help.

A rigidbody is designed to live under the laws of physics, each physics time step the position and rotation of the rigidbody is calculated with several forces taken into account, such as gravitational, angular and velocity. Luckily these forces can be manipulated with directly, with some care you can get a great result without too much fiddle, setting the velocity directly.

To get a smooth result when it comes to clamping you need to access the velocity of the object and reset it, but you probably want to keep the velocity on other axes as well.

Here’s an example script of limiting a rigidbody within the camera view whilst keeping velocity on those axes that should not be limited by the frustrum,

#pragma strict

var clampBorderOffset : float = 1; // Offset from border in Units

private var speed : float = 100;
private var cam : Camera;
private var camTransform : Transform;

function Start () {
	cam = Camera.main;
	camTransform = cam.transform;
}

function FixedUpdate () {
	
	// Steering example
	if (Input.GetMouseButton(0))
		rigidbody.AddForce(Vector3.up*speed);
	rigidbody.AddForce(Vector3(Input.GetAxis("Horizontal")*speed,0,0));
	
	// Clamp object, could be any rigidbody but in this case it's the same as the script it's attached to
	ClampObjectIntoView(rigidbody);
}

function ClampObjectIntoView (r : Rigidbody) {

	// Set limits within the frustrum of the camera
	var objectPosition : Vector3 = r.position;
	var frustrumPositionTopY : float = cam.ViewportToWorldPoint(Vector3(0,1,r.position.z-camTransform.position.z)).y;
	var frustrumPositionBottomY : float = cam.ViewportToWorldPoint(Vector3(0,0,r.position.z-camTransform.position.z)).y;
	var frustrumPositionLeftX : float = cam.ViewportToWorldPoint(Vector3(0,0,r.position.z-camTransform.position.z)).x;
	var frustrumPositionRightX : float = cam.ViewportToWorldPoint(Vector3(1,0,r.position.z-camTransform.position.z)).x;
	
	// Clamp top
	if (objectPosition.y>frustrumPositionTopY-clampBorderOffset) {
		r.velocity.y = 0;
		r.position.y = frustrumPositionTopY-clampBorderOffset;
	} else
	
	// Clamp bottom
	if (objectPosition.y<frustrumPositionBottomY+clampBorderOffset) {
		r.velocity.y = 0;
		r.position.y = frustrumPositionBottomY+clampBorderOffset;
	}
	
	// Clamp left
	if (objectPosition.x<frustrumPositionLeftX+clampBorderOffset) {
		r.velocity.x = 0;
		r.position.x = frustrumPositionLeftX+clampBorderOffset;
	} else
	
	// Clamp right
	if (objectPosition.x>frustrumPositionRightX-clampBorderOffset) {
		r.velocity.x = 0;
		r.position.x = frustrumPositionRightX-clampBorderOffset;
	}
	
}

Making it bounce when hitting the border would be a negative value of the current velocity multiplied with bounciness - which for a good result would be a normalized value between 0 and 1, for instance:

var bounciness : float = .5;
r.velocity.y = -r.velocity.y * bounciness;

C# converted, however left to right works but up and down does not. Can’t figure out why…

using UnityEngine;
using System.Collections;

public class CameraClamp : MonoBehaviour {
float clampBorderOffset = 1; // Offset from border in Units
private float speed = 100;

void Start () {
}

void FixedUpdate () {
	
	// Steering example
	if (Input.GetMouseButton(0))
		rigidbody.AddForce(Vector3.up*speed);
	rigidbody.AddForce(new Vector3(Input.GetAxis("Horizontal")*speed,0,0));
	
	// Clamp object, could be any rigidbody but in this case it's the same as the script it's attached to
	ClampObjectIntoView(rigidbody);
}

void ClampObjectIntoView (Rigidbody r) {
	
	// Set limits within the frustrum of the camera
	Vector3 objectPosition = r.position;
	float frustrumPositionTopY = Camera.main.ViewportToWorldPoint(new Vector3(0,1,r.position.z-Camera.main.transform.position.z)).y;
	float frustrumPositionBottomY = Camera.main.ViewportToWorldPoint(new Vector3(0,0,r.position.z-Camera.main.transform.position.z)).y;
	float frustrumPositionLeftX = Camera.main.ViewportToWorldPoint(new Vector3(0,0,r.position.z-Camera.main.transform.position.z)).x;
	float frustrumPositionRightX = Camera.main.ViewportToWorldPoint(new Vector3(1,0,r.position.z-Camera.main.transform.position.z)).x;

	// Clamp top
	if (objectPosition.y>frustrumPositionTopY-clampBorderOffset) {
		r.velocity = new Vector3(r.position.x,0,r.position.z);
		r.position = new Vector3(r.position.x,frustrumPositionTopY-clampBorderOffset,r.position.z);
	} else
		
		// Clamp bottom
	if (objectPosition.y<frustrumPositionBottomY+clampBorderOffset) {
		r.velocity = new Vector3(r.position.x,0,r.position.z);
		r.position = new Vector3(r.position.x,frustrumPositionBottomY+clampBorderOffset,r.position.z);
	}
	
	// Clamp left
	if (objectPosition.x<frustrumPositionLeftX+clampBorderOffset) {
		r.velocity = new Vector3(0,r.position.y,r.position.z);
		r.position = new Vector3(frustrumPositionLeftX+clampBorderOffset,r.position.y,r.position.z);
	} else
		
		// Clamp right
	if (objectPosition.x>frustrumPositionRightX-clampBorderOffset) {
		r.velocity = new Vector3(0,r.position.y,r.position.z);
		r.position = new Vector3(frustrumPositionRightX-clampBorderOffset,r.position.y,r.position.z);
	}
	
}

}