📲

【Unity】Componentを移動するPackageを作成しました

2024/07/22に公開

はじめに

コンポーネントを別のオブジェクトに付け替えたくなったのですが、
標準の方法(ドラッグ&ドロップ)だと内部的には再生成されているようで、
被参照箇所が全てMissingとなってしまうため、一つ一つ参照を付け替えていくという不毛な作業が発生してしまったので、こちらを解決するべくエディタ拡張を作成しました。

シンプルな機能ではありますが、今後同じような作業を行う方への手助けとなればと思ってPackageとして公開いたしました!

https://github.com/trs-game-tech/MoveComponent

PackageManagerから下記URLを指定することでインストール可能です

https://github.com/trs-game-tech/MoveComponent.git

また、ライセンスはThe Unlicenseにしているので気軽に導入いただけます。
(クレジット表記不要です)

使い方

移動したいコンポーネントを右クリックすると「MoveComponent...」というメニューが生えているので、そこからエディタウィンドウを起動できます。

あとは移動対象をDestination GameObjectにD&Dして「Move」ボタンを押すだけで完了です。

この操作で該当のコンポーネントを参照していた箇所のシリアライズ値も自動的に置き換わります🤗

Gif

仕組み

コンポーネントの移動はシンプルに「コピーを移動先に追加」で実装

var destinationComponent = Undo.AddComponent(_destinationGameObject, sourceType);
EditorUtility.CopySerialized(_sourceComponent, destinationComponent);

その後にシーンやプレハブ内のオブジェクトからSerializedObjectを使って参照プロパティを取得し、一括置換という処理の流れになっています。

// 対象オブジェクト内のコンポーネント参照を取得する処理
private static void FindReferencesRecursive(in List<SerializedProperty> references, GameObject gameObject, in Component target, in List<Component> components)
{
    gameObject.GetComponents(components);
    foreach (var component in components)
    {
        if (component is Transform)
            continue;

        var serializedObject = new SerializedObject(component);
        var iterator = serializedObject.GetIterator();
        if (!iterator.Next(enterChildren: true))
        {
            serializedObject.Dispose();
            continue;
        }

        var beforeCount = references.Count;
        do
        {
            if (iterator.propertyType == SerializedPropertyType.ObjectReference)
            {
                if (ReferenceEquals(iterator.objectReferenceValue, target))
                {
                    references.Add(iterator.Copy());
                }
            }
        } while (iterator.Next(enterChildren: ShouldEnterChildren(iterator.propertyType)));

        if (beforeCount == references.Count)
            serializedObject.Dispose();
    }

    foreach (Transform transform in gameObject.transform)
    {
        FindReferencesRecursive(references, transform.gameObject, target, components);
    }
}
// 置き換え部分の処理
foreach (var serializedProperty in references)
{
    serializedProperty.objectReferenceValue = destinationComponent;
    serializedProperty.serializedObject.ApplyModifiedProperties();
}

おわりに

コンポーネントの移動は発生することの少ない作業かと思いますが、
実装方法含め何かの参考になれば幸いです🥰

Discussion