在unity使用過程中,其實我們都是在各個不同功能的Window下工作。
比如在Scene窗口中操作物體,在Inspector中操作物體屬性,在Game視窗中觀察游戲狀態。
所以窗口是Unity的靈魂,這是唯一接口我們能夠通過它來制作游戲。
那么,我們想自定義一個自己的窗口,那該如何呢?今天我們就來學習下EditorWindow,由於這個類在UnityEdior下,所以要using UnityEditor;
using UnityEngine; using System.Collections; using UnityEditor;//注意要引用 public class MyWindow: EditorWindow { [MenuItem("Window/MyWindow")]//在unity菜單Window下有MyWindow選項 static void Init() { MyWindow myWindow = (MyWindow)EditorWindow.GetWindow(typeof(MyWindow), false, "MyWindow", true);//創建窗口 myWindow.Show();//展示 } }
這是個簡單的創建窗口的代碼,首先通過EditorWindow.GetWindow來取得窗口實例,然后展現,我們來看看官方的API說明。
GetWindow是個靜態方法,有三個參數:
第一個參數是窗口類型,注意是一定要繼承自EditorWindow。
第二個參數是窗口是否浮動,如果是就不能內嵌到unity其他窗口中去,如果不是就能嵌入其他窗口。(可以省略,默認為內嵌入式)
第三個參數是窗口的標題,如果為空的話就采用類的名稱來當標題。(可以省略,默認類的名稱)
可以看到第一個參數是必須的,其他參數可以省略。c#可選參數的特性。
回到Unity,在Window菜單下拉列表選擇MyWindow,可以看到彈出自己的窗口。好開心!
當然這個窗口里面什么東西都沒有,我們需要往里面添加各個功能組件,當然這個以后再慢慢詳談,貌似好像如飢似渴了!!=_=
接着,下面我們來學習下EditorWindow的各個屬性和方法:
fousedWindow(靜態變量):
注意到類型是EditorWindow,所以顧名思義這個是表示當我們聚焦到哪個窗口,那么這個靜態變量就是那個窗口。其實也就是記錄我們聚焦的窗口。
於是我做了個小實驗,功能也就是聚焦哪個窗口會打印這個窗口的信息。
void OnGUI() { EditorGUILayout.LabelField(EditorWindow.focusedWindow.ToString()); }
mouseOverWindow(靜態變量):
與fouseWindow相似,這個是鼠標懸停在哪個Window,這個靜態變量就是那個窗口。
using UnityEngine; using System.Collections; using UnityEditor; public class MyWindow: EditorWindow { string move; [MenuItem("Window/MyWindow")]//在unity菜單Window下有MyWindow選項 static void Init() { MyWindow myWindow = (MyWindow)EditorWindow.GetWindow(typeof(MyWindow), false, "MyWindow", false); myWindow.Show(true); } void OnGUI() { move = EditorWindow.mouseOverWindow ? EditorWindow.mouseOverWindow.ToString() : "Nothing"; EditorGUILayout.LabelField(move); } void OnInspectorUpdate() { if (EditorWindow.mouseOverWindow) EditorWindow.mouseOverWindow.Focus();//就是當鼠標移到那個窗口,這個窗口就自動聚焦 this.Repaint();//重畫MyWindow窗口,更新Label } }
於是我又做了個小測試,功能很簡單,鼠標移動的哪個窗口,在MyWindow打印那個窗口的信息,並且自動聚焦到那個窗口。
反正我是成功了,你們可以自行測試。
EditorWindow.autoRepaintOnSceneChange
當這個變量為true時,如果unity編輯視窗(注意不只是scene視窗,其他窗口)只要有變動,就會重畫窗口,為false就不會。
當然我們做個小測試:
using UnityEngine; using System.Collections; using UnityEditor; public class MyWindow: EditorWindow { int i = 0; [MenuItem("Window/MyWindow")]//在unity菜單Window下有MyWindow選項 static void Init() { MyWindow myWindow = (MyWindow)EditorWindow.GetWindow(typeof(MyWindow), false, "MyWindow", false); myWindow.autoRepaintOnSceneChange = true; myWindow.Show(true); } void OnGUI() { i++; EditorGUILayout.LabelField(i.ToString()); } }
這段代碼就是當我們變動unity編輯器時,label就會顯示i自增。
EditorWindow.maximized
當為true,就是當窗口是內嵌到其他窗口,也就是docked停靠的意思,窗口就能最大化。開上面,他說如果窗口沒有在停靠狀態,那么這個值永遠為false,並且設置無效。
測試的話,我們可以用toggle來改變這個bool值。
using UnityEngine; using System.Collections; using UnityEditor; public class MyWindow: EditorWindow { [MenuItem("Window/MyWindow")]//在unity菜單Window下有MyWindow選項 static void Init() { MyWindow myWindow = (MyWindow)EditorWindow.GetWindow(typeof(MyWindow), false, "MyWindow", false); myWindow.autoRepaintOnSceneChange = true; myWindow.Show(true); } void OnGUI() { maximized = EditorGUILayout.ToggleLeft("Max",maximized); } }
注意一定要是內嵌的狀態下,那么會觀察到點擊Max會最大化,再次點擊會回到原來。
maxSize,minSize,position這里我就不詳細介紹了,自己可以去改變着玩玩。
EditorWindow.titleContent
這個變量是設置窗口的小圖標的,圖標的類型為GUIContent。
我們再來順便學習下GUIContent,看看他的API。
GUIContent.GUIContent
這里我們只看他的構造函數,因為我們只想要圖標,所以我們選擇public GUIContent(Texture image);這個構造函數
不知為何,unity現在沒有titleContent這個變量,那么我們也就不要管他了,影響不是很大。
EditorWindow.wantsMouseMove
也就是設置這個變量為true的時候,這個窗口會接收OnGUI里面的鼠標在窗口上面移動的事件。我們來測試一下:
using UnityEngine; using System.Collections; using UnityEditor; public class MyWindow: EditorWindow { static MyWindow myWindow; [MenuItem("Window/MyWindow")]//在unity菜單Window下有MyWindow選項 static void Init() { myWindow = (MyWindow)EditorWindow.GetWindow(typeof(MyWindow), false, "MyWindow", false); myWindow.Show(true); } void OnEnable() { } void OnGUI() { wantsMouseMove = EditorGUILayout.Toggle("receive mouseMove:", wantsMouseMove);//是否啟用接收鼠標移動事件監聽 EditorGUILayout.LabelField("Mouse Position:", Event.current.mousePosition.ToString()); if (Event.current.type == EventType.mouseMove && wantsMouseMove)//如果是鼠標移動的事件,就重畫窗口 { ///因為上面注意那里有講到:他不會自動調用repaint()方法 Repaint(); } }
運行項目,可以看到,當勾選了recevive mouseMove選項后,label會實時更新鼠標的坐標。
--------------------------------------------------------------------------分隔符-------------------------------------------------------------------------------------------------------
講完了EditorWindow的所有變量之后,接下來我們來學習下他的一些有用的方法。
1.EditorWindow.BeginWindows
顧名思義,函數名為beginWindows,也就是說從這里開始會創建窗口。咦?這里有些童鞋有些疑惑了。EditorWindow本身就是個窗口,這里又有窗口。
那就好玩了,對的。其實也就是窗口中嵌入窗口,就跟我們game視窗,在OnGUI()里面,創建窗口是一個道理。
這里是EditorWindow中創建Gui.window,具體如上圖所示。
這個函數他是需要和EndWindows()配合使用,也就是begin之后必須要End。
具體可以看官方說明:他說GUI.Window內嵌game和editor視窗中表現有所不同。在game中GUI.Window是從你的屏幕中彈出,而在Editor中則是成為你editorwindow的子窗口。
using UnityEngine; using System.Collections; using UnityEditor; public class MyWindow: EditorWindow { static MyWindow myWindow; public Rect windowRect = new Rect(0, 0, 200, 200);//子窗口的大小和位置 [MenuItem("Window/MyWindow")]//在unity菜單Window下有MyWindow選項 static void Init() { myWindow = (MyWindow)EditorWindow.GetWindow(typeof(MyWindow), false, "MyWindow", false); myWindow.Show(true); } void OnEnable() { } void OnGUI() { BeginWindows();//標記開始區域所有彈出式窗口 windowRect = GUILayout.Window(1, windowRect, DoWindow, "子窗口");//創建內聯窗口,參數分別為id,大小位置,創建子窗口的組件的函數,標題 EndWindows();//標記結束 } void DoWindow(int unusedWindowID) { GUILayout.Button("按鈕");//創建button GUI.DragWindow();//畫出子窗口 } }
運行后,可以看到效果,並且這個窗口是可以移動的。
OK,貌似很久沒有更新這篇博客,現在回過頭重新來學習下。
前面我們講到EditorWindow的BeginWindow開始子窗口的編寫,接着我們來看下他的Close()方法。
這個應該是十分簡單,就是我們呢調用Close()的時候,我們創建的窗體就銷毀。在官方API中這樣解釋的:
我們做下簡單的測試:
using UnityEngine; using System.Collections; using UnityEditor; public class MyWindow: EditorWindow { static MyWindow myWindow; [MenuItem("Window/MyWindow")]//在unity菜單Window下有MyWindow選項 static void Init() { myWindow = (MyWindow)EditorWindow.GetWindow(typeof(MyWindow), false, "MyWindow", false); myWindow.Show(); } void OnEnable() { } void OnGUI() { if (GUILayout.Button("關閉窗口")) { myWindow.Close(); } } }
就是我們點擊窗體里面的關閉窗體按鈕的時候,就調用close方法,然后我們創建的窗體就銷毀消失了。
OK,接着我們看下其他方法:
EditorWindow.Focus
窗體聚焦方法,也就是說調用這個方法,就自動聚焦到該窗體。
那么我們也來做個小實驗:
using UnityEngine; using System.Collections; using UnityEditor; public class MyWindow: EditorWindow { static MyWindow myWindow; [MenuItem("Window/MyWindow")]//在unity菜單Window下有MyWindow選項 static void Init() { myWindow = (MyWindow)EditorWindow.GetWindow(typeof(MyWindow), false, "MyWindow", false); myWindow.Show(); } void OnEnable() { } void OnGUI() { EditorGUILayout.LabelField("聚焦窗體名字:"+EditorWindow.focusedWindow.ToString()); } [MenuItem("Custom Editor/Focus Window")] static void FocusWindow() { myWindow.Focus(); } }
也就是我們點擊Custom Editor菜單下面的Focus Window就會聚焦窗體到自己創建的窗體,然后利用EditorWindow的靜態FoursedWindow變量取得,打印在label中。
接着看下個方法:
EditorWindow.ShowNotification
這個方法是顯示消息提示的方法,我們看下官方的API解釋:
做事必須得親自動手試下才能明白他的原理,這里我們編寫下程序實驗下:
using UnityEngine; using System.Collections; using UnityEditor; public class MyWindow: EditorWindow { static MyWindow myWindow; string m_notification = "我是消息內容"; [MenuItem("Window/MyWindow")]//在unity菜單Window下有MyWindow選項 static void Init() { myWindow = (MyWindow)EditorWindow.GetWindow(typeof(MyWindow), false, "MyWindow", false); myWindow.Show(); } void OnEnable() { } void OnGUI() { m_notification = EditorGUILayout.TextField(m_notification); if (GUILayout.Button("顯示消息")) { myWindow.ShowNotification(new GUIContent(m_notification)); } if (GUILayout.Button("不顯示消息")) { myWindow.RemoveNotification(); } } }
下個方法:
EditorWindow.SendEvent
這個方法就是傳遞事件的方法,什么意思,也就是說比如你想在Hierarchy窗口粘貼要復制的Cube,那么這個粘貼他是一個事件,那么正常我們是用鼠標操作粘貼,如果我們想要用代碼控制,就得使用該方法,直接傳遞粘貼事件給Hierarchy窗口就行了。
具體看下官方的解釋:
那么我們先要知道粘貼事件:
EditorGUIUtility.CommandEvent("Paste")
這個方法是通過事件名字取得事件,OK,我們來寫下代碼:
using UnityEngine; using System.Collections; using UnityEditor; public class MyWindow: EditorWindow { static MyWindow myWindow; [MenuItem("Window/MyWindow")]//在unity菜單Window下有MyWindow選項 static void Init() { myWindow = (MyWindow)EditorWindow.GetWindow(typeof(MyWindow), false, "MyWindow", false); myWindow.Show(); } void OnEnable() { } void OnGUI() { if (EditorWindow.focusedWindow.ToString().Trim() == "(UnityEditor.SceneHierarchyWindow)") { EditorWindow.focusedWindow.SendEvent(EditorGUIUtility.CommandEvent("Paste"));//傳遞粘貼的事件 } } }
這段代碼就是如果聚焦的窗口是Hierarchy窗口的話,那么就傳遞粘貼事件給Hierarchy窗口。
所以我們事先復制一個cube,點擊Asset/Copy。然后打開我們的MyWindow,然后點擊Cube,就會發現神奇的事情:
感覺篇幅有點過長,下節繼續。。。。。未完待續
ok,繼續回來,時隔這么久,先自己都快忘記了差不多,只能看之前自己寫的。
EditorWindow.OnHierarchyChange()
這個方法是當Hierarchy視窗改變的時候,會執行這個方法的邏輯。
那么什么時候Hierarchy視窗會改變呢?官方的解釋是這樣的:
Description
Called whenever the scene hierarchy has changed.
This is transform.parent changed, gameObject.name, creating a new game object, etc.
當Transform組件參數改變的時候,這里注意下你在視窗Scene中拖動坐標軸是不會監聽到,只有在Inspector視窗中手動改Transform的值才發生監聽。
還有GameObject的名稱改變會有觸發事件監聽,還有創建,刪除,粘貼等等操作,都會被監聽到。
寫個小例子測試下:
using UnityEngine; using UnityEditor; using System.Collections; public class MyWindow : EditorWindow { static EditorWindow myWindow; [MenuItem("Window/MyWindow")] static void Init() { myWindow = (MyWindow)EditorWindow.GetWindow(typeof(MyWindow), false, "MyWindow", false); myWindow.Show(); } void OnHierarchyChange() { Debug.Log("Sync"); } }
可以看到我創建和刪除都監聽到。
EditorWindow.OnProjectChange()
這個方法和上述類似,是Project視窗發生改變的時候,會執行該方法。
什么時候發生監聽,官方的解釋是:
Description
Called whenever the project has changed.
Typically after import, file moves, etc.
比如說導入文件,移動文件,等等,當然包括刪除文件。
這里我就不演示了,和上述類似。
EditorWindow.OnSelectionChange()
這個方法是當你選擇的物件發生變化(包括Scene,Project和Hierarchy視窗)的時候會執行該方法。