Bir oyun nesnesinin bileşeninin yok edilip edilemeyeceğini kontrol edin


11

Unity'de bir oyun geliştirirken, [RequireComponent(typeof(%ComponentType%))]bileşenlerin tüm bağımlılıkların karşılanmasını sağlamak için liberal bir şekilde kullanıyorum .

Şimdi çeşitli kullanıcı arayüzü nesnelerini vurgulayan bir eğitim sistemi uyguluyorum. Vurgulamayı yapmak için GameObject, sahnede bir referans alıyorum , daha sonra Instantiate () ile klonladım ve sonra görüntüleme için gerekli olmayan tüm bileşenleri özyinelemeli olarak ayırıyorum. Sorun şu ki RequireComponent, bu bileşenlerin liberal kullanımı ile , çoğu henüz silinmemiş olan diğer bileşenler tarafından bağımlı oldukları için herhangi bir sırayla kolayca kaldırılamaz.

Sorum şu: a'nın şu anda bağlı Componentolduğundan kaldırılıp kaldırılamayacağını belirlemenin bir yolu var mı GameObject?

Teknik olarak gönderdiğim kod çalışıyor, ancak Unity hataları atıyor (resimde görüldüğü gibi try-catch bloğuna yakalanmadı)

resim açıklamasını buraya girin

İşte 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);
    }
}

Dolayısıyla, bu kodun yukarıdaki hata ayıklama günlüğünün son üç satırını oluşturma şekli şöyledir: GameObjectİki bileşenli girdi a olarak alındı : Buttonve PopupOpener. PopupOpenerbir Buttonbileşenin aşağıdakilerle aynı GameObject'te bulunmasını gerektirir:

[RequireComponent(typeof(Button))]
public class PopupOpener : MonoBehaviour
{
    ...
}

While döngüsünün ilk yinelemesi ("ITERATION ON Button Invite (clone)" metniyle belirtilir) Buttonilkini silmeye çalıştı , ancak PopupOpenerbileşen tarafından bağımlı olduğu için yapılamadı . bu bir hata attı. Daha sonra PopupOpener, başka bir şeye bağlı olmadığından, yaptığı bileşeni silmeye çalıştı . Bir sonraki yinelemede ("ITERATION ON Button Invite (clone)" adlı ikinci metinle belirtilir), ilk yinelemede (hatadan sonra) silindiği Buttoniçin şimdi yapabileceği kalan bileşeni silmeye çalıştı PopupOpener.

Benim sorum belirtilen bir bileşen kendi akımından çıkarılabilir eğer vaktinden kontrol etmek mümkün olup olmadığıdır Yani GameObjectsöz etmeksizin Destroy()veya DestroyImmediate(). Teşekkür ederim.


Orijinal sorun hakkında - GUI öğesinin / öğelerinin etkileşimli olmayan kopyasını (= görsel) oluşturmak mı?
wondra

Evet. Amaç, aynı pozisyonda aynı şekilde ölçeklendirilmiş, altındaki gerçek gameobject ile aynı metni göstererek özdeş, etkileşimsiz bir kopya oluşturmaktır. Bu yöntemle gitmemin nedeni, kullanıcı arayüzü daha sonraki bir noktada düzenlenmişse öğreticinin modifikasyonlara ihtiyaç duymamasıdır (ekran görüntülerini gösterirsem gerçekleşmesi gerekir).
Liam Lime

Unity ile deneyimim yok ama her şeyi klonlamak ve bir şeyleri kaldırmak yerine sadece ihtiyacınız olan parçaları kopyalayabilir misiniz?
Zan Lynx

Test etmedim, ancak Destroyçerçevenin sonunda bileşeni kaldırır (aksine Immediate). DestroyImmediateyine de kötü stil olarak kabul edilir, ancak geçiş yapmak Destroyaslında bu hataları ortadan kaldırabilir düşünüyorum .
CAD97

Her ikisini de denedim, Destroy ile başlayarak (bu doğru yol olduğu için) ve daha sonra işe yarayıp yaramayacağını görmek için DestroyImmediate'e geçtim. Yok etme hala çerçevenin sonunda aynı istisnalara neden olan belirtilen sıraya göre gidiyor gibi görünüyor.
Liam Lime

Yanıtlar:


9

Kesinlikle mümkündür, ancak oldukça fazla ayak işi gerektirir: Kaldırmak üzere olduğunuz bileşen türüne atanabilir alanlarından birine sahip olan bir türün Componentekli olup olmadığını kontrol edebilirsiniz . Tümü bir uzantı yöntemiyle sarılır:GameObjectAttributeRequireComponentm_Type#

static class GameObjectExtensions
{
    private static bool Requires(Type obj, Type requirement)
    {
        //also check for m_Type1 and m_Type2 if required
        return Attribute.IsDefined(obj, typeof(RequireComponent)) &&
               Attribute.GetCustomAttributes(obj, typeof(RequireComponent)).OfType<RequireComponent>()
               .Any(rc => rc.m_Type0.IsAssignableFrom(requirement));
    }

    internal static bool CanDestroy(this GameObject go, Type t)
    {
        return !go.GetComponents<Component>().Any(c => Requires(c.GetType(), t));
    }
}

GameObjectExtensionsprojenin herhangi bir yerine bırakıldıktan (veya alternatif olarak komut dosyasında normal bir yöntem haline getirdikten) sonra , bir bileşenin kaldırılıp kaldırılamayacağını kontrol edebilirsiniz, örneğin:

rt.gameObject.CanDestroy(c.getType());

Örneğin tıklama müdahale edecek olan neredeyse şeffaf bir panel ile kaplanacağı ya da doku için işleme - Ancak orada un etkileşimli GUI nesne yapmak için başka araçlar olabilir devre dışı bırakma Tüm (yerine yok etme) Componenttüreyen s MonoBehaviourya da IPointerClickHandler.


Teşekkürler! Hazır bir çözümün Unity ile birlikte gelip gelmediğini merak ediyordum, ama sanmıyorum :) Kodunuz için teşekkür ederim!
Liam Lime

@LiamLime Aslında hiçbir yerleşik yoktur teyit edemiyorum ama tipik Unity paradigması catchhataları önlemek yerine (yani if, o zaman) kod hataya dayanıklı (yani , log, devam) yapmak olası değildir. Bununla birlikte, çok fazla C # stili yoktur (bu cevaptaki gibi). Sonunda orijinal kodunuz işlerin tipik olarak nasıl yapıldığına daha yakın olabilirdi ... ve en iyi uygulama kişisel tercih meselesidir.
wondra

7

Klondaki bileşenleri kaldırmak yerine, bunları ayarlayarak devre dışı bırakabilirsiniz .enabled = false. Bu, bağımlılık kontrollerini tetiklemez, çünkü bağımlılığın yerine getirilmesi için bir bileşenin etkinleştirilmesi gerekmez.

  • Bir Davranış devre dışı bırakıldığında, yine de nesnenin üzerinde olacaktır ve hatta özelliklerini değiştirebilir ve yöntemlerini çağırabilirsiniz, ancak motor artık Updateyöntemini çağırmaz .
  • Bir İşleyici devre dışı bırakıldığında, nesne görünmez olur.
  • Bir Çarpıştırıcı devre dışı bırakıldığında, herhangi bir çarpışma olayının gerçekleşmesine neden olmaz.
  • Bir UI bileşeni devre dışı bırakıldığında, oynatıcı artık onunla etkileşime giremez, ancak oluşturucusunu da devre dışı bırakmazsanız yine de oluşturulur.

Bileşenlerin etkin veya devre dışı olduğu fikri yoktur. Davranışlar, Çarpıştırıcılar ve diğer bazı türler yapar. Bu nedenle programcının çeşitli alt sınıflar için GetComponent'e birden fazla çağrı yapması gerekir.
DubiousPusher
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.