8 votes

Comment obtenir les données de collision de Physics.OverlapBox() ?

J'essaie de trouver d'autres méthodes pour enregistrer les collisions (autre que OnCollisionEnter() y OnCollisionExit() ). J'utilise actuellement Physics.OverlapBox() mais j'ai besoin de plus d'informations sur la collision, à savoir la normale et le point.

Je peux utiliser Physics.BoxCast() mais le problème est qu'il déplace une boîte d'une distance donnée, et qu'en utilisant maxDistance = 0f ne fonctionnera pas.

J'ai besoin d'une méthode pour vérifier les collisions, similaire à Physics.OverlapBox() sauf qu'il renverrait aussi des informations sur tous des collisions dans la distribution.

Toute aide est appréciée. Merci.

4voto

Ruzihm Points 7430

Vous pouvez utiliser OverlapBox et utiliser Collider 's ClosestPoint pour sélectionner un seul point de chevauchement, et l'utiliser pour effectuer vos calculs de collision.

Collider[] cols = Physics.OverlapBox(...);
Vector3 myPosition = transform.position; // for example

foreach (Collider col in cols) {
    Vector3 closestPoint = col.ClosestPoint(myPosition);
    Vector3 positionDifference = (closestPoint-myPosition);
    Vector3 overlapDirection = positionDifference.normalized; 
}

Ce site overlapDirection sera orienté dans la direction opposée à la position que vous utilisez dans ClosestPoint au centre de chaque collisionneur. Si vous voulez quelque chose basé sur la surface de votre objet, vous pouvez utiliser cette direction de chevauchement pour placer un raycast dirigé vers votre objet, afin de trouver la normale de cette façon :

// ...

foreach (Collider col in cols) {
    Vector3 closestPoint = col.ClosestPoint(myPosition);
    Vector3 positionDifference = (closestPoint-myPosition);
    Vector3 overlapDirection = positionDifference.normalized; 

    RaycastHit hit;
    int layerMask = 1;  // Set to something that will only hit your object
    float raycastDistance = 10.0; // something greater than your object's largest radius, 
                                  // so that the ray doesn't start inside of your object
    Vector3 rayStart = myPosition + overlapDirection * raycastDistance;
    Vector3 rayDirection = -overlapDirection ;

    if (Physics.Raycast(rayStart, rayDirection, out hit, Mathf.Infinity, layerMask)) {
        Debug.Log(hit.normal);
        Debug.Log(hit.position);
    } else {
        // The ray missed your object, somehow. 
        // Most likely it started inside your object 
        // or there is a mistake in the layerMask
    }
}

4voto

isp-zax Points 2226

Votre préoccupation, exprimée dans le commentaire de la première réponse, est valable, mais la mauvaise nouvelle est qu'il n'y a pas d'astuce simple pour la contourner. Ce que vous devez rechercher s'appelle détection continue des collisions avec une version simplifiée décrite dans mon réponse sur un sujet assez similaire :

En gros, pour chaque objet en mouvement dans votre scène, vous devez calculer le moment de la prochaine collision dans la fraction de l'image 0<t0<1 , puis avancer les positions jusqu'à cet instant t0 dans le cadre, mettre à jour les les vitesses dues à la collision et passer à la collision suivante. t0<t1<1 jusqu'à ce que vous atteigniez le temps de tn=1 (fin du cadre), en s'assurant que que vous ne restez pas bloqué au milieu de l'image à cause de l'arrondi des calculs. calcul ou d'objets "en coin". Pour les collisionneurs sphériques, cela est généralement effectué en utilisant capsule contre capsule (pour les paires d'objets) intersection et capsule ou boîte pour les frontières.

Contrairement au moteur simple de la réponse à laquelle je me réfère, Unity a une détection continue des collisions . Vous pouvez donc activer les collisions continues et la dynamique continue, ce qui est très coûteux en termes de calcul.

Vous pouvez également essayer d'utiliser RigidBody.SweepTest qui renvoie les informations sur les collisions les plus proches. Remarquez que même s'il y a aussi RigidBody.SweepTestAll ça n'aide pas beaucoup. Non seulement parce qu'elle ne renvoie que les 128 premières collisions, mais aussi parce qu'elle ne les traite pas puisqu'il n'y a pas de réflexion. Pour un comportement physiquement correct, vous devez faire ce qui est décrit ci-dessus - avancer le temps jusqu'à la première collision et mettre à jour les vélocités. Soit avec le moteur physique, soit par vous-même. C'est très coûteux et peu de jeux le font, même en trichant en utilisant des objets simplifiés (les sphères sont les moins chères car deux sphères balayées sont deux capsules et leur intersection est un calcul relativement bon marché), mais le nombre d'étapes, surtout dans le cas "coincé" où les objets n'ont nulle part où aller et par conséquent sont constamment en collision pourrait être très importante et de tels cas se produisent plus souvent qu'on ne le pense.

Pour les objets complexes, il est peu probable que vous puissiez faire mieux que SweepTest, à moins que vous ne le déclenchiez sur la base de primitives plus simples, telles que Physics.BoxCast o Physics.SphereCast . Encore une fois, même s'il y a Physics.BoxCastAll y Physics.SphereCastAll elles ne sont pas particulièrement utiles car seule la première collision est garantie. Ces xxxCastAll sont les fonctions que vous avez écrites et que vous recherchiez, alors essayez-les, elles pourraient fonctionner suffisamment bien pour votre cas d'utilisation.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X