Rigidbodies flying up in the air on collision

Hey guys!

I’ve got a little problem going on over here, and i can’t seem to find a solution. I’ve been making a simple Animal-AI for pigs, who are wandering around in the bounds of a fence, so far so good. When an animal get’s to close to the fence (detected via Raycast) it turns around 180 degrees and walks on. But the problem is: When two pigs walk into each other, one of them either starts spinning around in weird ways or flies up 30-50 metres in the air.

The pigs have a Rigidbody as well as a Caspule Collider (oriented in Z-Direction) attached to them, and they are getting moved by

transform.Translate(Vector3.forward * Time.deltaTime * 1.9f);

Can someone help with that?

T.i.a.

transform.Translate does a micro-teleport, as far as the physics engine is concerned. A little like Lancelot attacking in Monty Python’s Holy Grail. The “physics speed” (rigidbody.velocity) is left as mostly 0. You never get a real collision, caused by the physics engine moving them. Instead they just suddenly overlap, and it gets confused.

Physics wants to bounce the pigs away at their flipped speeds. Often both pigs have a tiny amount of down speed from gravity, so it bounces them both up, or velocity is close enough to 0 so bouncing does nothing, giving the vibrate.

I noticed you grab the controller, but then don’t use it:

CharacterController controller = GetComponent<CharacterController>();
transform.Translate(Vector3.forward * Time.deltaTime * 1.9f);

If you instead move with controller.Move(Vector3.forward * Time.deltaTime * 1.9f); it should do the math for you. Translate and Move look similar, but Translate will gladly shove you through hills. Controller.Move does a lot of collision and ground slope detection to shove you around solid objects.

Alternately, lose the CharacterController and work with physics. Change the move line to:

rigidbody.velocity = Vector3.forward * 1.9f;

There’s no Time.deltaTime in there, since the units are meters/second. The physics engine will automatically apply T.DT each frame. You used rigidbody.AddForce in Start, but can’t use that here – it would thrust your pigs like rockets like a game of bumpercars.

You might get some vigorous, un-pig-like bounces. Playing with the physics material bounciness could help that.

Create a new layer called “Pig”
Set the Layer of the Pigs prefab to “Pig”.
Go to Edit > Project Settings > Physics
Make sure the checkbox in the Layer Collision Matrix is unchecked for pig-pig collision.
That will disable collisions for the pigs

Enjoy

I think the best way to prevent flying out is freezing contains in rigidbody. For example I freeze “z axel” and all things is ok. Unity physics is not ideal. Sorry for mistakes. English is not my native language

Thanks for helping me :wink:

I’ve thought about the rotation a pig can make when starting movement could cause the other pigs getting pushed up or away.

Nevertheless, here’s the code:

private List <string> anims = new List <string>();

public LayerMask checkLayers;

private float lastTime;
private float nextTime;

private int animNumber;

private bool rotate;
private bool turnAround;
private float rotationAmount;
private float rotationSpeed;
private float currentRotation;
private float t;

void Awake() {
	rotate = false;
	turnAround = false;
	rotationAmount = 0.0f;
	rotationSpeed = 0.0f;
	currentRotation= 0.0f;
	t = 0.0f;
	
	anims.Add("WlObjPig_Idle");
	anims.Add("WlObjPig_Walking");
	
	lastTime = 0.0f;
	nextTime = 0.0f;
	rigidbody.AddForce(Vector3.up * 10);
}

void Update() {
	checkForwardDistance();
	Vector3 mypos = transform.eulerAngles;
	mypos.z = 0;
	transform.eulerAngles = mypos;
	Rotate();
	if(animNumber == 1){
		CharacterController controller = GetComponent<CharacterController>();
		transform.Translate(Vector3.forward * Time.deltaTime * 1.9f);
	}
	
	if(Time.time > lastTime + nextTime){
		lastTime = Time.time;
		
		nextTime = UnityEngine.Random.Range(1.0f, 5.0f);
		animNumber = UnityEngine.Random.Range(0, 2);
		animation.CrossFade(anims[animNumber], 0.2f);
		
		if(animNumber == 1){
			rotate = true;
			rotationAmount = UnityEngine.Random.Range(-90.0f, 90.0f);
			rotationSpeed = UnityEngine.Random.Range(0.01f, 0.05f);
			currentRotation = transform.eulerAngles.y;
			
		} else {
			rotate = false;
		}
	}
}

void Rotate(){
	if(t >= 1.0f){
		t = 0.0f;
		rotationAmount = 0.0f;
	}
	if(rotationAmount != 0.0f){
		transform.eulerAngles = new Vector3(0.0f, Mathf.Lerp(currentRotation, currentRotation + rotationAmount, t), 0.0f);
		t += rotationSpeed;
	}
}

void checkForwardDistance(){
	Vector3 rayStart = new Vector3(transform.position.x, transform.position.y + 1f, transform.position.z);
	Debug.DrawRay(rayStart, transform.forward * 4f, Color.red);
	if(Physics.Raycast(rayStart, transform.forward,  4.0f, checkLayers)){
		Debug.Log(true);
		nextTime = 2.0f;
		animNumber = 1;
		rotate = true;
		rotationAmount = 180f;
		rotationSpeed = 0.03f;
		currentRotation = transform.eulerAngles.y;
		turnAround = false;
		animation.CrossFade(anims[animNumber], 0.2f);
	}
}