初步學習Unity EditorWindow 的擴充
案例1.在代碼中進行GameObject生成和指派儲存預知體 下面有GIF示範圖檔
- 生成Gameobject 并對其進行指派
- 序列化
- 生成Prefabs
- 編輯器視窗 EditorWindow
建立UIRoot類
using UnityEngine;
public class UIRoot : MonoBehaviour
{
public Transform bg;
public Transform common;
public Transform uiPop;
public Transform forward;
[SerializeField]
private Canvas mRootCanvas;
}
生成Canvas 等。。
using System.IO;
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using UnityEngine.EventSystems;
using UnityEngine.UI;
public class CreateUIRootPanel : EditorWindow
{
static CreateUIRootPanel window;
[MenuItem("編輯器擴充/1.SetUp(UIRoot)", true)]
static bool ValidateUIRoot()
{
return !GameObject.Find("UIRoot");
}
[MenuItem("編輯器擴充/1.SetUp(UIRoot)", false)]
static void MenuItem()
{
window = GetWindow<CreateUIRootPanel>();
window.Show();
}
private string mWidth = "720";
private string mHeight = "1280";
private void OnGUI()
{
GUILayout.BeginHorizontal();
//var width = GUILayout.TextField("width", GUILayout.Width(50));
mWidth = GUILayout.TextField(mWidth, GUILayout.Width(50));
GUILayout.Label("X", GUILayout.Width(15));
mHeight = GUILayout.TextField(mHeight, GUILayout.Width(55));
GUILayout.EndHorizontal();
if (GUILayout.Button("Build"))
{
Debug.Log("build");
SetUP(float.Parse(mWidth), float.Parse(mHeight));
// window.Close();
}
}
public static void SetUP(float width, float height)
{
Debug.Log("編輯器擴充");
// GameObject go = Resources.Load<GameObject>("UIroot");
// GameObject.Instantiate(go);
var UIRootObj = new GameObject("UIRoot");
UIRootObj.layer = LayerMask.NameToLayer("UI");
UIRoot UIScript = UIRootObj.AddComponent<UIRoot>();
var canvas = new GameObject("Canvas");
canvas.transform.SetParent(UIRootObj.transform);
canvas.layer = LayerMask.NameToLayer("UI");
canvas.AddComponent<Canvas>().renderMode = RenderMode.ScreenSpaceOverlay;
//canvas.AddComponent<CanvasGroup>();
//CanvasScaler
CanvasScaler canvasScaler = canvas.AddComponent<CanvasScaler>();
canvasScaler.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize;
canvasScaler.referenceResolution = new Vector2(width, height);
canvas.AddComponent<GraphicRaycaster>();
//EventSystem
var eventSystem = new GameObject("EventSystem");
eventSystem.layer = LayerMask.NameToLayer("UI");
eventSystem.transform.SetParent(UIRootObj.transform);
eventSystem.AddComponent<EventSystem>();
eventSystem.AddComponent<StandaloneInputModule>();
//BG
var bgObj = new GameObject("Bg");
bgObj.AddComponent<RectTransform>();
bgObj.transform.SetParent(canvas.transform);
bgObj.transform.localPosition = Vector3.zero;
UIScript.bg = bgObj.transform;
//Common
var commonObj = new GameObject("Common");
commonObj.AddComponent<RectTransform>();
commonObj.transform.SetParent(canvas.transform);
commonObj.transform.localPosition = Vector3.zero;
UIScript.common = commonObj.transform;
//PopUI
var popUI = new GameObject("PopUI");
popUI.AddComponent<RectTransform>();
popUI.transform.SetParent(canvas.transform);
popUI.transform.localPosition = Vector3.zero;
UIScript.uiPop = popUI.transform;
//ForwardObj
var forwardObj = new GameObject("Forward");
forwardObj.AddComponent<RectTransform>();
forwardObj.transform.SetParent(canvas.transform);
forwardObj.transform.localPosition = Vector3.zero;
UIScript.forward = forwardObj.transform;
///序列化腳本 添加 UIRoot中的Private的值
var uirootScriptSerializedObj = new SerializedObject(UIScript);
uirootScriptSerializedObj.FindProperty("mRootCanvas").objectReferenceValue = canvas.GetComponent<Canvas>();
uirootScriptSerializedObj.ApplyModifiedProperties();
var saveFolder = Application.dataPath + "/Resources";
if (!Directory.Exists(saveFolder))
{
Directory.CreateDirectory(saveFolder);
}
var saveFilePath = saveFolder + "/UIRoot.Prefab";
// PrefabUtility.CreatePrefab(saveFilePath, UIRootObj);
bool IsLoad = false;
PrefabUtility.SaveAsPrefabAsset(UIRootObj, saveFilePath, out IsLoad);
Debug.Log(string.Format(" 儲存預知體[{0}]", IsLoad ? "成功" : "失敗"));
}
}
案例1最終效果

案例2 代碼生成器 下面有GIF示範圖檔
- 在Hierarchy生成腳本指令菜單
- 生成腳本
- 添加标簽
- 搜尋并且生成功能
- 生成代碼分為兩個腳本
- 命名空間的設定 (包括在windows視窗進行設定命名空間)
- 生成路徑選擇路徑修改路徑和儲存路徑
- 建立Prefab
- 觸發自動編譯
- 快捷鍵的支援
- 生成腳本的元件類型指定
- 自定義面闆
主要的生成代碼腳本 CreateComponentCode 在Editor檔案夾下
using System;
using System.Linq;
using System.IO;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using UnityEditor.Callbacks;
using EditorExtension;
public class CreateComponentCode : EditorWindow
{
[MenuItem("編輯器擴充/2.Build 配合 GameObject的@EdtiorExtemsion-CreateCode使用 %#T", false, 2 )]
public static void OpenMenu()
{
GetWindow<CreateComponentCode>().Show();
}
[MenuItem("GameObject/@(Alt+A)EdtiorExtemsion-Add View %A", false, 0)]
public static void AddView()
{
GameObject currentGameObject = Selection.gameObjects.First();
if (currentGameObject == null)
{
Debug.LogError("請選擇Gameobject");
return;
}
var view = currentGameObject.GetComponent<CodeGenderateInfo>();
if (!view)
{
currentGameObject.AddComponent<CodeGenderateInfo>();
}
}
[MenuItem("GameObject/@(Alt+B)EdtiorExtemsion-Bind %B", false, 0)]
public static void Bind()
{
GameObject currentGameObject = Selection.gameObjects.First();
if (currentGameObject == null)
{
Debug.LogError("請選擇Gameobject");
return;
}
var view = currentGameObject.GetComponent<UIMark>();
if (!view)
{
currentGameObject.AddComponent<UIMark>();
}
}
private void OnGUI()
{
GUILayout.BeginHorizontal();
GUILayout.Label("NameSpace");
NamespaceSettingsData.Namespace = GUILayout.TextField(NamespaceSettingsData.Namespace);
GUILayout.EndHorizontal();
}
static List<UIMarkInfo> uIMarkInfos = new List<UIMarkInfo>();
[MenuItem("GameObject/@(Alt+C)EdtiorExtemsion-CreateCode %C", true, 0)]
static bool ValidateCreateCode()
{
Debug.Log("Create Code");
GameObject currentGameObject = Selection.gameObjects.First();
if (currentGameObject == null)
{
Debug.LogError("請選擇Gameobject");
return false;
}
return currentGameObject.GetComponent<ViewMark>();
}
[MenuItem("GameObject/@EdtiorExtemsion-CreateCode", false, 0)]
static void CreateCode()
{
Debug.Log("Create Code");
GameObject currentGameObject = Selection.gameObjects.First();
if (currentGameObject == null)
{
Debug.LogError("請選擇Gameobject");
return;
}
var mName = currentGameObject.name;
var scriptsFolder = Application.dataPath + "/Scripts";
var generateInfo = currentGameObject.GetComponent<CodeGenderateInfo>();
if (generateInfo != null)
{
scriptsFolder = generateInfo.ScriptsFolder;
}
if (!Directory.Exists(scriptsFolder))
{
Directory.CreateDirectory(scriptsFolder);
}
uIMarkInfos.Clear();
//搜尋所有綁定
SearchBinds("", currentGameObject.transform, uIMarkInfos);
ComponentTemplate.Write(mName, scriptsFolder);
ComponentDesignerTemplate.Write(mName, scriptsFolder, uIMarkInfos);
EditorPrefs.SetString("Load", mName);
//重新整理目錄
AssetDatabase.Refresh();
}
static void SearchBinds(string basePath, Transform transform, List<UIMarkInfo> uIMarks)
{
var uiMark = transform.GetComponent<UIMark>();
var isRoot = string.IsNullOrEmpty(basePath);
if (uiMark && !isRoot)
{
uIMarks.Add(new UIMarkInfo() { FindPath = basePath, Name = transform.name, ComponentName = uiMark.ComponentName });
}
foreach (Transform childTrans in transform)
{
SearchBinds(isRoot ? childTrans.name : basePath + "/" + childTrans.name, childTrans, uIMarks);
}
}
//系統的事件 DidReloadScripts 編譯完成以後就會調用
//将此屬性添加到方法中,以便在重新加載腳本後獲得通知。
//DidReloadScript有一個提供訂單索引的選項。這允許您更改調用回調的順序。(Builtin回調的值始終為0)。
[DidReloadScripts]
static void AddComponent2GameObject()
{
Debug.Log("DidReloadScripts" + System.DateTime.Now);
string generateClassGame = EditorPrefs.GetString("Load");
Debug.Log(generateClassGame);
if (string.IsNullOrWhiteSpace(generateClassGame))
{
Debug.Log("不進行操作");
EditorPrefs.DeleteKey("Load");
}
else
{
Debug.Log("繼續操作");
// 1.擷取目前項目中所有的 assembly (可以了解為 代碼編譯好的 dll)
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
// 2.擷取編輯器環境(dll)
var defaultAssetBly = assemblies.First(assembly => assembly.GetName().Name == "Assembly-CSharp");
//添加Namespace
var typeName = NamespaceSettingsData.Namespace + "." + generateClassGame;
Debug.Log(typeName);
Type type = defaultAssetBly.GetType(typeName);
Debug.Log(type);
if (type == null)
{
Debug.Log("編譯失敗~");
return;
}
var currentGameObject = GameObject.Find(generateClassGame);
var scriptComponent = currentGameObject.GetComponent(type);
if (!scriptComponent)
{
scriptComponent = currentGameObject.AddComponent(type);
}
var serialiedScript = new SerializedObject(scriptComponent);
uIMarkInfos.Clear();
//搜尋所有綁定
SearchBinds("", currentGameObject.transform, uIMarkInfos);
foreach (UIMarkInfo markInfo in uIMarkInfos)
{
// var name=
var name =markInfo.Name;
serialiedScript.FindProperty(name).objectReferenceValue = currentGameObject.transform.Find(markInfo.FindPath).gameObject;
Debug.Log(markInfo.FindPath);
}
var codeGenerateInfo = currentGameObject.GetComponent<CodeGenderateInfo>();
if (codeGenerateInfo != null)
{
serialiedScript.FindProperty("ScriptsFolder").stringValue = codeGenerateInfo.ScriptsFolder;
var generatePrefab = codeGenerateInfo.GeneratePrefab;
var prefabForder = codeGenerateInfo.PrefabFolder;
serialiedScript.FindProperty("PrefabFolder").stringValue = prefabForder;
serialiedScript.FindProperty("GeneratePrefab").boolValue = generatePrefab;
var fullPrefabFolder = prefabForder.Replace("Assets", Application.dataPath);
serialiedScript.ApplyModifiedPropertiesWithoutUndo();
if (codeGenerateInfo.GetType() == type)
{
}
else
{
DestroyImmediate(codeGenerateInfo);
}
if (generatePrefab)
{
if (!Directory.Exists(fullPrefabFolder))
{
Directory.CreateDirectory(fullPrefabFolder);
}
// bool isFinish = false; //兩種方法都可以
// PrefabUtility.SaveAsPrefabAsset(currentGameObject, fullPrefabFolder + "/" + currentGameObject.name + ".Prefab", out isFinish);
// Debug.LogFormat("儲存預制體【{0}】", isFinish ? "成功" : "失敗");
PrefabUtility.SaveAsPrefabAssetAndConnect(currentGameObject, fullPrefabFolder + "/" + currentGameObject.name + ".prefab", InteractionMode.AutomatedAction);
}
}
else
{
serialiedScript.FindProperty("ScriptsFolder").stringValue = "Assets/Scripts";
serialiedScript.ApplyModifiedPropertiesWithoutUndo();
}
AssetDatabase.Refresh();
EditorPrefs.DeleteKey("Load");
}
}
}
public class ComponentDesignerTemplate
{
public static void Write(string name, string scriptsFolder, List<UIMarkInfo> uIMarkInfos)
{
var scriptFile = string.Format("{0}/{1}.Designer.cs", scriptsFolder, name);
// if (File.Exists(scriptFile))
// {
// File.Delete(scriptFile);
// }
StreamWriter writer = File.CreateText(scriptFile);
writer.WriteLine("\tusing UnityEngine;");
writer.WriteLine();
writer.WriteLine("\t//1.請在菜單編輯器擴充/NamespaceSetting 設定命名空間");
writer.WriteLine("\t//2.命名空間更改後 生成代碼後 需要吧邏輯代碼檔案(非Designer)的命名空間手動更改");
writer.WriteLine($"\tnamespace {NamespaceSettingsData.Namespace}");
writer.WriteLine("\t{");
writer.WriteLine($"\t\tpublic partial class {name} ");
writer.WriteLine("\t\t{");
foreach (UIMarkInfo uiMarkInfo in uIMarkInfos)
{
writer.WriteLine($"\t\tpublic {uiMarkInfo.ComponentName} { uiMarkInfo.Name};");
}
writer.WriteLine();
writer.WriteLine("\t\t}");
writer.WriteLine("\t}");
writer.Close();
}
}
public class ComponentTemplate
{
public static void Write(string name, string scriptsFolder)
{
var scriptFile = string.Format("{0}/{1}.cs", scriptsFolder, name);
if (File.Exists(scriptFile))
{
return;
}
StreamWriter writer = File.CreateText(scriptFile);
//保證每次生成的東西都不一樣 就會執行編譯操作
writer.WriteLine($"//Generate ID:{Guid.NewGuid().ToString()}");
writer.WriteLine("\tusing UnityEngine;");
writer.WriteLine("\tusing EditorExtension;");
writer.WriteLine();
writer.WriteLine("\t//1.請在菜單編輯器擴充/NamespaceSetting 設定命名空間");
writer.WriteLine("\t//2.命名空間更改後 生成代碼後 需要吧邏輯代碼檔案(非Designer)的命名空間手動更改");
writer.WriteLine($"\tnamespace {NamespaceSettingsData.Namespace} ");
writer.WriteLine("\t{");
writer.WriteLine($"\t\tpublic partial class {name} : CodeGenderateInfo");
writer.WriteLine("\t\t{");
writer.WriteLine("\t\tvoid Start()");
writer.WriteLine("\t\t{");
writer.WriteLine("\t\t//Code here");
writer.WriteLine("\t\t}");
writer.WriteLine("\t\t}");
writer.WriteLine();
writer.WriteLine("\t}");
writer.Close();
}
}
public class UIMarkInfo
{
public string FindPath;
public string ComponentName;
public string Name;
}
public class NamespaceSettingsData
{
private readonly static string NAMESPACE_KEY = Application.productName + "@NAMESPACE";
public static string Namespace
{
get
{
var retNamespace = EditorPrefs.GetString(NAMESPACE_KEY, "DefaultNamespace");
return string.IsNullOrEmpty(retNamespace) ? "DefaultNamespace" : retNamespace;
}
set { EditorPrefs.SetString(NAMESPACE_KEY, value); }
}
public static bool IsDefaultNamespace => Namespace == "DefaultNamespace";
}
UIMark腳本 标記
using UnityEngine;
[AddComponentMenu("EditorExension/Mark")]
public class UIMark : MonoBehaviour
{
public enum GetComponentType
{
Transform,
MeshRenderer,
RectTransform,
Rigibody,
BoxCollider,
MeshCollider,
}
public GetComponentType componentType = GetComponentType.Transform;
public string ComponentName
{
get
{
// if (GetComponent<MeshRenderer>())
// {
// return "MeshRenderer";
// }
return componentType.ToString();
}
}
}
ViewMark Gameobject必須挂載 ViewMark才能進行生成腳本
using UnityEngine;
public class ViewMark : MonoBehaviour
{
}
生成腳本的配置腳本 腳本路徑 生成prefab 生成brefab的路徑
using UnityEngine;
namespace EditorExtension
{
[ExecuteInEditMode]
public class CodeGenderateInfo : MonoBehaviour
{
[HideInInspector]
public string ScriptsFolder;
[HideInInspector]
public string PrefabFolder;
[HideInInspector]
public bool GeneratePrefab = false;
private void Awake()
{
ScriptsFolder = "Assets/Scripts";
PrefabFolder = "Assets/Prefabs";
}
}
}
修改挂載代碼的腳本樣式 依然放在Editor下
using UnityEditor;
using UnityEngine;
namespace EditorExtension
{
[CustomEditor(typeof(CodeGenderateInfo), editorForChildClasses: true)]
public class CodeGenerateInfoInspector : Editor
{
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
var codeGenerateInfo = target as CodeGenderateInfo;
GUILayout.BeginVertical("box");
GUILayout.Label("代碼生成部分", new GUIStyle()
{
fontSize = 20,
fontStyle = FontStyle.Bold
});
GUILayout.BeginHorizontal();
GUILayout.Label("Scripts Generate Folder : ", GUILayout.Width(150));
codeGenerateInfo.ScriptsFolder = GUILayout.TextField(codeGenerateInfo.ScriptsFolder);
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal();
GUILayout.Label("GeneratePrefab :", GUILayout.Width(150));
codeGenerateInfo.GeneratePrefab = GUILayout.Toggle(codeGenerateInfo.GeneratePrefab, "Generete Perfab");
GUILayout.EndHorizontal();
if (codeGenerateInfo.GeneratePrefab)
{
GUILayout.BeginHorizontal();
GUILayout.Label("Prefab Generate Folder : ", GUILayout.Width(150));
codeGenerateInfo.PrefabFolder = GUILayout.TextField(codeGenerateInfo.PrefabFolder);
GUILayout.EndHorizontal();
}
GUILayout.EndVertical();
}
}
}
案例2最終效果如下:
案例3重新開機項目 下面有GIF示範圖檔
using UnityEditor;
using UnityEngine;
public class ReopenProject
{
[MenuItem("編輯器擴充/3.(Alt+R)ReopenProject &R", false, 3)]
static void DOReopenProject()
{
EditorApplication.OpenProject(Application.dataPath.Replace("Assets", string.Empty));
}
}