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("Tools/test/test1")] 2 static void Test() 3 {4 Debug.Log("Test");//UnityEngine 5 } 6 [MenuItem("Tools/test/test2")] 7 static void Test2() 8 { 9 Debug.Log("Test"); 10 }
也可以在現有的選單路徑下新增:
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,判定下面的方法能夠執行 else return 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:同步