Page 1 sur 1

[C#] Raycasting de Sphere sans l'API d'Unity

Publié : 12 Fév 2017 03:39
par ZJP
Bonjour,

Pour mon IA , il me fallait - vu le nombre de tests à faire- une fonction (100% Math "Classique" :mrgreen: ) de RayCast pouvant être utilisée dans des threads (voir même dans une DLL Native en C++), le tout a partir de données "brutes".

Ce code apporte une solution a ce problème.


-NOM DU SCRIPT : testINTERSECT

-AUTEUR(S): ZJP aidé par Google. :
https://www.google.fr/search?q=intercep ... EQ_AUIBygC
http://paulbourke.net/geometry/circlesphere/
http://stackoverflow.com/search?q=ray%20sphere
http://stackoverflow.com/questions/6533 ... tersection
https://en.wikipedia.org/wiki/Line%E2%8 ... tersection
etc etc... Honnêtement, ce ne fut pas si facile. :mrgreen:

-DESCRIPTION : Fonction qui permet de tester si un rayon (ligne) entre en collision avec une sphère de détection. Le tout, sans utiliser l'API d'Unity et/ou les fonctions de Vector3..
Edit du 22/09/17 : Ajout de la localisation du point d'impact

-UTILISATION :
Pour tester la fonction.
a) Créer une scène avec 3 GOs (2 Cubes pour délimiter le rayon de Raycast, 1 Sphere cible. Pas besoin de Collider vu que le but est de se passer de PhysX justement. Edit du 22/09/17 : Ajoutez un GO pour marquer le point d'impact
b) Placer le script sur un Go quelconque. Ex la Camera
c) Activer l'affichage des Gizmos
d) Après lancement du projet, bouger dans la scène l'un des 3 GOs. Le rayon du Raycast passera au rouge si détection.

-SCRIPT :

Code : Tout sélectionner


using System;
using System.Diagnostics;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class testINTERSECT : MonoBehaviour {

	public Transform deb;   // Cube
	public Transform fin;   // Cube
	public Transform cible; // Sphere
	public Transform contact;
	private float rayonC = 0.0f;
	private Color GizCol = Color.green;
	
	private Vector3 tempo = new Vector3(0,0,0);

	void Start () {
		rayonC = cible.localScale.y / 2.0f;
		/*
		// test sqrt ***********************
		UnityEngine.Debug.Log("Precision methode UNITY = " + Mathf.Sqrt(123456.0f));
		UnityEngine.Debug.Log("Precision methode .NET = "  + (float)Math.Sqrt(123456.0f));
		int f = 2000000;
		int z = 0;
		float c = 0.0f;
		var s1 = Stopwatch.StartNew();
		for (z = 0; z < f; z++){
			c = Mathf.Sqrt(z + 1);
		}
		s1.Stop();
		//
		var s2 = Stopwatch.StartNew();
		for (z = 0; z < f; z++){
			c = (float)Math.Sqrt(z + 1);
		}
		s2.Stop();
		UnityEngine.Debug.Log("Timing methode UNITY = " + (double)(s1.Elapsed.TotalMilliseconds));
		UnityEngine.Debug.Log("Timing methode .NET = "  + (double)(s2.Elapsed.TotalMilliseconds)); // 2,5 fois plus rapide !!!!!!!!!!!
		*/
	}

	void LateUpdate () {
		bool Bam = SphereIntersect(deb.position.x, deb.position.y, deb.position.z , fin.position.x, fin.position.y, fin.position.z , cible.position.x, cible.position.y, cible.position.z , rayonC);
		GizCol = Bam ? Color.red : Color.green;
	}

	public bool SphereIntersect( float x1, float y1, float z1, float x2, float y2, float z2, float xC, float yC, float zC, float r )
	{
		float a = (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1) + (z2 - z1) * (z2 - z1);
		//Debug.Log("Distance entre les cubes = " + Mathf.Sqrt(a)); // Pour info !!!
		//Debug.Log("Distance sans SQRT - plus rapide- entre les cubes = " + a); // Pour info !!!
		//if (a > far * far || a < near * near) return false; // Pas la bonne distance. Comparaison sans SQRT !!!
		float b = 2.0f * ((x2 - x1) * (x1 - xC) + (y2 - y1) * (y1 - yC) + (z2 - z1) * (z1 - zC));
		float c = (x1 - xC) * (x1 - xC) + (y1 - yC) * (y1 - yC) + (z1 - zC) * (z1 - zC) - r * r;
		float d = b * b - (4.0f * a * c);

		if (d < 0.01f) return false;
		else
		{ 
			// Contact perpendiculaire au centre de la sphere
			//float mu = -b / ( 2 * a);

			// Contact fin du raycast
			//float mu = (-b + (float)Math.Sqrt((b * b) - 4 * a * c )) / (2 * a);

			// Contact depart du raycast
			float mu = (-b - (float)Math.Sqrt((b * b) - 4 * a * c )) / (2 * a);
			tempo.x = x1 + mu * (x2 - x1);
			tempo.y = y1 + mu * (y2 - y1);
			tempo.z = z1 + mu * (z2 - z1);
			contact.transform.position = tempo;
			return true;
		}
	}

	public void OnDrawGizmos() 
	{
		if(fin == null || deb == null) return;
		Gizmos.color = GizCol;
		Gizmos.DrawLine(deb.position, fin.position);
	}
	
}
PS : si vous cherchez un SQRT plus performant .

Re: [C#] Raycasting de Sphere sans l'API d'Unity

Publié : 12 Fév 2017 13:27
par F@B
Super ZJP ! :super: :super:

merci

Re: [C#] Raycasting de Sphere sans l'API d'Unity

Publié : 12 Fév 2017 15:18
par E3DStef
Super intéressant ! Bravo et merci ZJP.

Par contre désolé je n'ai pas saisi pourquoi c'est mieux que d'utiliser la détection d'unity ?

Problématique de charge CPU de calculs ?

+
Stef

Re: [C#] Raycasting de Sphere sans l'API d'Unity

Publié : 12 Fév 2017 15:56
par ZJP
F@B a écrit :..merci
A ton service ;-)
E3DStef a écrit :Super intéressant ! Bravo et merci ZJP.
Merci.
E3DStef a écrit :..pourquoi c'est mieux que d'utiliser la détection d'unity ?
Tu ne peux pas utiliser l'API d'Unity pour le multi-thread. En tout cas pas facilement et sans risque. De plus, j'étais à la recherche de formules plus classiques pouvant être utilisées même hors Unity .
E3DStef a écrit :Problématique de charge CPU de calculs ?
Multi-thread possible + pas de limite sur le nombre ni la portée des Raycasts?. Oui, incontestablement

Pour mon projet je traite les entités en 3 groupes.
a) Ceux dans le champs de vision du joueur.
b) Ceux proches du joueur mais pas vus par lui.
c) Ceux éloignés du joueur.
Dans les cas a) je suis sur du 100% PhysX. Dans b) un mélange PhysX + Math classique. Dans c), 100% Math classique.

Re: [C#] Raycasting de Sphere sans l'API d'Unity

Publié : 12 Fév 2017 15:59
par E3DStef
Ok ok intéressant tout cela...

:pleur4: pourquoi je n'ai qu'une vie pour apprendre tout cela :pleur4:

Je suivrai ton pblm avec intérêt !

A+ et bon dimanche...

Stef

Re: [C#] Raycasting de Sphere sans l'API d'Unity

Publié : 22 Sep 2017 19:26
par ZJP
Mise à jour avec un ajout de la localisation du point d'impact. ;-)