ゲームオブジェクトのコンポーネントを破棄できるかどうかを確認する


11

Unityでゲームを開発し、[RequireComponent(typeof(%ComponentType%))]コンポーネントをすべての依存関係に確実に適合させるためにを自由に使用しています。

現在、さまざまなUIオブジェクトを強調表示するチュートリアルシステムを実装しています。強調表示を行うにはGameObject、シーン内のへの参照を取得し、それをInstantiate()で複製してから、表示に不要なすべてのコンポーネントを再帰的に取り除きます。問題はRequireComponent、これらのコンポーネントを自由に使用すると、まだ削除されていない他のコンポーネントに依存しているため、多くのコンポーネントを任意の順序で単純に削除できないことです。

私の質問はComponentGameObject現在アタッチされているからを削除できるかどうかを判断する方法はありますか。

私が投稿しているコードは技術的には機能しますが、Unityエラーをスローします(画像に示すように、try-catchブロックでキャッチされません)

ここに画像の説明を入力してください

これがコードです:

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);
    }
}

上記のデバッグログの最後の3行を生成し、このコードは、次のされている方法は、そう:これは、入力Aとして取ったGameObject二つの成分と:ButtonPopupOpener。コンポーネントが同じPopupOpenerGameObjectに存在することを要求しButtonます:

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

whileループの最初の反復(「ITERATION ON Button Invite(clone)」というテキストで示される)は、最初の反復を削除しようButtonとしましたが、PopupOpenerコンポーネントに依存しているため、削除できませんでした。これによりエラーが発生しました。次に、他のPopupOpenerコンポーネントに依存していないため、コンポーネントを削除しようとしました。次の反復(2番目のテキスト "ITERATION ON Button Invite(clone)"でButton示さPopupOpenerれる)では、最初の反復(エラー後)で削除されたため、現在実行可能な残りのコンポーネントを削除しようとしました。

したがって、私の質問はGameObjectDestroy()またはを呼び出さずに、指定したコンポーネントを現在のコンポーネントから削除できるかどうかを事前に確認できるかどうかDestroyImmediate()です。ありがとうございました。


元の問題について-GUI要素の非インタラクティブなコピー(=ビジュアル)を作成することが目標ですか?
ワンドラ2017年

はい。目標は、同じ位置に同じ方法で拡大縮小された同一の対話不可能なコピーを作成し、その下にある実際のゲームオブジェクトと同じテキストを表示することです。私がこの方法を採用した理由は、UIが後で編集された場合にチュートリアルで変更を加える必要がないためです(スクリーンショットを表示した場合に必要になるでしょう)。
Liam Lime

Unityの経験はありませんが、全部を複製して削除するのではなく、必要な部分だけをコピーできるのではないかと思います。
Zan Lynx

私はそれをテストしていませんDestroyが、フレームの最後にあるコンポーネントを削除します(Immediatelyではなく)。DestroyImmediateとにかく悪いスタイルと見なされますDestroyが、実際にこれらのエラーを排除できるように切り替えると思います
97年

Destroyから始めて(それが適切な方法であるため)両方を試し、次にDestroyImmediateに切り替えて、機能するかどうかを確認しました。Destroyは、指定された順序で実行されるようですが、フレームの最後で同じ例外が発生します。
Liam Lime 2017年

回答:


9

それは間違いなく可能ですが、かなりのレッグワークが必要です。削除しようとしているコンポーネントタイプに割り当て可能なフィールドの1つを持つタイプのにComponent接続されてGameObjectいるがあるかどうかを確認できます。すべて拡張メソッドでラップされています:AttributeRequireComponentm_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));
    }
}

GameObjectExtensionsプロジェクトのどこかにドロップした後(または、スクリプトの通常のメソッドにした後)、コンポーネントを削除できるかどうかを確認できます。例:

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

ただし、非インタラクティブなGUIオブジェクトを作成する他の方法もあります。たとえば、オブジェクトをテクスチャーにレンダリングしたり、ほぼ透明なパネルでオーバーレイしたりして、クリックをインターセプトしたりComponentMonoBehaviourまたはから派生したすべてのを無効化したりします(破棄するのではなく)IPointerClickHandler


ありがとう!Unityに既製のソリューションが同梱されているかどうか疑問に思っていましたが、そうではないと思います:)コードをありがとうございます!
Liam Lime、

@LiamLimeええと、組み込みがないことは本当に確認できませんが、一般的なUnityパラダイムがコードをフォールトトレラント(つまりcatch、ログ、続行)にするので、フォールトを防ぐ(つまり、if可能性がある)ため、そうではありません。ただし、これはそれほど多くのC#スタイルではありません(この回答のようなもの)。結局のところ、元のコードは、物事が通常どのように行われるかにより近いかもしれません...そして、ベストプラクティスは個人的な好みの問題です。
ワンドラ2017年

7

クローン上のコンポーネントを削除する代わりに、それらを設定することで無効にすることができ.enabled = falseます。コンポーネントが依存関係を満たすためにコンポーネントを有効にする必要がないため、これは依存関係チェックをトリガーしません。

  • 動作が無効になっていても、その動作はオブジェクト上にあり、そのプロパティを変更してメソッドを呼び出すこともできますが、エンジンはそのUpdateメソッドを呼び出さなくなります。
  • レンダラーが無効になると、オブジェクトは非表示になります。
  • コライダーが無効になっている場合、コリジョンイベントは発生しません。
  • UIコンポーネントが無効になると、プレーヤーはそれを操作できなくなりますが、レンダラーも非アクティブ化しない限り、引き続きレンダリングされます。

コンポーネントには、有効または無効の概念はありません。ビヘイビアー、コライダー、その他のいくつかのタイプがそうです。したがって、プログラマーはさまざまなサブクラスに対してGetComponentを複数回呼び出す必要があります。
DubiousPusher
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.