Rozwijając grę w Unity, korzystam z niej [RequireComponent(typeof(%ComponentType%))]
swobodnie, aby upewnić się, że wszystkie komponenty spełniają wszystkie zależności.
Wdrażam teraz system samouczka, który wyróżnia różne obiekty interfejsu użytkownika. Aby podświetlić, odnoszę się do GameObject
sceny, następnie klonuję ją za pomocą Instantiate (), a następnie rekurencyjnie usuwam wszystkie elementy, które nie są potrzebne do wyświetlania. Problem polega na tym, że przy swobodnym stosowaniu RequireComponent
w tych komponentach wielu nie można po prostu usunąć w dowolnej kolejności, ponieważ zależą one od innych komponentów, które nie zostały jeszcze usunięte.
Moje pytanie brzmi: czy istnieje sposób, aby ustalić, czy Component
można usunąć z GameObject
niego, do którego jest on aktualnie podłączony.
Kod, który publikuję technicznie działa, jednak generuje błędy Unity (nie wychwycone w bloku try-catch, jak widać na obrazku
Oto kod:
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);
}
}
Tak więc sposób, w jaki ten kod wygenerował trzy ostatnie wiersze dziennika debugowania powyżej, wygląda następująco: Pobrano jako dane wejściowe GameObject
z dwoma składnikami: Button
i PopupOpener
. PopupOpener
Requies że Button
składnik ten występuje w tym samym GameObject'u z:
[RequireComponent(typeof(Button))]
public class PopupOpener : MonoBehaviour
{
...
}
Pierwsza iteracja pętli while (oznaczona tekstem „ITERATION ON Button Invite (clone)”) usiłowała usunąć Button
pierwszą, ale nie mogła, ponieważ zależy od PopupOpener
komponentu. to rzuciło błąd. Następnie próbował usunąć PopupOpener
komponent, co zrobił, ponieważ nie zależy od niego nic innego. W następnej iteracji (oznaczonej drugim tekstem „ITERATION ON Button Invite (clone)”) próbował usunąć pozostały Button
komponent, co teraz mógł zrobić, ponieważ PopupOpener
został usunięty w pierwszej iteracji (po błędzie).
Moje pytanie brzmi więc, czy można z wyprzedzeniem sprawdzić, czy określony składnik można usunąć z bieżącego GameObject
, bez wywoływania Destroy()
lub DestroyImmediate()
. Dziękuję Ci.
Destroy
usuwa komponent na końcu ramki (w przeciwieństwie doImmediate
ly).DestroyImmediate
i tak jest uważany za zły styl, ale myślę, że przejście naDestroy
może faktycznie wyeliminować te błędy.Odpowiedzi:
Jest to zdecydowanie możliwe, ale wymaga sporo pracy nóg: możesz sprawdzić, czy istnieje
Component
przyłączenie do tego,GameObject
które maAttribute
typ,RequireComponent
który ma jedno zm_Type#
pól przypisanych do typu komponentu, który chcesz usunąć. Wszystko zapakowane w metodę rozszerzenia:po upuszczeniu w
GameObjectExtensions
dowolnym miejscu projektu (lub alternatywnie ustaw go jako zwykłą metodę w skrypcie), możesz sprawdzić, czy składnik można usunąć, np .:Mogą jednak istnieć inne sposoby tworzenia nieinteraktywnego obiektu GUI - na przykład renderowanie go do tekstury, nakładanie go na prawie przezroczysty panel, który będzie przechwytywał kliknięcia lub wyłączał (zamiast niszczyć) wszystkie
Component
pochodzące zMonoBehaviour
lubIPointerClickHandler
.źródło
catch
Loguj, kontynuuj), a nie zapobiega awariom (tzn.if
Wtedy możliwe). Nie jest to jednak styl C # (taki jak ten w tej odpowiedzi). Ostatecznie twój oryginalny kod mógł być bliżej tego, jak zwykle się to robi ... a najlepsza praktyka jest kwestią osobistych preferencji.Zamiast usuwać komponenty z klonu, możesz je po prostu wyłączyć, ustawiając
.enabled = false
je. Nie spowoduje to sprawdzenia zależności, ponieważ komponent nie musi być włączony, aby spełnić zależność.Update
metody.źródło