[DB] communication entre scripts

Questions à propos du scripting. Hors Shader, GUI, Audio et Mobile.
Avatar de l’utilisateur
jmhoubre
Messages : 856
Inscription : 05 Oct 2019 22:05

[DB] communication entre scripts

Message par jmhoubre » 11 Mars 2020 20:19

Bonsoir,

quand j'ai besoin d'appeler un event situé dans un autre script, j'utilise habituellement la classe et le nom de l'event :

Code : Tout sélectionner

// Script Publisher.
public class PlayerCollision : MonoBehaviour {
    public static event System.Action OnPlayerDeath;

    private void OnTriggerEnter2D (Collider2D collision) {
        if (collision.CompareTag ("Mine")) {    
            OnPlayerDeath?.Invoke ();
            Destroy (gameObject);
        }
    }
}

// Script Listener.
private void Start () {
        PlayerCollision ().OnPlayerDeath += OnGameOver;
}
Je vois aussi souvent ce genre d'abonnement ::

Code : Tout sélectionner

// Autre script Listener.
private void Start () {
        FindObjectOfType<PlayerCollision> ().OnPlayerDeath += OnGameOver;
}
Je n'arrive pas à voir la différence entre les deux. Une forme est-elle plus efficace ou plus rapide que l'autre ?
Merci d'avance.

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

Re: [DB] communication entre scripts

Message par jmhoubre » 15 Mars 2020 11:47

Personne n'a d'avis ?

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

Re: [DB] communication entre scripts

Message par Max » 15 Mars 2020 12:08

Salut,

personnellement je n'utilise pas les events pour ce genre de chose. Donc je n'ai pas vraiment d'avis sur la question.
Ceci dit, en terme de perf, je ne vois pas en quoi une éventuelle différence de perf entre les deux types de définitions soit vraiment importante, à partir du moment où c'est effectué dans la phase d'initialisation/démarrage de l'appli (void Start()).
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 : 856
Inscription : 05 Oct 2019 22:05

Re: [DB] communication entre scripts

Message par jmhoubre » 16 Mars 2020 11:41

Bonjour Max,

c'est assez vrai en général, mais si tu as 5000 objets à instancier (par exemple) ? Et je suis d'un naturel curieux. Je vais faire un test dans la journée, quelle est la meilleure façon de mesurer le temps d'exécution ? Un Time.time au début et à la fin est-il suffisant ? Efficace ?

Merci

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

Re: [DB] communication entre scripts

Message par Max » 16 Mars 2020 12:54

Hello,

je pense que cela doit se jouer à un poil de cul de cloporte. Mais sur plusieurs milliers d'instanciations, alors quel serait l'impact ? Comme tu le suggères, le mieux reste de faire des tests.
jmhoubre a écrit :
16 Mars 2020 11:41
Un Time.time au début et à la fin est-il suffisant ? Efficace ?
Perso, pour ce genre de chose, je préfère des lib non liées à l'API d'Unity. Par exemple passer par System.DateTime ou encore System.Diagnostics; avec Stopwatch().
Un lien qui illustres certaines utilisations: https://stackoverflow.com/questions/135 ... ds-c-sharp
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 : 856
Inscription : 05 Oct 2019 22:05

Re: [DB] communication entre scripts

Message par jmhoubre » 16 Mars 2020 23:48

Bonsoir
j'ai donc effectué quelques essais. Voici quelques éléments, même si j'ai besoin que quelqu'un confirme ou infirme mes conclusions.

Utilisation de StopWatch
Elle est très simple. StopWatch est en gros un chrono, qui utilise quand il le peut l'horloge matérielle, plus précise que l'horloge système (voir la documentation Microsoft).

Code : Tout sélectionner

using System.Diagnostics; // Librairie .Net

private Stopwatch mainChrono;

private void Awake () {
        // Création chrono.
        private mainChrono = new Stopwatch ();
        // Lancement chrono.
        private mainChrono.Start ();
}

private void Start () {
        print ("Durée intermédiare : " + mainChrono.ElapsedMilliseconds.ToString ("0.0") + " millisecondes");

        // Arrêt du chrono.
        mainChrono.Stop ();
        print ("Durée totale : " + mainChrono.ElapsedMilliseconds.ToString ("0.0") + " millisecondes");
}
Particularité des 2 méthodes
La méthode d'appel direct a besoin d'un event static, au contraire de la méthode avec FindObjectOfType<Test> ().

Description de la procédure de test.
L'objet du test est d'instancier 5000 instances d'un prefab. Le prefab est un cube sans collider et muni de ce script, duquel j'ai ôté le superflu. On bascule entre les méthodes en commentant / dé-commentant (initialement, la méthode Update faisait avancer le cube).

Code : Tout sélectionner

using UnityEngine;

public class Cube : MonoBehaviour {
    private float m_Speed = 5f;

    private void Start () {
        // Méthode 1.
        Test.OnAccelerateOrder += AccelerateOrder;
        // Méthode 2.
        // FindObjectOfType<Test> ().OnAccelerateOrder += AccelerateOrder;
    }

    public void AccelerateOrder (float _speedfactor) => m_Speed *= _speedfactor;

    private void OnDisable () {
        Test.OnAccelerateOrder -= AccelerateOrder; 
        //FindObjectOfType<Test> ().OnAccelerateOrder -= AccelerateOrder;
    }
}
Un empty porte le script de test :

Code : Tout sélectionner

using System;
using System.Diagnostics;
using UnityEngine;

public class Test : MonoBehaviour {

    public static event Action<float> OnAccelerateOrder; // Enlever static pour la méthode 2.

    [SerializeField] private GameObject m_Object;  // Référence sur le prefab du cube.

    private const int NUM_OF_OBJECTS = 5000; 
    private GameObject [] m_GameObjects; // Tableau des cubes instanciés.
    private bool isPrinted; // Evite d'arrêter le chrono à chaque update.

    private Stopwatch mainChrono;

    private void Awake () {
        // Création chrono.
        mainChrono = new Stopwatch ();
        // Lancement chrono.
        mainChrono.Start ();
    }

    private void Start () {
        // Suivi des objets.
        m_GameObjects = new GameObject [NUM_OF_OBJECTS];

        // Lancement du test.
        CreateCubes ();
        // Durée de l'instanciation des 5000 cubes.
        print ("Durée intermédiaire : " + mainChrono.ElapsedMilliseconds.ToString ("0.0") + " millisecondes");
    }

    private void Update () {
        if (!isPrinted) {
            mainChrono.Stop ();
            isPrinted = true;
            print ("Durée totale : " + mainChrono.ElapsedMilliseconds.ToString ("0.0") + " millisecondes");
        }
    }

    private void CreateCubes () {
        // Instantiation des objets.
        for (int i = 0; i < NUM_OF_OBJECTS; i++) {
            GameObject go = Instantiate (m_Object, new Vector3 (
            	UnityEngine.Random.Range (-50f, 50f), 0f, UnityEngine.Random.Range (-10f, 10f)), 
            	Quaternion.identity, gameObject.transform);
            go.name = "Cube" + i.ToString ("00000");
            m_GameObjects [i] = go;
        }
    }
}
Résultats
Pour les 5000 objets, voici les moyennes en millisecondes de ce que j'ai mesuré sur mon PC (sachant que j'ai placé un 2ème chrono et pris d'autres résultats intermédiaires qui ne sont plus dans le script pour simplifier (méthode 1 // méthode 2).
Durée de l'instanciation des cubes : 185.7 // 187.5
Durée totale : 397 // 3836

Interprétation
La grosse différence de temps est due selon moi (mais je peux me tromper) à la fonction Start des cubes, qui effectue l'abonnement des cubes à l'événement OnAccelerateOrder (cet événement déclenche le changement de vitesse des 5000 cubes, qui est très rapide). En effet, j'avais un temps intermédiaire à la fin du Start du script de test qui donnait 264 ms et 260 ms, et je pense que tous les Start des cubes s'effectuent après le Start de test. La méthode 1 met donc 133 ms pour faire les Start, contre 3439 ms pour la méthode 2. Et c'est pire avec 10000 cubes, où la méthode 2 met 16600 ms pour faire ses 10000 Start (La documentation Unity sur FindObjectOfType précise d'ailleurs que "Please note that this function is very slow." : l'utiliser dans une fonction Start, pourquoi pas, mais sur des milliers d'objets, ce n'est pas raisonnable.

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

Re: [DB] communication entre scripts

Message par Max » 18 Mars 2020 08:47

une preuve supplémentaire (si besoin il en était) que les Finds ... c'est le mal :diable:
:hehe:
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

Répondre

Revenir vers « Scripting »