I am currently trying to code a system that will play collision sounds when a 2d rigid body ball collides with 2d floors, walls, etc.
Although the function to play the sounds works perfectly as I expected it to, I am having an hours-long nightmare trying to calculate the angle of the collision so that it doesn't keep randomly playing the sound as it rolls across a surface (in my case the surface is slightly curved); only when it collides with it.
I've tried all kinds of crazy math, and none of it works- the result is always the same, either it continues playing the collision sounds at random, or it doesn't play them at all.
currently, my code is something along these lines:
private void OnCollisionEnter2D(Collision2D collision)
{
if (Mathf.Abs(Vector2.Angle(_rigidbody.velocity.normalized, -collision.GetContact(0).normal)) > 15)
{
return;
}
else
{
PlayTheSound(); //pseudo-code, my actual function for playing the sound goes here, but there's no need to define it in this question.
}
}
My understanding of the math here is basically, "take the difference between the ball's velocity direction and the inverse of the surface's outward direction, and if it happens to be negative, just erase the minus sign. If the result is more than 15 degrees, don't bother, otherwise, go ahead and play the sound."
I am not great at math as-is, and I'm also very rusty (returning to unity after a very, very long hiatus, trying to fix an old project)
After some testing, the problem appears to be that, for whatever weird reason, the velocity's normal and the surface normal are always at an exact 90-degree difference from each other. the way I tested that is by modifying my code to look like this:
private Vector2 _lastnormal;
private Vector2 _lastvel;
private void OnCollisionEnter2D(Collision2D collision)
{
if (Mathf.Abs(Vector2.Angle(collision.GetContact(0).normal, -_rigidbody.velocity.normalized)) > 15)
{
_lastvel = _rigidbody.velocity.normalized;
_lastnormal = collision.GetContact(0).normal;
return;
}
else
{
PlayTheSound();
}
}
private void Update()
{
Debug.DrawRay(Vector2.zero, _lastnormal, Color.cyan);
Debug.DrawRay(Vector2.zero, -_lastvel, Color.red);
Debug.DrawRay(Vector2.zero, _rigidbody.velocity.normalized, Color.green);
}
The resulting debug lines look like this. Green is the velocity of the ball updated in realtime (can be safely ignored cause the ball is rolling down a hill currently in this screenshot and not actively in the collision that made these lines), cyan is the hit normal (pointing up), Red is the negative incoming velocity (pointing to the right)... it is always at 90 degrees. Always. Huh? The ball hit the surface head-on when it updated these lines, and it only updates the lines in my collision entered function.. shouldn't it be one line then, since the incoming velocity normal and the negative surface normal would be the same in a head-on collision?
So the question seems to boil down to wondering why the velocity is always at an exact 90-degree angle to the hit normal rather than being, y'know, the actual angle of the velocity.