一:前言
對于面闆指派或Find綁定UI元件,我們可以使用一種工具化的方式去自動生成代碼并綁定對象,增加效率
分為logic和view,view層是UI界面上的元件,每次都會自動生成并覆寫,logic層是邏輯
二:使用
例如一個UI界面,我們隻需要做成預制體并在Project下右鍵預制體,選擇AutoGen/Create View則會自動生成view和logic兩個腳本,logic是我們要編寫邏輯的腳本,view是每次都會自動生成并覆寫的腳本
三:說明
——以下幾個路徑都是可以自定義的(view和logic生成的路徑、view和logic模版檔案的路徑)
——可以自定義忽略的元件類型清單
四:代碼實作
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using System.IO;
using System.Text;
using System;
public class AutoGenCode
{
//logic層代碼路徑
const string LogicDir = "Assets/AutoGen/Logic";
//view層代碼路徑
const string ViewDir = "Assets/AutoGen/View";
//logic層模版檔案路徑
const string LogicTempletePath = "Assets/AutoGen/LogicTemplete.txt";
//view層模版檔案路徑
const string ViewTempletePath = "Assets/AutoGen/ViewTemplete.txt";
//命名空間模闆
const string NameSpaceTemplete = "using {0};";
//字段模闆
const string FieldTemplete = "public {0} {1};\t";
//方法模闆
const string MethodTemplete = "{0} = gameObject.transform.Find(\"{1}\").GetComponent<{2}>();\t\t";
/// <summary>
/// 忽略的元件類型清單
/// </summary>
static List<Type> IgnoreComponentTypeList = new List<Type>()
{
typeof(CanvasRenderer),
typeof(RectTransform),
};
[MenuItem("Assets/AutoGen/Create View", priority = 0)]
static void CreateLogicAndView()
{
GameObject go = Selection.activeGameObject;
//判斷是否是prefab
if (PrefabUtility.GetPrefabAssetType(go) != PrefabAssetType.Regular)
{
Debug.LogWarning("選擇的不是預制體,選擇的對象:" + go.name);
return;
}
if (!Directory.Exists(ViewDir))
{
Directory.CreateDirectory(ViewDir);
}
if (!Directory.Exists(LogicDir))
{
Directory.CreateDirectory(LogicDir);
}
string className = go.name + "View";
StringBuilder fieldContent = new StringBuilder();
StringBuilder methodContent = new StringBuilder();
StringBuilder nameSpaceContent = new StringBuilder();
nameSpaceContent.AppendLine(NameSpaceTemplete.Replace("{0}", "UnityEngine"));//必須有UnityEngine命名空間
string logicTempleteContent = File.ReadAllText(LogicTempletePath, Encoding.UTF8);
string viewTempleteContent = File.ReadAllText(ViewTempletePath, Encoding.UTF8);
string logicPath = LogicDir + "/" + go.name + "Logic.cs";
string viewPath = ViewDir + "/" + go.name + "View.cs";
List<string> tempNameSpaceList = new List<string>();
//計算所有子物體元件資料
List<ComponentInfo> infoList = new List<ComponentInfo>();
CalcComponentInfo("", go.transform, infoList);
foreach (var tempInfo in infoList)
{
//字段
string tempFieldStr = FieldTemplete.Replace("{0}", tempInfo.TypeStr);
tempFieldStr = tempFieldStr.Replace("{1}", tempInfo.FieldName);
fieldContent.AppendLine(tempFieldStr);
//綁定方法
string tempMethodStr = MethodTemplete.Replace("{0}", tempInfo.FieldName);
tempMethodStr = tempMethodStr.Replace("{1}", tempInfo.Path);
tempMethodStr = tempMethodStr.Replace("{2}", tempInfo.TypeStr);
methodContent.AppendLine(tempMethodStr);
//命名空間
if (!tempNameSpaceList.Contains(tempInfo.NameSpace))
{
string tempNameSpaceStr = NameSpaceTemplete.Replace("{0}", tempInfo.NameSpace);
tempNameSpaceList.Add(tempInfo.NameSpace);
nameSpaceContent.AppendLine(tempNameSpaceStr);
}
}
//logic層腳本
if (!File.Exists(logicPath))
{
using (StreamWriter sw = new StreamWriter(logicPath))
{
string content = logicTempleteContent;
content = content.Replace("#CLASSNAME#", className);
sw.Write(content);
sw.Close();
}
}
//view層腳本
using (StreamWriter sw = new StreamWriter(viewPath))
{
string content = viewTempleteContent;
content = content.Replace("#NAMESPACE#", nameSpaceContent.ToString());
content = content.Replace("#CLASSNAME#", className);
content = content.Replace("#FIELD_BIND#", fieldContent.ToString());
content = content.Replace("#METHOD_BIND#", methodContent.ToString());
sw.Write(content);
sw.Close();
}
AssetDatabase.Refresh();
}
/// <summary>
/// 計算所有子物體元件資料
/// </summary>
static void CalcComponentInfo(string path, Transform child, List<ComponentInfo> infoList)
{
bool isRoot = string.IsNullOrEmpty(path);
if (!isRoot
&& IsVaildField(child.name))
{
var componentList = child.GetComponents<Component>();
foreach (var tempComponent in componentList)
{
ComponentInfo info = new ComponentInfo()
{
Path = path,
go = child.gameObject,
NameSpace = tempComponent.GetType().Namespace,
TypeStr = tempComponent.GetType().Name,
};
if (!HaveSameComponentInfo(info, infoList)
&& !IgnoreComponentTypeList.Contains(tempComponent.GetType()))
{
infoList.Add(info);
}
}
}
foreach (Transform tempTrans in child.transform)
{
CalcComponentInfo(isRoot ? tempTrans.name : path + "/" + tempTrans.name, tempTrans.transform, infoList);
}
}
/// <summary>
/// 是否為合法的字段名
/// </summary>
static bool IsVaildField(string goName)
{
if (goName.Contains("_"))
{
if (int.TryParse(goName[0].ToString(), out _))
{
Debug.LogWarning("字段名不能以數字開頭:, goName :" + goName);
return false;
}
return true;
}
return false;
}
/// <summary>
/// 是否有相同的元件資料
/// </summary>
static bool HaveSameComponentInfo(ComponentInfo info, List<ComponentInfo> infoList)
{
foreach (var tempInfo in infoList)
{
if (tempInfo.FieldName == info.FieldName)
{
Debug.LogWarning("子物體名重複:, goName :" + info.go.name);
return true;
}
}
return false;
}
}
/// <summary>
/// 元件資料
/// </summary>
public class ComponentInfo
{
public string Path;
public GameObject go;
public string NameSpace;
public string TypeStr;
public string FieldName
{
get
{
return $"{go.name}_{TypeStr}";
}
}
}