需求
根据选中的Prefab创建模板脚本
分析
先拆解一下需求,如下
当使用框架开发时,Prefab挂载的很多脚本都有固定的格式。从Unity的基础模板创建cs文件,再修改到应有的模板,会浪费一些时间。尤其是有大量的不同界面时,每个都改一遍,浪费时间不说,还有可能遗漏或错改。写个脚本创建指定的模板代替C#基础模板。
注:当前脚本使用的NGUI,使用UGUI时替换对应的Component即可
代码
获取属性
遍历所有节点并缓存所有m_
开头的节点,标记为需要在脚本中使用的,供后面使用
private static List<GameObject> _nodeList;
private static List<UISprite> _imgList;
private static List<UIButton> _btnList;
private static List<UILabel> _labelList;
private static List<UISlider> _sliderList;
private static void GetNodeData(GameObject prefab)
{
Transform[] nodes = prefab.GetComponentsInChildren<Transform>(true);
_nodeList = new List<GameObject>();
_imgList = new List<UISprite>();
_labelList = new List<UILabel>();
_btnList = new List<UIButton>();
_sliderList = new List<UISlider>();
// 遍历所有子对象
foreach (var node in nodes)
{
if (!node.gameObject.name.StartsWith("m_")) continue;
var btn = node.GetComponent<UIButton>();
var img = node.GetComponent<UISprite>();
var label = node.GetComponent<UILabel>();
var slider = node.GetComponent<UISlider>();
if (btn != null)
{
_btnList.Add(btn);
}
else if (slider != null)
{
_sliderList.Add(slider);
}
else if (img != null)
{
_imgList.Add(img);
}
else if (label != null)
{
_labelList.Add(label);
}
else
{
_nodeList.Add(node.gameObject);
}
}
}
生成模板脚本
生成模板脚本,并在脚本中添加对应的属性,属性名为节点名后半部分
private const string MenuItemText1 = "Assets/Create/模板脚本/创建Mediator模板脚本";
[MenuItem(MenuItemText1, false, 25)]
public static void CreateScript()
{
CreateScript(false);
}
private static void CreateScript(bool isPartial)
{
var assetPath = AssetDatabase.GetAssetPath(Selection.activeObject);
var directoryPath = System.IO.Path.GetDirectoryName(assetPath);
var folderName = System.IO.Path.GetFileName(directoryPath);
var folderPath = $"Assets/Game/GameLogic/{folderName}";//自己对应的路径
if (!Directory.Exists(folderPath))
{
if (directoryPath != null) Directory.CreateDirectory(folderPath);
}
var scriptPath = $"{folderPath}/{Selection.activeObject.name}Mediator.cs";
if (File.Exists(scriptPath))
{
Debug.LogError($"The script \"{scriptPath}\" already exists.");
return;
}
var myPrefab = AssetDatabase.LoadAssetAtPath<GameObject>(assetPath);
// 获取 Prefab 的所有子对象
GetNodeData(myPrefab);
var txt = $@"using UnityEngine;
namespace 自己的命名空间
{{
public {(isPartial ? "partial" : "")} class {Selection.activeObject.name}Mediator : NGuiFormMasterLogic
{{
//自己的模板
public override string FacadeName
{{
get {{ return HomeFacade.Name; }}//TODO:修改FacadeName
}}
public new const string NAME = {$"\"{Selection.activeObject.name}Mediator\""};
public override void OnConstruct(object userData)
{{
base.OnConstruct(userData);
MediatorName = NAME;
}}
";
var txtEnd = $@"
}}
}}";
for (var i = 0; i < _nodeList.Count; i++)
{
var name = _nodeList[i].name[2..];
name = name[..1].ToUpper() + name[1..];
var str = $@"
[SerializeField] private GameObject {name};
";
txt += str;
}
for (var i = 0; i < _imgList.Count; i++)
{
var name = _imgList[i].name[2..];
name = name[..1].ToUpper() + name[1..];
var str = $@"
[SerializeField] private UISprite {name};
";
txt += str;
}
for (var i = 0; i < _labelList.Count; i++)
{
var name = _labelList[i].name[2..];
name = name[..1].ToUpper() + name[1..];
var str = $@"
[SerializeField] private UILabel {name};
";
txt += str;
}
for (var i = 0; i < _btnList.Count; i++)
{
var name = _btnList[i].name[2..];
name = name[..1].ToUpper() + name[1..];
var str = $@"
[SerializeField] private UIButton {name};
";
txt += str;
}
for (var i = 0; i < _sliderList.Count; i++)
{
var name = _sliderList[i].name[2..];
name = name[..1].ToUpper() + name[1..];
var str = $@"
[SerializeField] private UISlider {name};
";
txt += str;
}
for (var i = 0; i < _btnList.Count; i++)
{
var name = _btnList[i].name[2..];
name = name[..1].ToUpper() + name[1..];
var str = $@"
public void On{name}Click()
{{
}}
";
txt += str;
}
txt += txtEnd;
EditorPrefs.SetString("creatorPath", assetPath);
EditorPrefs.SetString("creatorName", myPrefab.name);
EditorPrefs.SetString("creatorScriptPath", scriptPath);
File.WriteAllText(scriptPath, txt);
AssetDatabase.Refresh();
}
[MenuItem(MenuItemText1, true)]
public static bool CreatorNormal()
{
if (!Selection.activeObject) return false;
var prefabAssetType = PrefabUtility.GetPrefabAssetType(Selection.activeObject);
return prefabAssetType != PrefabAssetType.NotAPrefab;
}
添加脚本并自动挂载节点
根据脚本声明的属性,获取第一个满足条件的节点,并赋值给脚本(对同名节点未作考虑,Prefab 中做规避)
private const string MenuItemText3 = "Assets/Create/模板脚本/添加Mediator模板脚本";
[MenuItem(MenuItemText3, false, 25)]
public static void AddScript()
{
AddComp();
}
public static void AddComp()
{
var scriptName = EditorPrefs.GetString("creatorName");
string[] guids = AssetDatabase.FindAssets(scriptName);
if (guids.Length > 0)
{
var assetPath = EditorPrefs.GetString("creatorPath");
string path = AssetDatabase.GUIDToAssetPath(guids[0]);
MonoScript monoScript = AssetDatabase.LoadAssetAtPath<MonoScript>(path);
System.Type type = monoScript.GetClass();
var prefab =
GameObject.Instantiate(
AssetDatabase
.LoadMainAssetAtPath(
assetPath)) as GameObject; // PrefabUtility.LoadPrefabContents(assetPath);//
var component = prefab.AddComponent(type);
// 获取所有字段
var fields = type.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
var list = prefab.GetComponentsInChildren<Transform>(true).Where(a => a.name.StartsWith("m_")).ToList();
foreach (FieldInfo field in fields)
{
// 判断字段是否被序列化
var attributes = field.GetCustomAttributes(typeof(SerializeField), false);
if (attributes.Length > 0)
{
var temp = field.Name + "";
var name = temp[..1].ToLower() + temp[1..];
var go = list.FirstOrDefault(a => a.name == $"m_{name}");
if (go != null)
{
list.Remove(go);
var btn = go.GetComponent<UIButton>();
var slider = go.GetComponent<UISlider>();
var img = go.GetComponent<UISprite>();
var txt = go.GetComponent<UILabel>();
if (btn != null)
{
var str = $"On{field.Name}Click";
Debug.Log(str);
var delegateFunc1 = new EventDelegate(component as MonoBehaviour, str);
btn.onClick.Add(delegateFunc1);
field.SetValue(component, btn);
}
else if (slider != null)
{
field.SetValue(component, slider);
}
else if (img != null)
{
field.SetValue(component, img);
}
else if (txt != null)
{
field.SetValue(component, txt);
}
else
{
field.SetValue(component, go.gameObject);
}
}
}
}
// GetNodeData(prefab);
// PrefabUtility.SaveAsPrefabAssetAndConnect(prefab, assetPath);
PrefabUtility.SaveAsPrefabAssetAndConnect(prefab, assetPath, InteractionMode.AutomatedAction);
Object.DestroyImmediate(prefab);
AssetDatabase.Refresh();
}
else
{
Debug.LogError($"请先生成脚本");
}
}
[MenuItem(MenuItemText3, true)]
public static bool AddScript1()
{
if (!Selection.activeObject) return false;
var prefabAssetType = PrefabUtility.GetPrefabAssetType(Selection.activeObject);
return prefabAssetType != PrefabAssetType.NotAPrefab;
}
完整脚本
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using UnityEditor;
using UnityEngine;
public class MediatorScriptCreator
{
private const string MenuItemText1 = "Assets/Create/模板脚本/创建Mediator模板脚本";
private const string MenuItemText3 = "Assets/Create/模板脚本/添加Mediator模板脚本";
[MenuItem(MenuItemText1, false, 25)]
public static void CreateScript()
{
CreateScript(false);
}
[MenuItem(MenuItemText3, false, 25)]
public static void AddScript()
{
AddComp();
}
private static List<GameObject> _nodeList;
private static List<UISprite> _imgList;
private static List<UIButton> _btnList;
private static List<UILabel> _labelList;
private static List<UISlider> _sliderList;
private static void CreateScript(bool isPartial)
{
var assetPath = AssetDatabase.GetAssetPath(Selection.activeObject);
var directoryPath = System.IO.Path.GetDirectoryName(assetPath);
var folderName = System.IO.Path.GetFileName(directoryPath);
var folderPath = $"Assets/Game/GameLogic/{folderName}";//自己对应的路径
if (!Directory.Exists(folderPath))
{
if (directoryPath != null) Directory.CreateDirectory(folderPath);
}
var scriptPath = $"{folderPath}/{Selection.activeObject.name}Mediator.cs";
if (File.Exists(scriptPath))
{
Debug.LogError($"The script \"{scriptPath}\" already exists.");
return;
}
var myPrefab = AssetDatabase.LoadAssetAtPath<GameObject>(assetPath);
// 获取 Prefab 的所有子对象
GetNodeData(myPrefab);
var txt = $@"using UnityEngine;
namespace PD
{{
public {(isPartial ? "partial" : "")} class {Selection.activeObject.name}Mediator : NGuiFormMasterLogic
{{
public override string FacadeName
{{
get {{ return HomeFacade.Name; }}//TODO:修改FacadeName
}}
public new const string NAME = {$"\"{Selection.activeObject.name}Mediator\""};
public override void OnConstruct(object userData)
{{
base.OnConstruct(userData);
MediatorName = NAME;
}}
";
var txtEnd = $@"
}}
}}";
for (var i = 0; i < _nodeList.Count; i++)
{
var name = _nodeList[i].name[2..];
name = name[..1].ToUpper() + name[1..];
var str = $@"
[SerializeField] private GameObject {name};
";
txt += str;
}
for (var i = 0; i < _imgList.Count; i++)
{
var name = _imgList[i].name[2..];
name = name[..1].ToUpper() + name[1..];
var str = $@"
[SerializeField] private UISprite {name};
";
txt += str;
}
for (var i = 0; i < _labelList.Count; i++)
{
var name = _labelList[i].name[2..];
name = name[..1].ToUpper() + name[1..];
var str = $@"
[SerializeField] private UILabel {name};
";
txt += str;
}
for (var i = 0; i < _btnList.Count; i++)
{
var name = _btnList[i].name[2..];
name = name[..1].ToUpper() + name[1..];
var str = $@"
[SerializeField] private UIButton {name};
";
txt += str;
}
for (var i = 0; i < _sliderList.Count; i++)
{
var name = _sliderList[i].name[2..];
name = name[..1].ToUpper() + name[1..];
var str = $@"
[SerializeField] private UISlider {name};
";
txt += str;
}
for (var i = 0; i < _btnList.Count; i++)
{
var name = _btnList[i].name[2..];
name = name[..1].ToUpper() + name[1..];
var str = $@"
public void On{name}Click()
{{
}}
";
txt += str;
}
txt += txtEnd;
EditorPrefs.SetString("creatorPath", assetPath);
EditorPrefs.SetString("creatorName", myPrefab.name);
EditorPrefs.SetString("creatorScriptPath", scriptPath);
File.WriteAllText(scriptPath, txt);
AssetDatabase.Refresh();
}
public static void AddComp()
{
var scriptName = EditorPrefs.GetString("creatorName");
string[] guids = AssetDatabase.FindAssets(scriptName);
if (guids.Length > 0)
{
var assetPath = EditorPrefs.GetString("creatorPath");
string path = AssetDatabase.GUIDToAssetPath(guids[0]);
MonoScript monoScript = AssetDatabase.LoadAssetAtPath<MonoScript>(path);
System.Type type = monoScript.GetClass();
var prefab =
GameObject.Instantiate(
AssetDatabase
.LoadMainAssetAtPath(
assetPath)) as GameObject; // PrefabUtility.LoadPrefabContents(assetPath);//
var component = prefab.AddComponent(type);
// 获取所有字段
var fields = type.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
var list = prefab.GetComponentsInChildren<Transform>(true).Where(a => a.name.StartsWith("m_")).ToList();
foreach (FieldInfo field in fields)
{
// 判断字段是否被序列化
var attributes = field.GetCustomAttributes(typeof(SerializeField), false);
if (attributes.Length > 0)
{
var temp = field.Name + "";
var name = temp[..1].ToLower() + temp[1..];
var go = list.FirstOrDefault(a => a.name == $"m_{name}");
if (go != null)
{
list.Remove(go);
var btn = go.GetComponent<UIButton>();
var slider = go.GetComponent<UISlider>();
var img = go.GetComponent<UISprite>();
var txt = go.GetComponent<UILabel>();
if (btn != null)
{
var str = $"On{field.Name}Click";
Debug.Log(str);
var delegateFunc1 = new EventDelegate(component as MonoBehaviour, str);
btn.onClick.Add(delegateFunc1);
field.SetValue(component, btn);
}
else if (slider != null)
{
field.SetValue(component, slider);
}
else if (img != null)
{
field.SetValue(component, img);
}
else if (txt != null)
{
field.SetValue(component, txt);
}
else
{
field.SetValue(component, go.gameObject);
}
}
}
}
// GetNodeData(prefab);
// PrefabUtility.SaveAsPrefabAssetAndConnect(prefab, assetPath);
PrefabUtility.SaveAsPrefabAssetAndConnect(prefab, assetPath, InteractionMode.AutomatedAction);
Object.DestroyImmediate(prefab);
AssetDatabase.Refresh();
}
else
{
Debug.LogError($"请先生成脚本");
}
}
[MenuItem(MenuItemText1, true)]
public static bool CreatorNormal()
{
if (!Selection.activeObject) return false;
var prefabAssetType = PrefabUtility.GetPrefabAssetType(Selection.activeObject);
return prefabAssetType != PrefabAssetType.NotAPrefab;
}
[MenuItem(MenuItemText3, true)]
public static bool AddScript1()
{
if (!Selection.activeObject) return false;
var prefabAssetType = PrefabUtility.GetPrefabAssetType(Selection.activeObject);
return prefabAssetType != PrefabAssetType.NotAPrefab;
}
private static void GetNodeData(GameObject prefab)
{
Transform[] nodes = prefab.GetComponentsInChildren<Transform>(true);
_nodeList = new List<GameObject>();
_imgList = new List<UISprite>();
_labelList = new List<UILabel>();
_btnList = new List<UIButton>();
_sliderList = new List<UISlider>();
// 遍历所有子对象
foreach (var node in nodes)
{
if (!node.gameObject.name.StartsWith("m_")) continue;
var btn = node.GetComponent<UIButton>();
var img = node.GetComponent<UISprite>();
var label = node.GetComponent<UILabel>();
var slider = node.GetComponent<UISlider>();
if (btn != null)
{
_btnList.Add(btn);
}
else if (slider != null)
{
_sliderList.Add(slider);
}
else if (img != null)
{
_imgList.Add(img);
}
else if (label != null)
{
_labelList.Add(label);
}
else
{
_nodeList.Add(node.gameObject);
}
}
}
}
使用方法
知识点
1.字符串使用@前缀,可以不使用\n换行。和$搭配使用时,字符串中如果有 { 需要用 {{ 两个大括号表示,第一个为转义。 字符串中有 ” 时,需要使用反斜杠 " 转义
2.判断物体是不是Prefab,PrefabUtility.GetPrefabAssetType(Selection.activeObject) != PrefabAssetType.NotAPrefab
;
3.[MenuItem(MenuItemText2, true)]
第二个参数 bool值表示是否显示本扩展方法
4.获取指定脚本所有的Pulbic
或[SerializeField]
的属性: type.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public)