Kakoのいろいろやったこと記

主にUnity関連でやったことをかいていきます

Editor拡張で横着のススメ -Prefabの複数置換編- 紹介

はじめに

この記事は、Unity Advent Calendar 2020、21日目の記事です

qiita.com

今回の記事は、紹介です。解説は別途。

Prefabを一括で置き換えたいんじゃい!!

こんなことを考えたことはありませんか?

  • すでに設置したPrefabを置き換えたい
  • ステージ作ったけど、いくつか障害物にしたい

などなど、色んなシーンで使えそうなEditor拡張を書いてみました。

f:id:Kakovail:20201219164651g:plain

前提

他にもやりたいことがたくさんある場合、こちらのAssetがめちゃくちゃ便利です。
assetstore.unity.com

作ったもの

github.com

// Copyright (c) 2021 Hiroyuki Kako
// This software is released under the MIT License, see LICENSE.

#if UNITY_EDITOR
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;

namespace ObjectReplacer
{
    public class ObjectReplacer : EditorWindow
    {
        [MenuItem("KEditorExtensions/ObjectReplacer")]
        private static void Open()
        {
            GetWindow<ObjectReplacer>("Replace object in scene.");
        }

        static GameObject expectedObject;

        public GameObject[] targetObjectsInEditor;
        public GameObject expectedObjectInEditor;

        private static List<GameObject> _cachedObjects = new List<GameObject>();

        [MenuItem("GameObject/KEditorExtensions/ObjectReplacer/DO_REPLACE", false, 20)]
        public static void DO_REPLACE()
        {
            var tmp = Selection.gameObjects;

            foreach (var obj in tmp)
            {
                if (!_cachedObjects.Contains(obj))
                {
                    _cachedObjects.Clear();
                    _cachedObjects = new List<GameObject>(tmp);

                    ReplaceFunction();

                    break;
                }
            }
        }

        private void OnGUI()
        {
            EditorGUILayout.BeginHorizontal();
            EditorGUILayout.LabelField("Object of destinations to convert.");
            expectedObjectInEditor =
                (GameObject) EditorGUILayout.ObjectField(expectedObjectInEditor, typeof(GameObject), true);
            expectedObject = expectedObjectInEditor;
            EditorGUILayout.EndHorizontal();

            ScriptableObject target = this;

            SerializedObject so = new SerializedObject(target);

            SerializedProperty stringsProperty = so.FindProperty("targetObjectsInEditor");

            EditorGUILayout.PropertyField(stringsProperty, true);

            so.ApplyModifiedProperties();


            EditorGUILayout.BeginHorizontal();
            EditorGUILayout.LabelField("Right click on hierarchy and select DO REPLACE or push button.");
            EditorGUILayout.EndHorizontal();

            if (GUILayout.Button("DO REPLACE"))
            {
                _cachedObjects.Clear();
                _cachedObjects = new List<GameObject>(targetObjectsInEditor);
                expectedObject = expectedObjectInEditor;
                ReplaceFunction();
                targetObjectsInEditor = new GameObject[0];
            }
        }

        private static void ReplaceFunction()
        {
            if (expectedObject != null)
            {
                foreach (var tmp in _cachedObjects)
                {
                    GameObject newObj =
                        PrefabUtility.InstantiatePrefab(expectedObject) as GameObject;
                    if (!(newObj is null))
                    {
                        newObj.transform.position = tmp.transform.position;
                        newObj.transform.eulerAngles = tmp.transform.eulerAngles;
                        newObj.transform.SetParent(tmp.transform.parent);
                    }

                    DestroyImmediate(tmp);
                }
            }
            else
            {
                Debug.Log("No target objects are set.");
            }
        }
    }
}
#endif

使い方

ProjectのAssets以下、好きなところにおいてください。Editor内が良いでしょうが、どこでも動くぶんには動きます。その場合、ビルド時だけ注意してください。

Gifの通り、2つの使い方ができます。

右クリックから置換

置き換えたいObjectをセット、置き換えられたいObjectを任意の数選択し、右クリック→置換

Windowから置換

置き換えたいObjectをセット、置き換えられたいObjectを任意の数D&Dし、ボタン押下→置換