Il y a 2 cas où OnDestroy ()
est déclenché dans l'éditeur (la classe doit être marquée avec l'attribut [ExecuteInEditMode]
):
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?
3 Réponses :
@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 ^^
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.
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é ^^
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 { } }
Fonctionne parfaitement! Cela devrait être dans la documentation pour OnDestroy
Est-ce en mode lecture?
Unity.Editor.EditorSceneManager
est pour l'éditeurPourquoi 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 statiqueSortedDictionary _allWaypoints
pour conserver tous les waypoints existants. Oui, ils ontList
. 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.