1
votes

Unity Editor: vérifiez si la méthode OnDestroy () est appelée car la scène se ferme

Il y a 2 cas où OnDestroy () est déclenché dans l'éditeur (la classe doit être marquée avec l'attribut [ExecuteInEditMode] ):

  1. Lorsque la scène se ferme et que tous les objets sont détruits.
  2. Lorsqu'un objet est supprimé de la scène.

Est-il possible de différencier ces deux cas dans OnDestroy () ? J'ai essayé de m'abonner à EditorSceneManager.sceneClosing pour définir le drapeau, puis de l'utiliser dans OnDestroy () mais cet événement n'a pas fonctionné. Y a-t-il d'autres options?


8 commentaires

Est-ce en mode lecture? Unity.Editor.EditorSceneManager est pour l'éditeur


Pourquoi en avez-vous besoin, peut-être existe-t-il une approche différente?


J'ai des waypoints, chacun peut avoir plusieurs connexions avec les autres. Lorsqu'un waypoint est supprimé de la scène, toutes les connexions avec celui-ci (à partir d'autres waypoints) doivent également être supprimées. Le seul moyen de savoir qu'un objet est supprimé, je suppose, est OnDestroy . Mais il pourrait également être déclenché lorsque la scène se ferme, auquel cas cela ne signifie pas "supprimer un objet" et aucune connexion ne doit être supprimée.


En fait, j'ai déjà trouvé un moyen différent: les connexions sont vérifiées chaque fois qu'un nouveau waypoint est ajouté, mais cette logique semble maladroite et cela conduit à des calculs redondants chaque fois qu'un nouvel objet est créé ou que certaines de ses propriétés sont modifiées.


J'aurais peut-être trouvé un moyen d'utiliser EditorApplication.hierarchyChanged . Si vous me dites comment vous utilisez vos waypoints (sont-ils tous des objets racine ou des enfants d'un objet de jeu spécifique, avez-vous peut-être même un manager pour eux? Comment stockez-vous les connexions (liste?)) Je pourrais peut-être venir avec une solution.


@oui c'étaient des objets racine mais maintenant ils sont tous placés dans un objet enraciné nommé "_WAYPOINTS" qui est créé automatiquement lorsqu'un nouveau waypoint est ajouté à la scène. J'ai eu quelques réflexions à m'appuyer sur cet objet pour comprendre si la carte est déchargée ou non en utilisant le fait qu'un parent est détruit avant ses enfants mais cela ne semblait pas possible puisque cet objet pouvait également être supprimé manuellement.


Je n'utilise pas de gestionnaire pour eux, un préfabriqué de waypoint peut être ajouté à la scène ou un waypoint existant peut être dupliqué sur la scène et la classe Waypoint a un champ statique SortedDictionary _allWaypoints pour conserver tous les waypoints existants. Oui, ils ont List . Quand on est ajouté à la scène, il est automatiquement connecté à la précédente mais il est également possible de changer ces connexions (liste) via l'Inspecteur. Lorsqu'il est modifié, OnValidate est appelé là où cette liste est vérifiée pour les objets vides qui sont supprimés.


Maintenant j'ai fait l'optimisation: je vérifie si OnDestroy () est appelé pour un waypoint mais il n'a pas été déclenché pour le conteneur parent, alors cet événement est considéré comme la suppression d'un waypoint de la scène. Mais comme je l'ai dit, ce n'est pas fiable à 100% car un waypoint peut toujours être déplacé du parent vers la racine ou tout autre objet.


3 Réponses :


0
votes

2 commentaires

@yes EditorSceneManager hérite de SceneManager . Ne serait-il pas correct d'utiliser cet événement?


Il semble qu'aucun des deux gestionnaires ne soit appelé dans l'éditeur si vous ne chargez pas la scène à partir d'un script d'éditeur (et que vous vous abonnez à partir de là). Le simple fait d'avoir un gameobject avec un composant avec [ExecuteInEditMode] dans la hiérarchie ne semble pas le couper. Ou peut-être que c'est juste moi qui le fais mal, ce qui est totalement une option ^^



2
votes

J'ai fait un hackjob hacky, il y a beaucoup de place à l'amélioration en fonction de la façon dont vos waypoints sont exactement disposés et se connaissent. Si une connexion est toujours mutuelle (signifie que le waypoint A est connecté à B et B est connecté à A), vous pouvez ajouter les connexions du waypoint supprimé dans la méthode OnDestroy () , ce qui vous permettrait uniquement vérifier les waypoints nécessaires ^^

using UnityEngine;

[ExecuteInEditMode]
public class Waypoint : MonoBehaviour
{
    public static readonly SortedDictionary<int, Waypoint> AllWaypoints = new SortedDictionary<int, Waypoint>(); // The relevance of the list is controlled by the logic of the `Waypoint` class.
    public static bool NeedsCleanUp = false;
    public List<Waypoint> ConnectedWaypoints = new List<Waypoint>();

    private void OnDestroy()
    {
        if (!IsEditor)
            return;

        NeedsCleanUp = true;
        // Other logic...
    }

    private bool IsEditor
    {
        get { return !Application.isPlaying; }
    }
}

Ci-dessus fonctionne avec ça.

#if UNITY_EDITOR
using UnityEngine;
using UnityEngine.SceneManagement;

using UnityEditor;
using UnityEditor.SceneManagement;

[InitializeOnLoad]
public static class WaypointCleanup
{
    static Scene lastActiveScene;

    static WaypointCleanup() {
        lastActiveScene = EditorSceneManager.GetActiveScene();
        EditorApplication.hierarchyChanged += CheckAllWaypointsForNullConnections;
    }

    static void CheckAllWaypointsForNullConnections() {
        if(lastActiveScene.Equals(EditorSceneManager.GetActiveScene())) 
        {
            //Here be room for improvement, but if you want to check all
            //waypoints that could be child of everything, the only other way
            //I can currently think of would be to somehow register which
            //waypoints have to be checked from the OnDestroy method of the
            //waypoint class.
            if (Waypoint.NeedsCleanUp)
            {
                foreach (var waypoint in Waypoint.AllWaypoints.Values) 
                {
                    waypoint.ConnectedWaypoints.RemoveAll(item => !item);
                }
            }
        }
        else 
        {
            lastActiveScene = EditorSceneManager.GetActiveScene();
        }

        Waypoint.NeedsCleanUp = false;
    }
}
#endif

J'espère que vous pourrez en faire quelque chose d'utile. Cela ne sert qu'à titre d'exemple pour savoir où vous pouvez vous connecter, donc utilisez avec précaution, actuellement cela passe par tous les points de cheminement et vérifie toutes les connexions par rapport à null.

Modifier: Ce qui précède semble fonctionner dans n'importe quel dossier, j'ai oublié de le mettre dans un dossier spécial de l'éditeur.


5 commentaires

Je pense que cela fonctionnera, merci! Je demanderais à tous ceux qui liront ceci de voter pour ce problème car si l'événement EditorSceneManager.sceneClosing fonctionnait, la logique de la solution serait beaucoup plus simple.


Cependant, les rappels ne sont pas complètement interrompus, si vous ouvrez / fermez / créez une scène directement à partir du SceneManager, ils sont effectivement appelés. C'est un peu incohérent même si je suis d'accord. Je lui ai donné mon vote, mais étant donné ses seulement 6 votes maintenant, je doute que ce soit réglé bientôt ^^


Non, cela fonctionne également dans l'éditeur, mais uniquement si vous utilisez EditorSceneManager pour ouvrir / fermer des scènes. Si vous changez simplement de scène en double-cliquant, rien ne se passe.


J'ai modifié votre code, il attend d'être examiné.


Idk si c'était vraiment nécessaire, je pense que cela véhiculait l'idée générale avant aussi, mais ok. Je n'ai édité que dans le champ statique Needs CleanUp puisque vous l'avez oublié ^^



2
votes

Une question un peu ancienne, mais c'est le premier résultat dans Google, alors j'ai pensé que je pourrais partager mon approche.

void OnDestroy()
{
    if(gameObject.scene.isLoaded) //Was Deleted
    {

    }
    else //Was Cleaned Up on Scene Closure
    {

    }
}


1 commentaires

Fonctionne parfaitement! Cela devrait être dans la documentation pour OnDestroy