- Home /
How to check additional contacts during collisionStay
Situation: I want to play collision sounds for a dice rolling.
Problem: The dice rolls in a way that it doesn't always leave the table and collisionEnter is not called when the dice flips over.
So I have to find a way to chek when the next corner hits the table in OnCollisionStay.
Already attepted: Counting the contacts and play the sound when you have more contacts than before.
Problem is that Unity detects any amount up to 30 or so collision points for a simply collision of a cube with a plane.
Sometimes it's 7 in one and 8 in the next frame without any visual distintion and the sound get's played too often.
simple workaround just play random colliding sounds (I#d prefer to properly synchronise the sound with the dice movement though.
Just a thought, I presume the impact of the surface alters the dice's movement so perhaps you could check for shifts in velocity and angularVelocity?
An untested idea: Given the simple geometry, perhaps you check the distance of each corner of the die to the table each frame. Point distance to a plane is an easy calculation. A corner is considered a hit when the distance to the table is within some threshold. Record the hit/no-hit state for each of the eight corners each frame. If a corner goes from no-hit to hit state during the frame, play the sound.
Checking geometry is a nice idea, but this is a items tumbling/flipping is a rather common problem. And it can get rather complicated fast.
So I'd rather have a more general solution. I'll try checking for velocity shifts.
Answer by vexe · Oct 28, 2013 at 11:55 PM
OK, I actually worked something out for you. It's not perfect, but you could make it so.
The idea is very simple, cast rays out of your cube's faces! when a ray hits, you play the sound, very simple.
(I actually had some issues here and got help from robertbu recently a few hours ago, so I was asking for help to help you - yaw dawg I heard you like help, I asked for help to help you, so that I could help while I'm being helped :-D)
Hehe, anyway. Instead of casting 3 rays, I'm now casting 6, from the center of the cube going out.
The way you could improve this, is for example by adding more rays, in other directions (diagonals, or maybe 45-dgs versions of the current vectors?) thus making it more sensitive.
public float length = 1f; // this is what you will mostly mess a lot with, this is the length of the rays depending on the way you throw the dice, I found 1 to be a good value...
public AudioClip clip;
Transform mTransform;
Face[] faces = new Face[6];
void Awake()
{
mTransform = transform;
for(int i = 0; i < faces.Length; i++)
faces[i] = new Face();
}
void Update()
{
faces[0].dir = mTransform.up;
faces[1].dir = -mTransform.up;
faces[2].dir = mTransform.right;
faces[3].dir = -mTransform.right;
faces[4].dir = mTransform.forward;
faces[5].dir = -mTransform.forward;
foreach (var face in faces) {
RaycastHit hit;
if (face != currentFace && Physics.Raycast(mTransform.position, face.dir, out hit, length)) {
currentFace = face;
audio.PlayOneShot(clip);
print("face hit: " + face.id);
}
}
}
public class Face
{
// these two are just for testing purposes
static int counter = 0;
public int id;
public Vector3 dir = Vector3.zero;
public Face()
{
id = counter++;
}
}
The Face
class is just their for convenience, don't like it? Nuke it and use a Vector3
sequence for the directions instead, it's up to you.
The currentFace
is there so that we don't play the clip, if we settle.
Some testing:
void OnGUI()
{
if (GUI.Button(new Rect(10, 10, 100, 100), "AddForce")) {
int s = (Random.Range(0, 10) > 5) ? 1 : -1;
rigidbody.AddForce(Random.Range(10, 15) * s, Random.Range(200, 300), Random.Range(10, 15) * s);
rigidbody.AddTorque(Random.Range(10, 20), Random.Range(10, 20), Random.Range(10, 20));
}
}
End result:
I have been busy with other stuff, so I haven't even tested it yet, but it seems a bit cumbersome. Plus I don't quite understand what you are even doing. For one: why do you shoot the rays out of the faces, not the corners?
Answer by jacupiri · Jan 02, 2018 at 07:22 PM
I believe my solution is a lot simpler: just detect the collinsions that have a minimum relative velocity.
It works for any object, including dices.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SoundOnImpact : MonoBehaviour
{
private float impactVelocity;
private AudioSource audioSource;
// improvement: create a list of impact sounds that can activate depending on the otherGameObject that it collided with
public AudioClip impactSound;
// for further implementation, for sliding/dragging sounds (OnCollisionStay)
// public AudioClip dragSound;
private float soundMultiplier;
void Start ()
{
audioSource = GetComponent<AudioSource>();
}
void OnCollisionEnter (Collision collision)
{
impactVelocity = collision.relativeVelocity.magnitude;
print (gameObject.name + " collided with " + collision.gameObject.name + ", at " + (impactVelocity/10));
//Check that we are colliding with sufficient velocity
if (impactVelocity > 1.0f)
{
if (impactVelocity / 100 > 1.0f)
{
soundMultiplier = 1f;
}
else
soundMultiplier = impactVelocity / 10;
audioSource.PlayOneShot(impactSound, soundMultiplier);
}
}
}
I am still to implement drag/slide sound and the possibility of adding multiple soundclips for types of collision.
Your answer
Follow this Question
Related Questions
Not detecting a simple collision? 2 Answers
OnCollisionStay perfomance 1 Answer
Wheel Colliders not registering Collision on OnCollisionStay 1 Answer
Ball n Floor Collision Problems 0 Answers
Is this how a ladder collider works? 1 Answer