0

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?

the debug lines are always at 90 degree angles

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.

FenFox
  • 21
  • 5
  • What do you mean by "the velocity's normal"? If you normalize the velocity, you get a unit vector in the same direction as the velocity. If that velocity is rolling along the surface, then by definition that's 90 degrees to the surface normal. What other thing would you expect here? – DMGregory Dec 13 '22 at 04:29
  • Keep in mind, the velocity you read in OnCollisionEnter is the velocity after collision resolution, not the incoming velocity as you describe. That means any component sticking into the surface will have been removed (to prevent the object from tunneling into the surface) – DMGregory Dec 13 '22 at 04:32
  • @DMGregory despite my best efforts i guess i didn't explain very well...basically it keeps colliding even when its rolling on the surface, im trying to make code that only plays the sound when it actually does hit a surface head-on more or less. how might i got about getting the velocity normal before collision resolution then? – FenFox Dec 13 '22 at 08:31
  • You may be interested in How to measure force of an impact to deal damage. You can get the collision resolution impulse applied, which will be large when the object's velocity changes abruptly, and small when the velocity barely changes during a roll. Big changes = lots of energy released = sound. – DMGregory Dec 13 '22 at 13:59
  • well i tried that but part of the problem is that the force seems to be randomly pretty large even when just rolling along the floor. i know because my sound play function bases it's volume on the impact force and its playing really loudly even when just rolling. manually setting the values confirms its not my sound playing function's problem. – FenFox Dec 13 '22 at 19:14

0 Answers0