- Home /
Collisions, Getting the Normal of the Collision Surface (Not the Angle of Impact)
I'm working on my first Unity project right now (a bit of a self prescribed crash-course) and I've run into a slight issue.
I'm trying deflect one object off of another one (via collision). For now, the deflection should be at a perfectly mirrored angle. No problem! I think to myself, I see this super handy contact.normal
property; seems pretty straight forward (I'm working in a 2D plane, so it really should be have been smooth sailing from there).
A few frustratingly failed attempts, and several thousand drawRay
commands later, I realize that the normal is NOT a normal to the face of impact, but is a normal OF the impact (ie, mirrors the angle of impact).
This is actually useful information, but to be useful, I need a face-normal to reflect the angle of impact over.
The moving object is using this.rigidbody.velocity =
so I can't rely on built in physics; I'm also being a total stickler for how this bounce looks, so I'd really love to maintain full control over it.
Relevant code below:
function OnCollisionEnter(collision : Collision)
{
var contactCount : int = 0;
var normalAve : Vector3 = Vector3(0,0,0);
var velocityAve : Vector3 = Vector3.zero;
for (var contact : ContactPoint in collision.contacts)
{
contactCount++;
normalAve += contact.normal;
velocityAve += collision.relativeVelocity;
Debug.DrawRay(contact.point, contact.normal, Color.green, 2, false);
}
normalAve /= contactCount;
velocityAve /= contactCount;
var damage = Vector3.Dot(normalAve, velocityAve) * collision.rigidbody.mass;
life -= damage;
if(life <= 0)
{
// Stackoverflow people can ignore this, bounces
// don't happen when you're dead
this.rigidbody.useGravity = this.GetComponent(ship_small_physics).stage.GetComponent(stage_params).grav_on_hit;
this.GetComponent(ship_small_physics).impact();
}else{
// Stackoverflow people! Right here!
Debug.Log("survived! with " + life + " life");
this.GetComponent(ship_small_physics).small_impact(normalAve, velocityAve);
}
}
Then, in .small_impact
function small_impact(n : Vector3, v : Vector3)
{
var h_angle : Vector3 = Vector3(n.x, 0, n.z);
Debug.DrawRay(this.transform.position, n, Color.blue, 2, false);
Debug.DrawRay(this.transform.position, h_angle, Color.red, 2, false);
}
Just to clarify: The game IS in some sorts of 3D, but for various reasons in this case I only care about the collision normals in the x-z
plane (another reason why the plane old physics engine won't do). That's why I make a new vector h_angle
that's essentially the projection of the collision-normal onto the x-z
plane.
It's entirely possible that I'm just not understanding everything.
Answer by Nevermind · Feb 26, 2014 at 09:11 AM
Just had this same problem, and I came up with this solution: You need to find one single point on the collider that you can consider as the "collision point". This can never be completely accurate, unless you code a full-blown collision solver yourself; but what you can do is use raycasting to find some approximation. Basically, you step away from the collision using the collision normal provided, and than raycast against the collider. RaycastHit class gives you the actual surface normal at the hit point, as well as other potentially useful info (like what triangle exactly was hit) Here's my code (it's not very polished, but is generally working)
void OnCollisionEnter(Collision info){
// find collision point and normal. You may want to average over all contacts
var point = info.contacts[0].point;
var dir = -info.contacts[0].normal; // you need vector pointing TOWARDS the collision, not away from it
// step back a bit
point -= dir;
RaycastHit hitInfo;
// cast a ray twice as far as your step back. This seems to work in all
// situations, at least when speeds are not ridiculously big
if(info.collider.Raycast( new Ray( point, dir ), out hitInfo, 2 ) )
{
// this is the collider surface normal
var normal = hitInfo.normal;
// this is the collision angle
// you might want to use .velocity instead of .forward here, but it
// looks like it's already changed due to bounce in OnCollisionEnter
var angle = Vector3.Angle( -transform.forward, normal );
}
}
so what should I do with the angle if I want to instantiate some thing on the surface?
Would it be better to do this in OnCollisionStay()? I need to constantly check for ground and the get the slope normal. Also would use would the angle have exactly? Slope threshold check?
Your answer
Follow this Question
Related Questions
Collision Normal 1 Answer
Not detecting a simple collision? 2 Answers
Get angle to retarget collision object. 1 Answer
Obtaining contact point and normal of first object in collision 0 Answers
Average of Normals 1 Answer