Ich entwickle ein Spiel in Unity und nutze es liberal, [RequireComponent(typeof(%ComponentType%))]
um sicherzustellen, dass die Komponenten alle Abhängigkeiten erfüllen.
Ich implementiere jetzt ein Tutorial-System, das verschiedene UI-Objekte hervorhebt. Um die Hervorhebung durchzuführen, verweise ich auf die GameObject
in der Szene, klone sie dann mit Instantiate () und entferne rekursiv alle Komponenten, die für die Anzeige nicht benötigt werden. Das Problem ist, dass bei der liberalen Verwendung RequireComponent
dieser Komponenten viele nicht einfach in beliebiger Reihenfolge entfernt werden können, da sie von anderen Komponenten abhängen, die noch nicht gelöscht wurden.
Meine Frage ist: Gibt es eine Möglichkeit festzustellen, ob ein Component
aus dem GameObject
aktuell angehängten entfernt werden kann?
Der Code, den ich veröffentliche, funktioniert technisch, löst jedoch Unity-Fehler aus (nicht im Try-Catch-Block abgefangen, wie im Bild dargestellt)
Hier ist der 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);
}
}
Die Art und Weise, wie dieser Code die letzten drei Zeilen des obigen Debug-Protokolls generiert hat, ist die folgende: Als Eingabe wurde a GameObject
mit zwei Komponenten verwendet: Button
und PopupOpener
. PopupOpener
erfordert, dass eine Button
Komponente in demselben GameObject vorhanden ist mit:
[RequireComponent(typeof(Button))]
public class PopupOpener : MonoBehaviour
{
...
}
Bei der ersten Iteration der while-Schleife (gekennzeichnet durch den Text "ITERATION ON Button Invite (Klon)") wurde versucht, die Button
erste zu löschen , dies konnte jedoch nicht, da dies von der PopupOpener
Komponente abhängt . Dies warf einen Fehler. Dann wurde versucht, die PopupOpener
Komponente zu löschen , was auch der Fall war, da sie von nichts anderem abhängig ist. In der nächsten Iteration (gekennzeichnet durch den zweiten Text "ITERATION ON Button Invite (Klon)") wurde versucht, die verbleibende Button
Komponente zu löschen , was jetzt möglich war, da PopupOpener
sie in der ersten Iteration (nach dem Fehler) gelöscht wurde.
Meine Frage ist also, ob es möglich ist, im Voraus zu prüfen, ob eine bestimmte Komponente aus ihrer aktuellen entfernt werden kann GameObject
, ohne Destroy()
oder aufzurufen DestroyImmediate()
. Vielen Dank.
Destroy
entferne aber die Komponente am Ende des Rahmens (im Gegensatz zuImmediate
ly).DestroyImmediate
wird sowieso als schlechter Stil angesehen, aber ich denke, ein Wechsel zuDestroy
kann diese Fehler tatsächlich beseitigen.Antworten:
Es ist definitiv möglich, aber es erfordert ziemlich viel Beinarbeit: Sie können überprüfen, ob
Component
an das angehängt ist,GameObject
das einenAttribute
TypRequireComponent
hat, dessenm_Type#
Felder dem Komponententyp zugeordnet werden können, den Sie entfernen möchten. Alles in einer Erweiterungsmethode verpackt:Nachdem Sie
GameObjectExtensions
irgendwo im Projekt abgelegt haben (oder es alternativ zu einer regulären Methode im Skript gemacht haben), können Sie überprüfen, ob eine Komponente entfernt werden kann, z.Es kann jedoch auch andere Mittel geben, um ein nicht interaktives GUI-Objekt zu erstellen - beispielsweise das Rendern in eine Textur, das Überlagern mit einem fast transparenten Bedienfeld, das Klicks abfängt oder alle von oder abgeleiteten Objekte deaktiviert (anstatt sie zu zerstören) .
Component
MonoBehaviour
IPointerClickHandler
quelle
catch
protokollieren, fortfahren), anstatt Fehler zu verhindern (dh dannif
möglich). Das ist jedoch nicht viel C # -Stil (wie einer in dieser Antwort). Am Ende war Ihr ursprünglicher Code möglicherweise näher an der typischen Vorgehensweise ... und bewährte Verfahren sind eine Frage der persönlichen Präferenz.Anstatt die Komponenten auf dem Klon zu entfernen, können Sie sie einfach deaktivieren, indem Sie sie festlegen
.enabled = false
. Dadurch werden die Abhängigkeitsprüfungen nicht ausgelöst, da eine Komponente nicht aktiviert werden muss, um die Abhängigkeit zu erfüllen.Update
Methode nicht mehr auf .quelle