En développant un jeu dans Unity, je fais un usage libéral de [RequireComponent(typeof(%ComponentType%))]
pour assurer que les composants ont toutes les dépendances respectées.
J'implémente maintenant un système de tutoriel qui met en évidence divers objets d'interface utilisateur. Pour faire la mise en surbrillance, je prends une référence au GameObject
dans la scène, puis je le clone avec Instantiate (), puis je supprime récursivement tous les composants qui ne sont pas nécessaires pour l'affichage. Le problème est qu'avec l'utilisation libérale de RequireComponent
dans ces composants, beaucoup ne peuvent pas être simplement supprimés dans n'importe quel ordre, car ils dépendent d'autres composants, qui n'ont pas encore été supprimés.
Ma question est la suivante: existe-t-il un moyen de déterminer si un Component
peut être supprimé du fichier auquel GameObject
il est actuellement attaché?
Le code que je publie fonctionne techniquement, mais il génère des erreurs Unity (pas pris dans le bloc try-catch, comme on le voit dans l'image
Voici le code:
public void StripFunctionality(RectTransform rt)
{
if (rt == null)
{
return;
}
int componentCount = rt.gameObject.GetComponents<Component>().Length;
int safety = 10;
int allowedComponents = 0;
while (componentCount > allowedComponents && safety > 0)
{
Debug.Log("ITERATION ON "+rt.gameObject.name);
safety--;
allowedComponents = 0;
foreach (Component c in rt.gameObject.GetComponents<Component>())
{
//Disable clicking on graphics
if (c is Graphic)
{
((Graphic)c).raycastTarget = false;
}
//Remove components we don't want
if (
!(c is Image) &&
!(c is TextMeshProUGUI) &&
!(c is RectTransform) &&
!(c is CanvasRenderer)
)
{
try
{
DestroyImmediate(c);
}
catch
{
//NoOp
}
}
else
{
allowedComponents++;
}
}
componentCount = rt.gameObject.GetComponents<Component>().Length;
}
//Recursive call to children
foreach (RectTransform childRT in rt)
{
StripFunctionality(childRT);
}
}
Ainsi, la façon dont ce code a généré les trois dernières lignes du journal de débogage ci-dessus est la suivante: il a pris comme entrée a GameObject
avec deux composants: Button
et PopupOpener
. PopupOpener
requiert qu'un Button
composant soit présent dans le même GameObject avec:
[RequireComponent(typeof(Button))]
public class PopupOpener : MonoBehaviour
{
...
}
La première itération de la boucle while (indiquée par le texte "ITERATION ON Button Invite (clone)") a tenté de supprimer la Button
première, mais n'a pas pu car elle dépend du PopupOpener
composant. cela a jeté une erreur. Ensuite, il a tenté de supprimer le PopupOpener
composant, ce qu'il a fait, car il ne dépend d'aucune autre chose. Dans l'itération suivante (désignée par le deuxième texte "ITERATION ON Button Invite (clone)"), il a tenté de supprimer le Button
composant restant , ce qu'il pouvait maintenant faire, car il PopupOpener
avait été supprimé lors de la première itération (après l'erreur).
Ma question est donc de savoir s'il est possible de vérifier à l'avance si un composant spécifié peut être supprimé de son courant GameObject
, sans invoquer Destroy()
ou DestroyImmediate()
. Je vous remercie.
Destroy
supprime le composant à la fin du cadre (par opposition àImmediate
ly).DestroyImmediate
est considéré comme un mauvais style de toute façon, mais je pense que le passage àDestroy
peut effectivement éliminer ces erreurs.Réponses:
C'est certainement possible, mais cela demande beaucoup de travail: vous pouvez vérifier s'il y a un
Component
attaché auGameObject
qui a unAttribute
typeRequireComponent
qui a un de sesm_Type#
champs attribuable au type de composant que vous êtes sur le point de supprimer. Le tout enveloppé dans une méthode d'extension:après avoir déposé
GameObjectExtensions
n'importe où dans le projet (ou en faire une méthode régulière sur le script), vous pouvez vérifier si un composant peut être supprimé, par exemple:Cependant, il peut y avoir d'autres moyens de créer un objet GUI non interactif - par exemple, le rendre en texture, le superposer avec un panneau presque transparent qui interceptera les clics ou désactivera (au lieu de détruire) tous les
Component
s dérivant deMonoBehaviour
ouIPointerClickHandler
.la source
catch
. Journal, continue) plutôt que de prévenir les pannes (c.-à-d.if
Possibles, alors). Ce n'est cependant pas beaucoup de style C # (comme celui de cette réponse). En fin de compte, votre code d'origine aurait pu être plus proche de la façon dont les choses sont généralement faites ... et les meilleures pratiques sont une question de préférence personnelle.Au lieu de supprimer les composants du clone, vous pouvez simplement les désactiver en les paramétrant
.enabled = false
. Cela ne déclenchera pas les vérifications de dépendance, car un composant n'a pas besoin d'être activé pour remplir la dépendance.Update
méthode.la source