[MY-AL] Mettre des gameobjects autour d'un cercle

Pour les scripts écrits en C#
Règles du forum
Merci de respecter la NOMENCLATURE suivante pour vos TITRES de messages :

Commencez par le niveau de vos scripts
DB = Débutant
MY = Moyen
CF = Confirmé

Puis le domaine d'application
-RS = Réseau
-AL = Algorithmie

Exemple :

[DB-RS] Mouvement perso multijoueur
Phoenix
Messages : 30
Inscription : 03 Sep 2018 17:49

[MY-AL] Mettre des gameobjects autour d'un cercle

Message par Phoenix » 16 Mars 2021 20:16

Bonjour,
je suis en train de créer un script qui me permettrait une fois fini d'instancier des gameobjects en cercle. Le problème est que je n'ai pas les connaissances mathématiques suffisantes pour finir ce script. J'ai déjà réussi à mettre les gameobject en cercle mais maintenant je veux qu'ils soient perpendiculaires au cercle (je sais ce n'est pas clair). Je vous mets des petites illustrations pour que vous comprenez mieux ma demande. De plus j'aimerai que le nombre de gameobject à instancier soit calculer automatique suivant le rayon du cercle voulu et la longueur du gameobject choisi (si à la fin je n'ai qu'une variable Vector3 à régler pour l'espacement entre les gameobject, ça serait parfait).

Image

Image

Voici également mon script :

Code : Tout sélectionner

public float radius = 1f;
    public int amountToSpawn;
    public GameObject go;
    List<GameObject> gameObjects = new List<GameObject>();
    
    public void SpawnObject()
    {
        for (int i = 0; i < gameObjects.Count; i++)
        {
            DestroyImmediate(gameObjects[i]);
        }
        gameObjects.Clear();
        Vector3 targetPosition = gameObject.transform.position;
        
        for(int i = 0; i < amountToSpawn; i++)
{
            float theta = i * 2 * Mathf.PI / amountToSpawn;
            float x = Mathf.Sin(theta) * radius;
            float y = Mathf.Cos(theta) * radius;

            GameObject ob = Instantiate(go); 
            ob.transform.parent = transform;
            ob.transform.position = new Vector3(targetPosition.x +  x, 0, targetPosition.y + y);
            gameObjects.Add(ob);
        }

    }
J'espère qu'avec les images, ça sera plus clair pour vous
Pouvez-vous m'aider ?
Par avance merci
Cordialement

Avatar de l’utilisateur
Max
Messages : 8764
Inscription : 30 Juil 2011 13:57
Contact :

Re: [MY-AL] Mettre des gameobjects autour d'un cercle

Message par Max » 16 Mars 2021 21:07

Bonsoir,

là je dirais que c'est pas vraiment de la trigo, mais une simple rotation des objets avec ton angle devrait faire le job:

Code : Tout sélectionner

  
  ...
            ob.transform.parent = transform;
            ob.transform.Rotate(0, Mathf.Rad2Deg * theta, 0);
  ...
Image
Pas d'aide par MP, le forum est là pour ça.
En cas de doute sur les bonnes pratiques à adopter sur le forum, consulter la Charte et sa FAQ

Phoenix
Messages : 30
Inscription : 03 Sep 2018 17:49

Re: [MY-AL] Mettre des gameobjects autour d'un cercle

Message par Phoenix » 16 Mars 2021 21:19

Eh bien merci. Je m'attendais à quelque chose de très compliquer mais non. C'est parfait. Et savez-vous comment on peut calculer le nombre de gameobject à instancier pour faire un tour de cercle complet ?
Merci encore
Cordialement

Avatar de l’utilisateur
Max
Messages : 8764
Inscription : 30 Juil 2011 13:57
Contact :

Re: [MY-AL] Mettre des gameobjects autour d'un cercle

Message par Max » 16 Mars 2021 21:27

Ben là par contre il va falloir sortir la règle à calcul par contre :mrgreen:
Après cela ne requière pas un niveau de ouf non plus hein ;)
Tu peux connaitre la circonférence du cercle (facile) ainsi que la largeur de l'objet à instancier. Pour démarrer c'est une première base simple de travail. Ton approche doit aussi prendre en compte le fait qu'il faut insérer des objets entiers (si tu fait une simple division de la circonférence par la largeur, tu as très peu de chance de tomber sur une valeur entière).
Image
Pas d'aide par MP, le forum est là pour ça.
En cas de doute sur les bonnes pratiques à adopter sur le forum, consulter la Charte et sa FAQ

Avatar de l’utilisateur
jmhoubre
Messages : 851
Inscription : 05 Oct 2019 22:05

Re: [MY-AL] Mettre des gameobjects autour d'un cercle

Message par jmhoubre » 16 Mars 2021 21:32

Bonsoir,

quelques remarques :

La doc déconseille d'utiliser DestroyImmediate : "You are strongly recommended to use Destroy instead. This function should only be used when writing editor code since the delayed destruction will never be invoked in edit mode."

Dans la seconde boucle, tu crées l'objet, puis tu le modifies. En faisant cela, Unity recalcule la position et le rotation des enfants de l'objet créé (je ne sais pas comment son constituées tes palissades, mais l'idée est générale).
Il vaut mieux préparer l'objet, puis le créer avec la bonne surcharge :

Code : Tout sélectionner

	Vector3 position = new Vector3(targetPosition.x +  x, 0, targetPosition.y + y);
	GameObject ob = Instantiate(go, position, Quaternion.identity, transform);  // Quaternion.identity à remplacer.
Toujours dans la seconde boucle, tu calcules theta à chaque fois : 2 multiplications et une division à chaque passage. Il vaut mieux calculer avant de passer dans la boucle et multiplier par theta :

Code : Tout sélectionner

float angle= 2 * Mathf.PI / amountToSpawn;
for(int i = 0; i < amountToSpawn; i++)
{
	float theta = i * angle;
	float x = Mathf.Sin (theta) * radius;
 	float y = Mathf.Cos (theta) * radius;    
}

Avatar de l’utilisateur
jmhoubre
Messages : 851
Inscription : 05 Oct 2019 22:05

Re: [MY-AL] Mettre des gameobjects autour d'un cercle

Message par jmhoubre » 16 Mars 2021 22:28

J'ai utilisé le scale du prefab de palissade pour régler le souci du nombre entier. Si le rayon de cercle est plus petit ou du même ordre que la largeur du prefab, cela laisse des trous entre les bouts de palissades. A partir de 1,5 fois le prefab, c'est bon. Une fois que j'ai le nombre de prefab, je recalcule leur largeur pour faire le tour complet du cercle.
Pour mes essais, j'ai utilisé un cube d'Unity, et je ne sais pas régler le point de pivot. Avec un objet provenant de Blender, il devrait y avoir moyen de régler le dernier souci sans faire trop de calculs : mes palissades sont collées par la face intérieure du cercle : il faudrait que ce soit la face externe. J'ai contourné en augmentant la taille recalculée de 2%.

Dernier souci : pour un objet quelconque, je ne sais pas comment calculer sa largeur. Je suppose qu'elle dépend par exemple des paramètres d'export de Blender dans Unity, et d'autres choses sans doute. J'ai importé un arbre d'une asset gratuite, il a un scale de (1,1,1), mais il est 6 fois plus grand qu'un cube unitaire d'Unity.

Code : Tout sélectionner

using System.Collections.Generic;

using UnityEngine;

public class Testing : MonoBehaviour
{
	[SerializeField] private GameObject palisade;
	[SerializeField] private Vector3 targetPosition;
	[SerializeField] private float radius = 5f;

	private List<Transform> palisades = new List<Transform> ();

	private void Start ()
	{
		if (palisade == null)
		{
			Debug.LogError ("Objet palissade non défini.");
			return;
		}

		// Calcule la largeur de l'objet.
		// Probablement à modifier pour un objet modélisé.
		float width = palisade.transform.localScale.z;
		Debug.Log ($"Largeur {width}");

		// Circonférence du cercle.
		float circleLength = 2 * Mathf.PI * radius;
		Debug.Log ($"Circonférence {circleLength}");

		// Nombre d'objets à placer.
		int amountToSpawn = (int) (circleLength / width);
		Debug.Log ($"Nombre de palissades {amountToSpawn}");

		// Calcul de la nouvelle largeur, augmentée de 2% pour "boucher les trous".
		// Avec un objet modélisé, régler le point de pivot à la place.
		float newWidth = circleLength / amountToSpawn * 1.02f;
		Debug.Log ($"Nouvelle largeur {newWidth}");

		// Met à jour l'échelle du prefab.
		Vector3 scale = palisade.transform.localScale;
		palisade.transform.localScale = new Vector3 (scale.x, scale.y, newWidth);

		// Calcule de la position en y du prefab.
		float height = scale.y / 2;

		float angle = 2 * Mathf.PI / amountToSpawn;
		for (int i = 0; i < amountToSpawn; i++)
		{
			float theta = i * angle;
			float x = Mathf.Sin (theta) * radius;
			float y = Mathf.Cos (theta) * radius;

			Vector3 position = new Vector3 (targetPosition.x + x, height, targetPosition.y + y);
			GameObject ob = Instantiate (palisade, position, Quaternion.identity, transform);
			ob.transform.Rotate (0f, 90 + (Mathf.Rad2Deg * theta), 0f);
			palisades.Add (ob.transform);
		}

		// Restaure la largeur du prefab.
		palisade.transform.localScale = new Vector3 (scale.x, scale.y, width);
	}
}

Dernière édition par jmhoubre le 16 Mars 2021 23:00, édité 1 fois.

Avatar de l’utilisateur
jmhoubre
Messages : 851
Inscription : 05 Oct 2019 22:05

Re: [MY-AL] Mettre des gameobjects autour d'un cercle

Message par jmhoubre » 16 Mars 2021 22:56

Résultat en image : on voit bien le trou qui subsiste entre 2 faces extérieures.
Image

Phoenix
Messages : 30
Inscription : 03 Sep 2018 17:49

Re: [MY-AL] Mettre des gameobjects autour d'un cercle

Message par Phoenix » 18 Mars 2021 19:03

Bonjour,
tout d'abord merci pour vos réponses elles vont beaucoup m'aider. J'ai continué mon script de mon coté sans avoir vu vos réponses donc voilà ce que j'ai fait :

Code : Tout sélectionner

public class SpawnObjectInCircle : MonoBehaviour
{
    public float radius = 1f;
    public int resolution = 50;
    public float sizeObject;
    public GameObject[] go;
    List<GameObject> gameObjects = new List<GameObject>();
    
    public void SpawnObject()
    {
        for (int i = 0; i < gameObjects.Count; i++)
        {
            DestroyImmediate(gameObjects[i]);
        }
        gameObjects.Clear();
        Vector3 targetPosition = gameObject.transform.position;

        //Calcule du périmètre du cercle
        float perimeter = radius * 2 * Mathf.PI;
        float numObject = perimeter / sizeObject;
        int amountToSpawn = (int)numObject;

        for (int i = 0; i < amountToSpawn; i++)
{
            float theta = i * 2 * Mathf.PI / amountToSpawn;
            float x = Mathf.Sin(theta) * radius;
            float z = Mathf.Cos(theta) * radius;


            int random = Random.Range(0, go.Length);
            GameObject ob = Instantiate(go[random]); 
            ob.transform.parent = transform;
            ob.transform.position = new Vector3(targetPosition.x +  x, targetPosition.y, targetPosition.z + z);
            ob.transform.Rotate(0, Mathf.Rad2Deg * theta, 0);
            gameObjects.Add(ob);
        }

    }

    public void Visualize()
    {
        LineRenderer line = GetComponent<LineRenderer>();
        Vector3 targetPosition = gameObject.transform.position;
        line.enabled = true;

        line.positionCount = resolution;

        for (int i = 0; i < resolution; i++)
        {
            float theta = i * 2 * Mathf.PI / resolution;
            float x = Mathf.Sin(theta) * radius;
            float y = Mathf.Cos(theta) * radius;
            line.SetPosition(i, new Vector3(targetPosition.x + x, targetPosition.y + 1, targetPosition.z + y));
        }
    }

    public void ResetSpawn()
    {
        for (int i = 0; i < gameObjects.Count; i++)
        {
            DestroyImmediate(gameObjects[i]);
        }
        gameObjects.Clear();
        GetComponent<LineRenderer>().enabled = false;
        
    }

}
Je vais bien sûre l'adapter avec ce que vous m'avez dit. D'ailleurs je n'ai pas préciser que le script fonctionne sans avoir mit play auparavant. En effet j'ai créer un script pour intégrer des boutons dans l'inspecteur pour pouvoir faire les modifications voulu sans mettre play la scène.
Maintenant que j'ai un résultat presque satisfaisant je voudrait aller plus loin :
- je voudrait pouvoir changer dans l'inspecteur la forme que dois prendre les palissades (carré, rectangle, courbe...)
- vous avez peut-être remarqué que dans mon script j'ai inclut un line renderer. Je voudrait pouvoir voir et modifier la forme directement (comme un box collider par exemple)
- j'aimerais aussi que les palissades s'adaptent au terrain
- et vraiment à la toute fin j'aimerait inclure des zones où on pourrait dire que l'objet peut apparaitre à un tel endroit ou non etc.
Avez-vous des pistes à me donner ? Et j'aimerai également que vous me donnez votre avis sur l'intérêt de ce script. Si il peut intéresser des personnes je le mettrait dans l'asset store gratuitement. (je ne me suis pas encore renseigner sur le sujet, je ne sais donc pas si c'est possible à faire).
Merci encore
Cordialement

Répondre

Revenir vers « (C#) CSharp »