Unity编辑器扩展——通用组件替换工具

在我们的项目过程中经常会遇到某个组件迭代的过程,那么在废弃旧组件,替换新组件的时候,就可以使用一个通用组件替换工具完成一键替换的功能。

设计图如下:

 

基类ReplaceComponentBase泛型类,将旧组件和新组件传递进去。

   public abstract class ReplaceComponentBase<TOldComponent, TNewComponent> 
        where TOldComponent : Component
        where TNewComponent : Component

MakeFindFile方法是一个虚方法供派生类可重写自己的查找方式。默认从预制体中进行查找。

        protected string[] _findFiles;        

        protected virtual void MakeFindFile()
        {
            //收集所有预制信息
            var extensions = new List<string>() {".prefab"};
            var files = Directory.GetFiles(Application.dataPath, "*.*", SearchOption.AllDirectories)
                .Where(s => extensions.Contains(Path.GetExtension(s)?.ToLower())).ToArray();

            _findFiles = Directory.GetFiles(Application.dataPath, "*.*", SearchOption.AllDirectories)
                .Where(s => extensions.Contains(Path.GetExtension(s)?.ToLower())).ToArray();
        }

StartReplace方法传入数据上下文,并打开进度条进行查找。

        protected virtual void StartReplace(ComponentDataContextBase dataContextBase)
        {
            _findComponentDict = new Dictionary<string, List<TOldComponent>>();
            _dataContextBase = dataContextBase;

            MakeFindFile();

            EditorUtilityHelp.ShowUpdate("正在查找", _findFiles.Length, (index) => { FindInPrefab(_findFiles[index]); },
                FindPrefabOver);
        }

ReplaceIng方法在替换过程中将旧的组件全部替换成新的组件,具体的数据操作交由ComponentDataContext子类实现。

        protected virtual void ReplaceIng(TOldComponent oldComponent)
        {
            var gameObject = oldComponent.gameObject;

            _dataContextBase.CopyComponent(oldComponent);

            Object.DestroyImmediate(oldComponent, true);

            var newComponent = gameObject.AddComponent<TNewComponent>();
            _dataContextBase.PasteComponent(newComponent);
        }

ReplaceOver在替换完成时调用,通常打印相关的替换信息。

        protected virtual void ReplaceOver()
        {
            foreach (var item in _findComponentDict)
            {
                GameObject prefab = LoadGameObjectByGuid(item.Key, out var prefabPath);
                Debug.Log($"Replace Prefab {prefab.name}  {prefabPath}  ComponentsLength {item.Value.Count}", prefab);
            }
        }

ComponentDataContextBase这个类提供两个抽象方法供子类重写,主要完成对数据的赋值和粘贴操作。

    public abstract class ComponentDataContextBase
    {
        public abstract void CopyComponent<TOldComponent>(TOldComponent oldComponent) where TOldComponent : Component;
        public abstract void PasteComponent<TNewComponent>(TNewComponent newComponent) where TNewComponent : Component;
    }

对于实际的应用类,例如我们需要将Test1换成Test2组件的话,testData和nameData为这两个脚本中公用的数据。那么对应的两个类分别为:

    public class TestScriptComponentDataContext : ComponentDataContextBase
    {
        public object testData;
    
        public object nameData;
        
        private static BindingFlags _flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
        
        public override void CopyComponent<TOldComponent>(TOldComponent oldComponent)
        {
            var test1Script = oldComponent as Test1;
            testData = typeof(Test1).GetField("TestData1", _flags)?.GetValue(test1Script);
            nameData = typeof(Test1).GetField("Name", _flags)?.GetValue(test1Script);
        }

        public override void PasteComponent<TNewComponent>(TNewComponent newComponent)
        {
            var test2Script = newComponent as Test2;
            
            typeof(Test2).GetField("TestData1", _flags)?.SetValue(test2Script,testData);
            typeof(Test2).GetField("Name", _flags)?.SetValue(test2Script,nameData);
        }
    }
    public class TestScriptReplaceComponent : ReplaceComponentBase<Test1,Test2>
    {
        public void StartReplaceTestScript()
        {
            StartReplace(new TestScriptComponentDataContext());
        }
    }


版权声明:本文为dmk17771552304原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。