Unity汉化一个插件 制作插件汉化工具

复制代码
我是编程一个菜鸟,英语又不好,有的插件非常牛!我想学一学,页面全是英文,完全不知所措,我该怎么办啊...

尝试在Unity中汉化一个插件

效果:

思路:

复制代码
如何在Unity中把一个自己喜欢的插件变成中文?

在Unity中编写插件一般会用到编辑器扩展
在编辑器扩展中想在Inspector显示自己想要的属性名或者别的什么,就需要用到编辑器扩展的API
把这些固定的API存到一个字典里,例如"EditorGUILayout.PropertyField","LabelField"...

我可以尝试先读取我们想要汉化插件的Editor文件夹下的每一个代码的每一行
把每一行的每个字符与字典做一个对比
对比成功就说明此行代码可以被汉化,收集可以被汉化的代码行,然后把可以被汉化的代码行替换成我们想要的代码
替换成功后保存代码

听起来好像没啥问题,试试看
  • 创建一个存储字典的代码
javascript 复制代码
using System.Collections.Generic;
using UnityEngine;

[CreateAssetMenu()]
public class SearchCharacterData : ScriptableObject
{
    [Header("检索字符对")]
    public List<Item> items;
    [Header("添加字符对")]
    public bool addItem = false;
    private void OnValidate()
    {
        if (addItem)
        {
            addItem = false;
            items.Add(new Item());
        }
    }
    /// <summary>
    /// 物品数据
    /// </summary>
    [System.Serializable]
    public class Item
    {
        public string startStr;
        public string endStr;
    }
}
  • 完成之后我们就可以在项目中创建一个自定义字典了

  • 在字典中添加几对常用AIP用来测试

  • 创建一个编辑器窗口代码

javascript 复制代码
using System;
using System.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEngine;

[System.Serializable]
public class Editor_ChinesizationTool : EditorWindow
{
    private static Editor_ChinesizationTool _window;
    [MenuItem("Tools/汉化编辑器")]
    public static void GUIDRefReplaceWin()
    {
        Rect wr = new Rect(0, 0, 300, 1000);//窗口大小
        _window = (Editor_ChinesizationTool)GetWindow(typeof(Editor_ChinesizationTool), true, "汉化编辑");// false 表示不能停靠的
        _window.Show();

    }
}
复制代码
没想到要去翻译一个插件,竟然要自己先写一个...造孽啊~
  • 读文件
javascript 复制代码
	 	/// <summary>
        /// 读取数据
        /// </summary>
        /// <returns></returns>
        public List<string> ReadFileInfo(bool IsUpdateNewData = true)
        {
            Datas.Clear();
            CurrentDatas.Clear();
            CurrentSplitDatas.Clear();
            if (IsUpdateNewData) NewSplitDatas.Clear();
            StreamReader sr = null;//读取
            string assetsName = FileInfo.FullName;
            sr = File.OpenText(assetsName.Substring(assetsName.IndexOf("Assets")));//读取文件
            //读取所有行
            int line = 0;
            string data = null;
            do
            {
                data = sr.ReadLine();
                if (data != null)
                {
                    Datas.Add(data);
                    CurrentDatas.Add(data);
                    foreach (var item in searchCharacterData.items)
                    {
                        string csData = FindString(data, item.startStr, item.endStr);
                        if (csData != "")
                        {
                            CurrentSplitDatas.Add(new NewCSData(line, csData));
                            if (IsUpdateNewData) NewSplitDatas.Add(new NewCSData(line, csData));
                            break;
                        }
                    }
                }
                line++;
            } while (data != null);
            sr.Close();//关闭流
            sr.Dispose();//销毁流
            return CurrentDatas;
        }
  • 将改好的数据进行写入
javascript 复制代码
		void WriteFileInfo(List<string> datas)
        {
            StreamWriter sw;//写入
            if (!FileInfo.Exists)
            {
                Debug.LogError("无法写入,没有该文件");
                return;
            }
            ClearData(path);
            sw = FileInfo.AppendText();//打开文件
            foreach (string linedata in datas)
            {
                sw.WriteLine(linedata);
            }
            sw.Flush();//清除缓冲区
            sw.Close();//关闭流
            sw.Dispose();//销毁流
            //ReadFileInfo();
        }
  • 稍稍修正一下编辑器页面,随便导入一个插件试试看

源码

复制代码
注意!此文件需要放在Editor文件夹下才可以正常使用
javascript 复制代码
using System;
using System.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEngine;

[System.Serializable]
public class Editor_ChinesizationTool : EditorWindow
{
    private static Editor_ChinesizationTool _window;
    public string modelPath = "Assets";
    public List<CSData> cslist = new List<CSData>();
    public static Rect modelRect;
    int maxLineCount = 5;
    Vector2 csDataPos, contentPos;
    SearchCharacterData searchCharacterData = null;
    CSData CurrentCSData = null;
    [MenuItem("Tools/汉化编辑器")]
    public static void GUIDRefReplaceWin()
    {
        Rect wr = new Rect(0, 0, 300, 1000);//窗口大小
        _window = (Editor_ChinesizationTool)GetWindow(typeof(Editor_ChinesizationTool), true, "汉化编辑");// false 表示不能停靠的
        _window.Show();

    }

    private Material m_material;//1
    private void OnEnable()
    {
        m_material = new Material(Shader.Find("Hidden/Internal-Colored"));//2
        m_material.hideFlags = HideFlags.HideAndDontSave;//3
    }
    void OnGUI()
    {
        EditorGUILayout.Space();
        EditorGUILayout.LabelField("将文件夹拖拽到此处");
        EditorGUILayout.Space();
        GUI.SetNextControlName("input1");//设置下一个控件的名字
        modelRect = EditorGUILayout.GetControlRect();
        modelPath = EditorGUI.TextField(modelRect, modelPath);
        EditorGUILayout.Space();
        DragFolder();

        EditorGUILayout.Space();
        searchCharacterData = EditorGUILayout.ObjectField("", searchCharacterData, typeof(SearchCharacterData), true) as SearchCharacterData;
        // 导出材质
        if (searchCharacterData == null)
        {
            GUILayout.Label("请添加字典");
            return;
        }
        if (GUILayout.Button("读取文件"))
        {
            ReadFile();
            CurrentCSData = null;
        }
        if (CurrentCSData == null)
        {
            int currentLineCount = 1;
            csDataPos = EditorGUILayout.BeginScrollView(csDataPos, GUILayout.Width(1000), GUILayout.Height(500));
            bool isLineEnd = true;
            maxLineCount = EditorGUILayout.IntField("每行显示脚本的个数", maxLineCount);

            foreach (CSData csdate in cslist)
            {
                if (currentLineCount == 1)
                {
                    GUILayout.BeginHorizontal();
                    isLineEnd = false;
                }
                if (GUILayout.Button(csdate.name))
                {
                    CurrentCSData = csdate;
                }
                if (currentLineCount == maxLineCount)
                {
                    GUILayout.EndHorizontal();
                    currentLineCount = 0;
                    isLineEnd = true;
                }
                currentLineCount++;
            }
            if (isLineEnd == false)
            {
                GUILayout.EndHorizontal();
            }
            GUILayout.EndScrollView();
        }
        if (CurrentCSData != null)
        {
            EditorGUILayout.BeginVertical("HelpBox");
            GUILayout.BeginHorizontal();
            csDataPos = EditorGUILayout.BeginScrollView(csDataPos, GUILayout.Width(500), GUILayout.Height(700));
            #region 显示代码
            int line = 1;
            lineLocations.Clear();
            foreach (var date in CurrentCSData.CurrentDatas)
            {
                GUILayout.Label(line + "  " + date);
                foreach (var item in CurrentCSData.CurrentSplitDatas)
                {
                    if (line == item.line)
                    {
                        LineLocation lineLocation = new LineLocation();
                        Rect rect = GUILayoutUtility.GetLastRect();
                        lineLocation.FirstRect = new Vector2(rect.x, rect.y - csDataPos.y);
                        lineLocation.FirstRectOffset = csDataPos;
                        lineLocations.Add(lineLocation);
                    }
                }
                line++;
            }
            GUILayout.EndScrollView();
            GUILayout.Space(100);
            #endregion
            contentPos = EditorGUILayout.BeginScrollView(contentPos, GUILayout.Width(700), GUILayout.Height(700));

            for (int i = 0; i < CurrentCSData.CurrentSplitDatas.Count; i++)
            {
                //GUILayout.BeginHorizontal();
                GUILayout.Label(CurrentCSData.CurrentSplitDatas[i].line + 1 + "  " + CurrentCSData.CurrentDatas[CurrentCSData.CurrentSplitDatas[i].line]);//找到可更换数据
                lineLocations[i].FirstRect.y += contentPos.y;
                lineLocations[i].LastRectOffset = contentPos;
                Rect rect = GUILayoutUtility.GetLastRect();
                lineLocations[i].LastRect = new Vector2(rect.x, rect.y);
                CurrentCSData.NewSplitDatas[i].data = EditorGUILayout.TextField(CurrentCSData.NewSplitDatas[i].data);

                //GUILayout.EndHorizontal();
            }
            foreach (var item in lineLocations)
            {
                m_material.SetPass(0);//4
                GL.Begin(GL.LINES);
                GL.Color(Color.red);
                GL.Vertex3(item.FirstRect.x - 100+10, LimitMax(item.FirstRect.y + 30,690+ item.LastRectOffset.y, item.LastRectOffset.y), 0);
                GL.Vertex3(item.FirstRect.x - 105+10, LimitMax(item.FirstRect.y + 30, 690+ item.LastRectOffset.y, item.LastRectOffset.y), 0);
                GL.End();

                GL.Begin(GL.LINES);
                GL.Color(Color.black);
                GL.Vertex3(item.FirstRect.x - 100+10, LimitMax(item.FirstRect.y + 30,690 + item.LastRectOffset.y, item.LastRectOffset.y), 0);
                //================================================================================================
                GL.Vertex3(item.LastRect.x-10, LimitMax(item.LastRect.y + 10, 690 + item.LastRectOffset.y, item.LastRectOffset.y), 0); 
                GL.End();

                GL.Begin(GL.LINES);
                GL.Color(Color.red);
                GL.Vertex3(item.LastRect.x-10, LimitMax(item.LastRect.y + 10, 690 + item.LastRectOffset.y, item.LastRectOffset.y), 0);
                GL.Vertex3(item.LastRect.x-5, LimitMax(item.LastRect.y + 10, 690 + item.LastRectOffset.y, item.LastRectOffset.y), 0);
                GL.End();
                //Debug.Log("FirstRect:" + item.FirstRect+"__"+ "LastRect:"+ item.LastRect);
                //break;
            }


            GUILayout.EndScrollView();

            GUILayout.EndHorizontal();
            GUILayout.BeginHorizontal();
            if (GUILayout.Button("确认更改"))
            {
                CurrentCSData.ReadFileInfo(false);
                for (int i = 0; i < CurrentCSData.CurrentSplitDatas.Count; i++)
                {
                    CurrentCSData.CurrentDatas[CurrentCSData.CurrentSplitDatas[i].line] =
                    ReplaceStr(CurrentCSData.CurrentDatas[CurrentCSData.CurrentSplitDatas[i].line],
                        CurrentCSData.CurrentSplitDatas[i].data,
                        CurrentCSData.NewSplitDatas[i].data);
                }
                for (int i = 0; i < CurrentCSData.Datas.Count; i++)
                {
                    CurrentCSData.Datas[i] = CurrentCSData.CurrentDatas[i];
                }
                CurrentCSData.WriterData();
                AssetDatabase.Refresh();
            }
            if (GUILayout.Button("重新选择文件"))
            {
                CurrentCSData = null;
            }
            GUILayout.EndHorizontal();
            GUILayout.EndVertical();
        }

        EditorGUILayout.Space();

    }
    List<LineLocation> lineLocations = new List<LineLocation>();
    public class LineLocation
    {
        public Vector2 FirstRectOffset;
        public Vector2 FirstRect;
        public Vector2 LastRectOffset;
        public Vector2 LastRect;
    }
    public void ReadFile()
    {
        GetAllFilesAndDertorys(modelPath, searchCharacterData);
    }
    /// <summary>
    /// 获得拖拽文件
    /// </summary>
    void DragFolder()
    {
        //鼠标位于当前窗口
        if (mouseOverWindow == this)
        {
            //拖入窗口未松开鼠标
            if (Event.current.type == EventType.DragUpdated)
            {
                DragAndDrop.visualMode = DragAndDropVisualMode.Generic;//改变鼠标外观
                                                                       // 判断区域
                if (modelRect.Contains(Event.current.mousePosition))
                    GUI.FocusControl("input1");
            }
            //拖入窗口并松开鼠标
            else if (Event.current.type == EventType.DragExited)
            {
                string dragPath = string.Join("", DragAndDrop.paths);
                // 判断区域
                if (modelRect.Contains(Event.current.mousePosition))
                    modelPath = dragPath;

                // 取消焦点(不然GUI不会刷新)
                GUI.FocusControl(null);
            }
        }
    }
    static string FindString(string str, string StartStr, string EndStr)
    {
        if (str.Length < 3)
            return "";
        int index3 = str.IndexOf(StartStr);
        if (index3 != -1)
        {
            int index4 = str.IndexOf(EndStr, index3);
            if (index4 != -1)
            {
                return str.Substring(index3 + StartStr.Length, index4 - index3 - StartStr.Length);
            }
        }
        return "";
    }
    void GetAllFilesAndDertorys(string _path, SearchCharacterData searchCharacterData)
    {
        //判断路径是否存在
        if (Directory.Exists(_path))
        {
            #region 找到Editor文件夹
            DirectoryInfo dir = new DirectoryInfo(_path);
            DirectoryInfo[] allDirs = dir.GetDirectories("*", SearchOption.AllDirectories);
            string EditorPath = "";
            foreach (var item in allDirs)
            {
                //忽略.meta
                if (item.Name.EndsWith(".meta")) continue;
                string assetsName = item.FullName;
                assetsName = assetsName.Substring(assetsName.IndexOf("Assets"));
                if (item.Name == "Editor")
                {
                    EditorPath = assetsName;
                    break;
                }
            }
            #endregion

            if (EditorPath != "")
            {
                #region 得到Editor文件夹下所有的.cs文件
                DirectoryInfo editorDir = new DirectoryInfo(EditorPath);
                cslist.Clear();
                int ListIndex = 0;
                foreach (var item in editorDir.GetFiles("*", SearchOption.AllDirectories))
                {
                    //忽略.meta
                    string assetsName = item.FullName;
                    assetsName = assetsName.Substring(assetsName.IndexOf("Assets"));
                    if (item.Name.EndsWith(".meta")) continue;
                    if (item.Name.EndsWith(".cs"))
                    {
                        cslist.Add(new CSData(ListIndex, item, assetsName, searchCharacterData, this));
                    }
                    ListIndex++;
                }
                #endregion
                foreach (var item in cslist)
                {
                    item.ReadFileInfo();
                }
            }
            else
            {
                Debug.LogError("该目录没有Editor文件夹");
            }
        }
    }
    string ReplaceStr(string str, string oldStr, string newStr)
    {
        if (str.Length < 3)
            return "";
        int strIndex = str.IndexOf(oldStr);
        if (strIndex != -1)
        {
            string startStr = str.Substring(0, strIndex);
            string endStr = str.Substring(strIndex + oldStr.Length);
            return startStr + newStr + endStr;
        }
        return "";
    }
    public float LimitMax(float f, float maxValue = 690, float minValue = 0)
    {
        //return f;
        return Math.Min(maxValue, Math.Max(f, minValue));
    }
    [System.Serializable]
    public class CSData
    {
        Editor_ChinesizationTool editor_ChinesizationTool = null;
        SearchCharacterData searchCharacterData = null;
        public string name;
        private FileInfo fileInfo;
        public int ListIndex = -1;
        public string path;
        /// <summary>
        /// 原始数据
        /// </summary>
        public List<string> Datas = new List<string>();
        /// <summary>
        /// 当前数据
        /// </summary>
        public List<string> CurrentDatas = new List<string>();
        /// <summary>
        /// 新数据
        /// </summary>
        public List<NewCSData> CurrentSplitDatas = new List<NewCSData>();
        public List<NewCSData> NewSplitDatas = new List<NewCSData>();

        public FileInfo FileInfo
        {
            get
            {
                if (fileInfo == null)
                {
                    editor_ChinesizationTool.ReadFile();
                    fileInfo = editor_ChinesizationTool.cslist[ListIndex].FileInfo;
                }
                return fileInfo;
            }
            set => fileInfo = value;
        }

        public CSData(int mListIndex, FileInfo mfileInfo, string path, SearchCharacterData searchCharacterData, Editor_ChinesizationTool parent)
        {
            FileInfo = mfileInfo;
            this.path = path;
            this.name = FileInfo.Name;
            this.searchCharacterData = searchCharacterData;
            this.ListIndex = mListIndex;
            this.editor_ChinesizationTool = parent;
        }
        /// <summary>
        /// 写入数据
        /// </summary>
        public void WriterData()
        {
            WriteFileInfo(Datas);
        }
        /// <summary>
        /// 读取数据
        /// </summary>
        /// <returns></returns>
        public List<string> ReadFileInfo(bool IsUpdateNewData = true)
        {
            Datas.Clear();
            CurrentDatas.Clear();
            CurrentSplitDatas.Clear();
            if (IsUpdateNewData) NewSplitDatas.Clear();
            StreamReader sr = null;//读取
            string assetsName = FileInfo.FullName;
            sr = File.OpenText(assetsName.Substring(assetsName.IndexOf("Assets")));//读取文件
            //读取所有行
            int line = 0;
            string data = null;
            do
            {
                data = sr.ReadLine();
                if (data != null)
                {
                    Datas.Add(data);
                    CurrentDatas.Add(data);
                    foreach (var item in searchCharacterData.items)
                    {
                        string csData = FindString(data, item.startStr, item.endStr);
                        if (csData != "")
                        {
                            CurrentSplitDatas.Add(new NewCSData(line, csData));
                            if (IsUpdateNewData) NewSplitDatas.Add(new NewCSData(line, csData));
                            break;
                        }
                    }
                }
                line++;
            } while (data != null);
            sr.Close();//关闭流
            sr.Dispose();//销毁流
            return CurrentDatas;
        }
        void WriteFileInfo(List<string> datas)
        {
            StreamWriter sw;//写入
            if (!FileInfo.Exists)
            {
                Debug.LogError("无法写入,没有该文件");
                return;
            }
            ClearData(path);
            sw = FileInfo.AppendText();//打开文件
            foreach (string linedata in datas)
            {
                sw.WriteLine(linedata);
            }
            sw.Flush();//清除缓冲区
            sw.Close();//关闭流
            sw.Dispose();//销毁流
            //ReadFileInfo();
        }
        void ClearData(string path)
        {
            StreamWriter tmpWrite = new StreamWriter(path);
            tmpWrite.Write("");
            tmpWrite.Close();
        }
    }
    [System.Serializable]
    public class NewCSData
    {
        public int line = 0;
        public string data = "";

        public NewCSData(int line, string data)
        {
            this.line = line;
            this.data = data;
        }
    }
}

最后的最后:

我自己反正没实践过,可以先拿这个玩玩看还是挺有意思的~

觉得有意思可以改巴改巴,也可以把建议放在评论区,有空我就更新一下~
Demo源码

相关推荐
Zillionnn9 小时前
Unreal Engine 自动设置图像
游戏引擎·虚幻
11 小时前
Unity开发中常用的洗牌算法
java·算法·unity·游戏引擎·游戏开发
马特说15 小时前
Unity VR手术模拟系统架构分析与数据流设计
unity·系统架构·vr
心前阳光1 天前
Unity WebGL文本输入
unity·游戏引擎·webgl
天涯过客TYGK2 天前
unity A星寻路
unity·游戏引擎
KhalilRuan2 天前
Unity Demo——3D平台跳跃游戏笔记
笔记·游戏·unity·游戏引擎
AA陈超2 天前
虚幻引擎UE5专用服务器游戏开发-20 添加基础能力类与连招能力
c++·游戏·ue5·游戏引擎·虚幻
ttod_qzstudio2 天前
Unity中使用EzySlice实现模型切割与UV控制完全指南
unity
南無忘码至尊3 天前
Unity 实现与 Ollama API 交互的实时流式响应处理
unity·游戏引擎·交互
平行云3 天前
如何实现UE程序大并发多集群的像素流部署
unity·ue5·图形渲染