How do I stop a projectile cold when colliding with another object? (projectile shifts on contact)

This is an attempt to do a simple arrow-stuck-in-wall simulation. My question pertains to how I can get a more accurate reaction out of the collision.

I’m Instantiating an Arrow prefab in front of a Third Person Controller, and only in the Start function of the arrow I am applying rigidbody force in the forward direction. I am having no problem detecting the collison, or bringing the object to a halt, but I am having a problem with the projectile shifting after the collision. This seems to occur before I have a chance to freeze the object.

This is particularly noticeable with an elongated projectile (such as a stretched capsule or cube) shot almost parallel to the target wall; In this scenario, the projectile will sometimes rotate 90 degrees as if it were bouncing off the surface before being frozen. Sometimes it might pick a nearby location to teleport to. Ideally, if the “arrow” were frozen exactly where it collided, then it should still be facing the camera perfectly straight. Dig?

:slight_smile: Please, any tips you can offer for perfecting the behavior and getting my arrows to stay still would be helpful. I feel I may be missing something fundamental about Unity.

public class Arrow : MonoBehaviour {
	
	Transform MyTransform;
	Rigidbody MyRigidbody;
	
	// Use this for initialization
	void Start () {			
		MyTransform = this.transform;
		MyRigidbody = this.rigidbody;
		MyRigidbody.AddForce(MyTransform.forward * 50, ForceMode.VelocityChange);			
	}

	void OnCollisionEnter()
	{		
		MyRigidbody.isKinematic = true;					
	}
}

Unity 3.5

This issue has come across this list a couple of times in the last month, and I don’t remember any reasonable solutions. So since you were thoughtful enough to provide a project, I thought I’d take a run at a solution. I came up with two workarounds. To me they are a bit hackish, but they appear to work. For both solutions start by doing:

  • From the edit menu, select Project Settings/Time. Reduce the Fixed Timestep from 0.02 to 0.01.
  • Select the Arrow prefab. Change the Collision Detection to Continuous Dynamic.

The first solution involves saving the position and rotation and restoring it if there is a hit. Here is the modified version of your script:

using UnityEngine;
using System.Collections;

public class Arrow : MonoBehaviour {
	private Quaternion q;
	private Vector3 v3;
	private bool hasHit = false;
	Transform MyTransform;
	Rigidbody MyRigidbody;
	
	// Use this for initialization
	void Start () {			
		MyTransform = this.transform;
		MyRigidbody = this.rigidbody;
		MyRigidbody.AddForce(MyTransform.forward * 50, ForceMode.VelocityChange);			
	}

	void OnCollisionEnter(Collision col)
	{		
		MyRigidbody.isKinematic = true;	
		hasHit = true;
	}
	
	void LateUpdate() {
		if (hasHit) {
			transform.position = v3;
			transform.rotation = q;
			hasHit = false;
		}
		else {
			v3 = transform.position;
			q = transform.rotation;
		}
	}
}

For the second solution:

  • Enable the the ‘IsTrigger’ setting for the colliders for all four walls.
  • Use your original script, but change the OnCollisionEnter() to OnTriggerEnter().

Note for both of these solutions, you can adjust the center of the collider. For the first solution, push the collider back inside the wall. The result will be that the arrows will stick in the walls rather than be on the surface. For the OnTriggerEnter() solution, I pulled the colliders a bit forward into the room.