unity3D编辑器扩展
編輯器擴展只是在編輯項目中運行,發布出來是不會運行的。
固定創建一個文件夾Editor:所有的資源或者代碼都不會被打包進去。
?
01、使用MenuItem添加菜單欄按鈕
? ? ? ?腳本不需要作為組件存在,可以不用繼承MonoBehaviour
? ? ? ?如何刪除引用(每個函數調用的次數):工具-文本編輯器
? ? ? ? ??
? ? ? ? 如果需要顯示行號:
? ? ? ? ? 工具-選項-文本編輯器-C#-行號
?
? ? ? ? ? ??? ? ? ??
?
?引用命名空間:using UnityEditor;
?[MenuItem("Tools/test/test1")]:在菜單欄會有Tools這一欄,點擊顯示test,點擊test顯示test1
1 public class Tools { 2 [MenuItem("Tools/test/test1")] 3 static void Test()//要是靜態方法,不然會出現警告 4 { 5 Debug.Log("Test");//UnityEngine里面的 6 } 7 }?
?[MenuItem("Tools/test/test2")]:test下會有兩個子按鈕test1、test2(但是不能完全一樣,方法名也不能重復)
也可以在現有的菜單路徑下添加:
1 [MenuItem("Window/mytool/test1")] 2 static void Test3() 3 { 4 Debug.Log("Test"); 5 }每個菜單欄里面都有一根橫線進行分類:
? [MenuItem("GameObject/my tool")]//會添加到最下面即第四類
?
為自己添加的菜單欄進行分組(即設置第三個參數,優先級越小越顯示在上面,相鄰的兩個菜單欄(下面是test3 與show info相鄰)的優先級相差11(或者大于11)就會多一個分割線):
unity里面認為優先級相差小就在同一組里面
1 //每一個菜單欄的priority優先級默認為1000,第三個參數 2 [MenuItem("Tools/show info",false,1)] 3 static void Test1() 4 { 5 //Debug.Log(Selection.activeGameObject.name );//是我們第一個選擇的游戲物體 6 //Debug.Log(Selection.objects.Length); 7 8 } 9 //%=ctrl #=shift &=alt 10 [MenuItem("Tools/test2 %q",false,100)] 11 static void Test2() 12 { 13 Debug.Log("Test2"); 14 } 15 [MenuItem("Tools/test3 %t",false,0)] 16 static void Test3() 17 { 18 Debug.Log("Test3"); 19 }?要放在現有菜單欄中間某一組中,要知道每個一組有多少個,一般都猜想。
如果要放在hierarchy或者project等面板右鍵的的彈出框菜單欄里面,也是需要自己根據位置去猜想,第一級菜單看應該放在哪個,比如project對應的菜單是assets。
?
02、給組件的右鍵菜單欄添加按鈕
注意強轉的兩種方式不同:
as強轉時如果后面的類型和要強轉的類型不一樣的時候不會報錯,但是會但會一個空值給前者,使用其就會報空指針的錯誤
而直接加類型強轉就會報錯
?
using System.Collections;using System.Collections.Generic;using UnityEngine;using UnityEditor;public class PlayerEditor {[MenuItem("CONTEXT/PlayerHealth/InitHealthAndSpeed")]// CONTEXT 組件名 按鈕名 系統自動調用下面的函數,編輯器大部分不需要運行時執行static void InitHealthAndSpeed( MenuCommand cmd )//menucommand是當前正在操作的組件(context,userData兩個參數) {//Debug.Log(cmd.context.GetType().FullName);//得到context完整類型名 //Debug.Log(cmd.context.name);//輸出的是組件所在游戲物體的名字CompleteProject.PlayerHealth health = cmd.context as CompleteProject.PlayerHealth;//強制轉化得到當前操作的對象,注意不能在前面加類型強制轉型health.startingHealth = 200;health.flashSpeed = 10;Debug.Log("Init");}//添加清除剛體的的阻力和重力 1e-07約等于1[MenuItem("CONTEXT/Rigidbody/Clear")]//CONTEXT 是固定的static void ClearMassAndGravity( MenuCommand cmd ){Rigidbody rgd = cmd.context as Rigidbody;rgd.mass = 0;rgd.useGravity = false;}}?
?
?03、學習使用selection獲取選擇的游戲物體(成員為靜態,直接用類名訪問,如果沒有選中物體則為空,active開頭的是選擇場景中的游戲物體(在inspector面板上顯示的))
點擊自己添加的按鈕實現將選中的所有游戲物體執行該按鈕對應的功能函數。
?objects參數:獲得當前選擇的游戲物體數組,可以獲得所有選中的游戲物體(包括project)
activeObject參數:在inspector面板上顯示的游戲物體,多選的就會顯示第一個(包括預制體)
activetransform:通過得到的activeobject物體上的transform組件,只能獲取一個
?刪除功能實現:
[MenuItem("GameObject/my delete", false, 11)]static void Mydelete(){foreach (Object o in Selection.objects)//不選擇返回的是空數組{//GameObject.DestroyImmediate(o);//編輯器模式下不能用destory,能用GameObject.DestroyImmediate,刪除之后無法用ctrl+Z無法撤銷Undo.DestroyObjectImmediate(o);//利用Undo進行的刪除操作 是可以撤銷的}//想要有撤銷功能首先需要把刪除操作注冊到 操作記錄里面}給菜單項添加快捷鍵:
[MenuItem("Tools/test3 _t",false,0)]//表示test3的快捷鍵為T,一定要有空格和快捷鍵static void Test3(){Debug.Log("Test3");}?
設置組合快捷鍵:
//%代表ctrl #代表shift &代表alt[MenuItem("Tools/test2 %q",false,100)]static void Test2(){Debug.Log("Test2");}?
?
04、控制菜單項是否啟用的功能(第二個參數,比如實現沒有選擇游戲物體的時候設置上面的mydelete刪除鍵為不能按(不啟用)的狀態)
true表示按鈕對應的這個調用函數是驗證函數。點擊game object會先執行上面的驗證方法,不能按為灰色(不可點擊狀態)
問題:右鍵彈出菜單與game object菜單下的按鈕點擊狀態顯示不一樣?
[MenuItem("GameObject/my delete", true, 11)]//路徑保持一致,第二個參數為true表示是給下面的響應函數作為驗證的,必須要有一個返回值,根據返回值判斷是否能判斷static bool MyDeleteValidate(){if (Selection.objects.Length > 0)return true;//選擇了游戲物體返回true,判定下面的方法能夠執行elsereturn false;}[MenuItem("GameObject/my delete", false, 11)]static void Mydelete(){foreach (Object o in Selection.objects){//GameObject.DestroyImmediate(o);Undo.DestroyObjectImmediate(o);//利用Undo進行的刪除操作 是可以撤銷的}//需要把刪除操作注冊到 操作記錄里面}?
?05、contextmenu和contextmenuitem的使用(給組件添加右鍵,可以直接在組件中使用,不需要引用editor命名空間,在unityEngine命名空間下)
對于系統內置組件無法修改腳本代碼,只能通過context方式添加右鍵功能,對于自己寫的腳本組件即可用以下方法
要給那個組件添加右鍵,就在該組件里面添加方法
[ContextMenu("setColor")]void setColor(){flashColour = setColor.green;}?
API中特性一般是attribute結尾,使用的時候不需要attribute
contextmenuitem給腳本的屬性添加功能。
右鍵添加給腳本組件的變量血量屬性添加增加方法(點擊startingHealth右鍵才有按鈕出現):
[ContextMenuItem("按鈕的名字","執行的方法(必須要存在)")][ContextMenuItem("AddHp","addHp")public int startingHealth = 100;void addHp() { startingHealth +=20;}?
?
?07、創建對話框(有批量修改游戲物體的需求,比如后期游戲對敵人prefabs修改血量什么的屬性且更改的值是不確定的,可以用方法06,可是一個個點也很麻煩,如果能全選之后彈出對話框批量修改屬性)
?可以在彈出的對話框輸入值。創建對話框的類后要創建一個對話框,對話框默認有一個close的按鈕
對話框類:
1 using System.Collections; 2 using System.Collections.Generic; 3 using UnityEngine; 4 using UnityEditor; 5 6 public class EnemyChange : ScriptableWizard {//對話框類要繼承ScriptableWizard 類,定義的字段都會顯示在對話框中 7 //實現統一修改敵人 8 [MenuItem("Tools/CreateWizard")]//創建對話框按鈕,menuItem可以在任何腳本都可以放,最好將其與對應的修改屬性放在一個腳本里 9 static void CreateWizard() 10 { 11 ScriptableWizard.DisplayWizard<EnemyChange>("統一修改敵人","Change And Close","Change");//第二個參數是對話框左下角按鈕的名字,第三個參數是otherButtonName按鈕 12 } 13 //繼承了對話框類腳本中的屬性是會顯示在對話框上的(公有的) 14 public int changeStartHealthValue = 10; 15 public int changeSinkSpeedValue = 1; 16 17 const string changeStartHealthValueKey = "EnemyChange.changeStartHealthValue"; 18 const string changeSinkSpeedValueKey = "EnemyChange.changeSinkSpeedValue"; 19 //當窗口被創建出來的時候調用的 20 void OnEnable() 21 {//第二個參數是傳默認值,因為第一次沒有保存值 22 changeStartHealthValue = EditorPrefs.GetInt(changeStartHealthValueKey, changeStartHealthValue); 23 changeSinkSpeedValue = EditorPrefs.GetInt(changeSinkSpeedValueKey, changeSinkSpeedValue); 24 } 25 //檢測create按鈕的點擊,對話框上面的create(不一定是create,左下角的按鈕)按鈕(點擊會自動調用此函數并關閉對話框) 26 void OnWizardCreate() 27 { 28 GameObject[] enemyPrefabs = Selection.gameObjects;//取得所有選擇的方法,gameobjects包括預制體 29 EditorUtility.DisplayProgressBar("進度", "0/" + enemyPrefabs.Length + " 完成修改值", 0); 30 int count = 0; 31 foreach (GameObject go in enemyPrefabs)//遍歷所有選擇的object 32 { 33 CompleteProject.EnemyHealth hp = go.GetComponent<CompleteProject.EnemyHealth>();//得到選中物體上的要修改屬性所屬的屬性,注意加上正確的命名空間 34 Undo.RecordObject(hp, "change health and speed");//記錄做了哪些更改的函數(第二個參數是記錄撤銷這個步驟的名字,可以隨意更改),撤銷實現,要注意要放在更改對應屬性之前,不然沒有用//修改成我們設置的值 35 hp.startingHealth += changeStartHealthValue; 36 hp.sinkSpeed += changeSinkSpeedValue;//即不能把記錄操作函數(undo)放在這之后 37 count++;//進度條 38 EditorUtility.DisplayProgressBar("進度", count+"/" + enemyPrefabs.Length + " 完成修改值", (float)count/enemyPrefabs.Length); 39 }//代碼清除進度條 40 EditorUtility.ClearProgressBar();//顯示提示信息,對話框被關閉也會關閉 41 ShowNotification(new GUIContent(Selection.gameObjects.Length + "個游戲物體的值被修改了")); 42 }//otherButtonName按鈕響應函數 43 void OnWizardOtherButton() 44 { 45 OnWizardCreate(); 46 }47 //當前字段值修改的時候會被調用 48 void OnWizardUpdate() 49 {//設置為空的原因:保證每次兩個提示信息都要實時更新(因為原先沒有選擇敵人的時候會出現錯誤提示而沒有幫助提示,選擇一個之后會顯示幫助提示而錯誤提示沒有更新) 50 errorString = null; 51 helpString = null; 52 if (Selection.gameObjects.Length > 0) 53 { 54 helpString = "您當前選擇了" + Selection.gameObjects.Length + "個敵人";//設置幫助提示,如果想要實時顯示當前選擇的敵人人數 55 } 56 else 57 { 58 errorString = "請選擇至少一個敵人"; 59 } 60 //本地保存對話框修改的值 61 EditorPrefs.SetInt(changeStartHealthValueKey, changeStartHealthValue); 62 EditorPrefs.SetInt(changeSinkSpeedValueKey, changeSinkSpeedValue); 63 }//當選擇的物體發生改變調用的函數 64 void OnSelectionChange() 65 { 66 OnWizardUpdate(); 67 } 69 }對話框中一些有用的方法屬性:
? 三個事件方法(untiy會自動幫我們調用):
void OnWizardCreate() :檢測create按鈕的點擊,對話框上面的create(不一定是create,左下角的按鈕)按鈕(點擊會自動調用此函數并關閉對話框) void OnWizardUpdate() :面板被創建(對話框彈出來的時候)會被調用一次,當前字段值修改的時候會被調用(一修改就調用) void OnWizardOtherButton() :otherButtonName按鈕響應函數 顯示提示信息字段: errorString?:顯示錯誤信息 helpString:顯示提示信息對話框默認只有一個按鈕,如果想要設置多個按鈕: ScriptableWizard DisplayWizard (title : string, klass : System.Type, createButtonName : string = "Create", otherButtonName : string = "")?:創建一個按鈕? ?顯示進度條:
? ??DisplayCancelableProgressBar:能夠點擊取消的進度條
? ? ?
? ??DisplayProgressBar:只能由代碼清除的進度條
??
?08.創建自定義窗口
1 using System.Collections; 2 using System.Collections.Generic; 3 using UnityEngine; 4 using UnityEditor; 5 //要繼承editorWindow 6 public class MyWindow : EditorWindow { 7 8 [MenuItem("Window/show mywindow")] 9 static void ShowMyWindow() 10 { 11 MyWindow window= EditorWindow.GetWindow<MyWindow>(); 12 window.Show(); 13 }14 //如果想要在窗口中添加東西,使用onGUI繪制 15 private string name=""; 16 void OnGUI() 17 { 18 GUILayout.Label("這是我的窗口"); 19 name = GUILayout.TextField(name); 20 if (GUILayout.Button("創建")) 21 { 22 GameObject go = new GameObject(name);//創建游戲物體 23 Undo.RegisterCreatedObjectUndo(go, "create gameobject");//添加到unity操作記錄,方便撤銷 24 } 25 } 26 27 }?
?平常使用的(持續添加):
1.RangeAttribute
在int或者float類型上使用,限制輸入值的范圍
public class TestRange : MonoBehaviour {[Range(0, 100)] public int HP; }2.[SerializeField,Tooltip("前景動畫視頻名字需加后綴")]:序列化并在Inspector面板上添加注釋(鼠標放上去會顯示Tooltip文字)
3.HeaderAttribute:在一些字段前面加上注釋,顯示在inspector面板上該屬性的上面
1 using UnityEngine; 2 using System.Collections; 3 4 public class ExampleClass : MonoBehaviour { 5 [Header("Health Settings")] 6 public int health = 0; 7 public int maxHealth = 100; 8 [Header("Shield Settings")] 9 public int shield = 0; 10 public int maxShield = 0; 11 }?4.[HideInInspector] :在ispector面板上隱藏
5.RequireComponent:當你添加一個腳本,使用requirecomponent到一個游戲對象,所需的組件將自動被添加到游戲對象。
using UnityEngine;// PlayerScript requires the GameObject to have a Rigidbody component [RequireComponent(typeof(Rigidbody))] public class PlayerScript : MonoBehaviour {Rigidbody rb;void Start(){rb = GetComponent<Rigidbody>();}void FixedUpdate(){rb.AddForce(Vector3.up);} }?
6.TextAreaAttribute:屬性使一個字符串被編輯以高度靈活和可滾動文本區域。
using UnityEngine;public class TextAreaExample : MonoBehaviour {[TextArea]public string MyTextArea; }?
7.RPC:同步
?
總結
以上是生活随笔為你收集整理的unity3D编辑器扩展的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: wan口已断开 服务器未响应,路由器设置
- 下一篇: 【Mendax1234】Thinkpad