problème avec Destroy(GameObject)

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
Avatar de l’utilisateur
Kaloverian
Messages : 343
Inscription : 10 Août 2016 03:03

problème avec Destroy(GameObject)

Message par Kaloverian » 11 Nov 2021 02:50

bonjour,

J'ai construit un script C# s'appliquant sur un objet NewObj qui est instancié de façon dynamique à chaque appui de la touche I dans une direction aléeatoire à l'endroit où se trouvait le précédent objet de la liste.

Code : Tout sélectionner

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

//ne fonctionne pas avec Destroy.A savoir pourquoi ?

public class random : MonoBehaviour
{
    int x_direction_spawn, y_direction_spawn, z_direction_spawn;
    public float moveSpeed;
    public GameObject NewObj;
    public Mesh mesh;
    int i = 0;
   
    Vector3  V1;
    public int a;
    public Material mater;
    public int x0,y0,z0;
    public int x_direction_0, y_direction_0, z_direction_0;
    

    List<GameObject> ObjectList;
    List<Vector3> ListVecteurs;


    void Start()
    {   
       
        ListVecteurs = new List<Vector3>();
        ListVecteurs.Add(V1);
        
        ObjectList = new List<GameObject>();
        ObjectList.Add(NewObj);//permet à i de commencer à 1 dans la création dynamique d'objets dans Update()
        ObjectList[0]= new GameObject("toto" + 0);
        
        ObjectList[0].transform.position = new Vector3(x0,y0,z0);
        ListVecteurs[0]= new Vector3(x_direction_0, y_direction_0, z_direction_0);
       


    
    }



    void Update()
    {   
        
        if (Input.GetKeyDown(KeyCode.I))
        {   
           
            i++;
           
             int x_direction_spawn = Random.Range(-a,a);
             int y_direction_spawn = Random.Range(-a,a);
             int z_direction_spawn = Random.Range(-a,a);



            ObjectList.Add(NewObj);//toujours Add avant création dynamique avec new
            ObjectList[i] = new GameObject("toto" + i);
           

           
            ObjectList[i].transform.position = ObjectList[i-1].transform.position;
            float xPos = ObjectList[i].transform.position.x;
            float yPos = ObjectList[i].transform.position.y;
            float zPos = ObjectList[i].transform.position.z;

           
            ObjectList[i].AddComponent<MeshRenderer>().material = mater;
            ObjectList[i].AddComponent<MeshFilter>().mesh = mesh;

            ListVecteurs.Add(V1);
            ListVecteurs[i]= new Vector3(x_direction_spawn, y_direction_spawn, z_direction_spawn);

             print("x_direction_spawn :" + x_direction_spawn);
             print("y_direction_spawn :" + y_direction_spawn);
             print("z_direction_spawn :" + z_direction_spawn);
             print("------------------------------------------");

             /*while(i> 0){ 
             Destroy(ObjectList[i-1]);
             }*/

            
            }// fin de if

         

            /*while(i> 0){ 
             Destroy(ObjectList[i-1]);
             }*/
          
         for (int k = 0; k < ObjectList.Count; k++)
           { 
             ObjectList[k].transform.Translate(ListVecteurs[k]* moveSpeed * Time.deltaTime,Space.Self);
           
             }
         //}//fin de for*/

       

    }//fin de Update()
}//fin de class




Le problème est qu'à chaque appui de la touche I,je souhaite détruire l'objet précédent mais je n'y arrive pas malgré les différents placements dans ce code de ces instructions:

Code : Tout sélectionner

 while(i> 0){ 
     Destroy(ObjectList[i-1]);
             }
D'où vient le problème?

merci de votre aide

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

Re: problème avec Destroy(GameObject)

Message par jmhoubre » 11 Nov 2021 11:49

Bonjour,

déjà, tu devrais adopter les conventions de nommage communément admises. Les relecteurs de tes scripts t'en remercient d'avance.

Quelques questions et remarques :
  1. Pas besoin d'alourdir ton script avec des commentaires "// fin de boucle" : si tu passes ton curseur sur l'accolade, Visual Studio t'indique où tu te trouves.
  2. public class random : MonoBehaviour : le nommage d'une classe utilise la PascalCase => public class Random : MonoBehaviour
  3. int x_direction_spawn, y_direction_spawn, z_direction_spawn; : ces variables ne sont pas utilisées, puisque tu les redéclares dans l'update (int x_direction_spawn = Random.Range (-a, a); etc...)
  4. elles sont déclarées en int, et pas en float : c'est étrange.
  5. J'ai attaché ton script à un objet vide, j'ai mis un prefab de cube dans le champ NewObj, j'ai viré tout ce qui concerne les mesh et j'ai pu faire tourner ce script. En ajoutant ce code à la fin du bloc if (Input.GetKeyDown), cela fonctionne.

    Code : Tout sélectionner

                i--;
                print (ObjectList.Count);
                // Détruit l'objet dans la scène.
                Destroy (ObjectList[i]);
                // Détruit la référence de l'objet dans la liste.
                ObjectList.Remove ((ObjectList[i]));
                print (ObjectList.Count);
    
  6. Cette façon de faire me semble hasardeuse, mais ne sachant pas très bien ce que tu veux faire, je ne peux pas aller plus loin.
  7. A mon avis, tu pourrais plutôt :
    1. créer ton objet : GameObject go = new GameObject ();
    2. ajouter tes composants : go.AddComponent<>();
    3. initialiser les composants, y compris le transform.
    4. ajouter l'objet à la liste.
    5. ne pas manipuler la liste à l'aide d'un index.
    6. sachant que quand tu crées un objet tu détruis aussitôt le précédent, une liste est-elle bien utile ? Une référence sur l'objet en cours, et une seconde sur le nouvel objet me paraissent suffisantes, avec un swap en fin d'update.

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

Re: problème avec Destroy(GameObject)

Message par jmhoubre » 11 Nov 2021 13:17

J'ai fait le script à ma manière :

Code : Tout sélectionner

using UnityEngine;

public class Spawner : MonoBehaviour
{
	[Header ("Object properties")]
	[SerializeField] private float moveSpeed;
	[SerializeField] private Material material;

	private GameObject newObject;
	private GameObject oldObject;

	private Vector3 objectDirection;

	private void Update ()
	{
		if (Input.GetKeyDown (KeyCode.I))
		{
			newObject = CreateNewObject ();

			// Cas particulier : on vient de lancer le jeu et il n'y a pas d'ancien objet.
			if (oldObject == null)
			{
				// On crée le nouveau en (0, 0, 0).
				newObject.transform.position = Vector3.zero;
			}
			else
			{
				// Sinon on le crée à la position de l'ancien.
				newObject.transform.position = oldObject.transform.position;
			}

			// Permute les références des objets et détruit l'ancien.
			GameObject tempObject = oldObject;
			oldObject = newObject;
			newObject = null;
			Destroy (tempObject);

			// Donne une nouvelle direction à l'objet.
			objectDirection = GetRandomNormalizedVector ();
		}

		// Mouvement de l'objet, avec cas particulier du premier.
		if (oldObject != null)
		{
			oldObject.transform.Translate (objectDirection * moveSpeed * Time.deltaTime);
		}
	}

	private GameObject CreateNewObject ()
	{
		// Création d'un cube.
		GameObject go = GameObject.CreatePrimitive (PrimitiveType.Cube);

		go.name = "Object - " + Time.frameCount.ToString ("0000");

		// Ajoute le material.
		go.GetComponent<Renderer> ().material = material;

		return go;
	}

	private Vector3 GetRandomNormalizedVector ()
	{
		return Random.onUnitSphere;
	}
}
  • J'ai renommé la classe Spawner, puisque c'est ce qu'elle fait. Je n'aime pas trop utiliser pour mes identifiants des mots clés ou des identifiants du C# ou d'Unity.
  • La classe crée des cubes rouges. Il est simple de créer d'autres objets en modifiant CreateNewObject.
  • Penses à bien reculer la caméra sinon les objets sont hors champ. J'ai mis un fond bleu foncé à la place de la skybox.
  • En relisant le message, je m'aperçois que la création et la destruction du même objet est un peu crétin... Il suffit de changer sa direction.

Avatar de l’utilisateur
Kaloverian
Messages : 343
Inscription : 10 Août 2016 03:03

Re: problème avec Destroy(GameObject)

Message par Kaloverian » 11 Nov 2021 23:31

merci bien !
Comment modifier la fonction CrearteNewObject() pour créer un objet de hierarchy de son choix à faire glisser dans un champ supplémentaire du script ?

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

Re: problème avec Destroy(GameObject)

Message par jmhoubre » 12 Nov 2021 01:04

Désolé, je ne comprends pas ta question.

Avatar de l’utilisateur
Kaloverian
Messages : 343
Inscription : 10 Août 2016 03:03

Re: problème avec Destroy(GameObject)

Message par Kaloverian » 12 Nov 2021 01:13

D'après la documentation de l'API,:
https://docs.unity3d.com/ScriptReferenc ... itive.html
avec CreatePrimitive,on peut seulement créer soit:
-un plan
-un cube
-une sphère
-une capsule
-un cylindre

IDans ton code,,un cube est crée.
Je pourrais le modifier pour créer un plan,une sphère,une capsule,un cylindre mais pas un prefab !

Peut-on modifier cette partie de ton code:

Code : Tout sélectionner

	private GameObject CreateNewObject ()
	{
		// Création d'un cube.
		GameObject go = GameObject.CreatePrimitive (PrimitiveType.Cube);

		go.name = "Object - " + Time.frameCount.ToString ("0000");

		// Ajoute le material.
		go.GetComponent<Renderer> ().material = material;

		return go;
	}
pour pouvoir mettre en scène un préfab?

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

Re: problème avec Destroy(GameObject)

Message par jmhoubre » 12 Nov 2021 13:13

Ok.

Au début de la classe, on ajoute la déclaration du prefab :

Code : Tout sélectionner

	[SerializeField] private GameObject prefabToSpawn;
Puis, dans la fonction CreateNewObject, on instancie le prefab :

Code : Tout sélectionner

	private GameObject CreateNewObject ()
	{
		// Instantiation du prefab.
		GameObject go = GameObject.Instantiate (prefabToSpawn, transform);

		go.name = "Object - " + Time.frameCount.ToString ("0000");

		// Ajoute le material.
		go.GetComponent<Renderer> ().material = material;

		return go;
	}

Avatar de l’utilisateur
Kaloverian
Messages : 343
Inscription : 10 Août 2016 03:03

Re: problème avec Destroy(GameObject)

Message par Kaloverian » 13 Nov 2021 02:26

je souhaite remplacer la condition par celle-ci:

Code : Tout sélectionner

if(dist>A)
avec en plus en début de classe:
float dist;
public Transform other;
et quelque part:
dist = Vector3.Distance(other.position,transform.position);
ou
dist = Vector3.Distance(other.position,go.transform.position);

de tele sorte que mon objet reste confiné au sein d'une sphère de rayon A
Dès que l'objet ateint la surface de la sphère,il change aléatoirement de direction mais vers l'intérieur de la sphère.

mais je n'y arrive pas.
Peut-on m'aider?

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

Re: problème avec Destroy(GameObject)

Message par jmhoubre » 13 Nov 2021 14:39

Bonjour,

une des bonnes pratiques consiste à ne donner à une classe qu'une seule responsabilité (un seul travail). C'est pourquoi j'ai scindé le projet en 3 scripts :
  • Une classe Spawner qui ne s'occupe que de créer les objets. Le script est attaché à un Empty. Seul l'objet à spawner est exposé. On peut aisément modifier cette classe pour spawner des objets différents, ou spawner des objets à plusieurs instants (actuellement, la classe spawne un seul objet quand le jeu est lancé).
  • Une classe PlayerController qui s'occupe de l'objet spawné (je considère qu'il s'agit du joueur). Elle gère sa vitesse, sa direction, sa couleur, son mouvement et le changement de direction. Le script est attaché au prefab. L'objet à spawner doit avoir un Collider.
  • Une classe SphereContainer, qui s'occupe de la zone de confinement. Quand l'objet sort de la zone, sa direction est inversée (ce comportement est aisément modifiable dans la classe PlayerController). Le script est attaché à une sphère, avec un SphereCollider (case IsTrigger cochée) et un Rigidbody (case UseGravity décochée). Les composants du mesh sont supprimés. La taille de la zone est règlable en modifiant le Radius du Collider.
Voici les scripts :

Code : Tout sélectionner

using UnityEngine;

public class Spawner : MonoBehaviour
{
	[Header ("Spawner properties")]
	[SerializeField] private GameObject prefabToSpawn;

	private void Start ()
	{
		// Instancie un objet au début du jeu.
		CreateNewObject ();
	}

	private void CreateNewObject ()
	{
		// Instantiation du prefab.
		GameObject.Instantiate (prefabToSpawn, transform);
	}	
}

Code : Tout sélectionner

using UnityEngine;

public class PlayerController : MonoBehaviour
{
	[Header ("Object properties")]
	[SerializeField] private float moveSpeed;
	[SerializeField] private Material material;

	private Vector3 randomDirection;

	private void Start ()
	{
		// Choisit une direction.
		randomDirection = GetRandomNormalizedVector ();

		// Initialise l'objet.
		name = "Object - " + Time.frameCount.ToString ("0000");
		GetComponent<Renderer> ().material = material;
	}

	void Update ()
	{
		// Mouvement de l'objet.
		transform.Translate (randomDirection * moveSpeed * Time.deltaTime);

		// Changement de direction sur commande.
		if (Input.GetKeyDown (KeyCode.Space))
		{
			// Donne une nouvelle direction à l'objet.
			randomDirection = GetRandomNormalizedVector ();
		}
	}

	/// <summary>
	/// Renvoie une direction aléatoire et normalisée.
	/// </summary>
	private Vector3 GetRandomNormalizedVector ()
	{
		return Random.onUnitSphere;
	}

	/// <summary>
	/// Modifie la direction de l'objet.
	/// </summary>
	public void ModifyDirection ()
	{
		// Inverse la direction (donne un effet de ping pong à l'objet).
		randomDirection = -randomDirection;
	}
}

Code : Tout sélectionner

using UnityEngine;

public class SphereContainer : MonoBehaviour
{	
	private void OnTriggerExit (Collider other)
	{
		// Si c'est le joueur.
		if (other.CompareTag ("Player"))
		{
			// Applique l'effet désiré à l'objet.
			PlayerController playerController = other.gameObject.GetComponent<PlayerController> ();
			if (playerController != null)
			{
				playerController.ModifyDirection ();
			}
		}
	}
}

Avatar de l’utilisateur
Kaloverian
Messages : 343
Inscription : 10 Août 2016 03:03

Re: problème avec Destroy(GameObject)

Message par Kaloverian » 13 Nov 2021 20:36

Pourquoi,d'après ce cript,l'objet kaka n'est-il pas détruit grâce à la condition?

Code : Tout sélectionner

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

public class essai1 : MonoBehaviour
{
    public GameObject koko,kaka;

    public float moveSpeed,A;

    float dist;

    
    
    // Start is called before the first frame update
    void Start()
    {
        dist=Vector3.Distance(koko.transform.position,kaka.transform.position);
    }

    // Update is called once per frame
    void Update()
    {
        if(dist>A)
         {
            Destroy(kaka);
         }

         koko.transform.Translate (Vector3.up* moveSpeed * Time.deltaTime);
    }
}


Répondre

Revenir vers « (C#) CSharp »