原文地址:http://www.unity.5helpyou.com/2803.html
本篇unity教程我們來學習下編輯器擴展在unity3d開發中的應用!
提到擴展程序,相信大家都不會陌生了。不管是Google的Chrome瀏覽器還是經典的FireFox,這些瀏覽器最為人所稱道的就是它支持各種各樣的擴展。擴展程序是一種插件,它遵循插件式設計的原則,可以隨時在宿主程序中安裝、卸載而不會影響宿主程序的正常運行。我們知道在Unity3d中有各種各樣的插件,如NGUI、2DToolKit、EasyTouch等等都是一種擴展程序。擴展程序在豐富宿主程序功能的基礎上,可以幫助宿主程序完成大量額外的工作。可以說正是因為Unity3d擁有大量的插件和資源支持,Unity3d才能夠受到大家如此的追捧。可是作為一個有節操的程序員,如果僅僅會使用工具,那么我們和普通用戶有什么區別啊,所以在今天的文章中博主將通過三個具體的實例來教大家如何為Unity3D的編輯器開發擴展程序,希望對大家學習Unity3d技術有所幫助!
常用的命名空間和類
開發Unity3D編輯器擴展程序的命名空間主要是UnityEditor,在該命名空間下常用的類有EditorGUI、EditorGUILayout、EditorWindow(可能還有其它的類,不過到目前為止博主就用過這些,如果有其它的類,歡迎大家來補充啊)。為Unity3D編輯器開發的擴展程序同樣是一種腳本,通常需要將腳本文件放在項目資源文夾下的Editor文件夾中,即Assets/Editor。不過該腳本不再繼承自MonoBehaviour,具體的內容我們會放到后面的實例中來講Unity3D編輯器擴展程序的形式通常有兩種,一種是沒有界面的(如案例1)、一種是有界面的(如案例2、案例3)。對於沒有界面的這種擴展程序,我們只需要定義一個類(無需繼承任何父類)然后再這個類中定義一個靜態的方法就可以了;而對於有界面的這種擴展程序,我們需要讓定義的這個類繼承EditorWindow並實現OnGUI()方法,因為在OnGUI()方法中我們將會對擴展程序的界面進行繪制,不過無需擔心啦,因此擴展程序的界面繪制和Unity3D腳本中的OnGUI()方法是相似的,我們要做的就是要熟悉常見的控件。好了,下面進入今天的實戰環節,大家准備好了嗎?
Unity3D編輯器擴展程序開發實例
案例1 快速修改貼圖類型
Unity3D4.6版本的一個重要更新就是UGUI和Unity2D的支持,因為有了對Unity2D的支持,所以Unity3D的貼圖類型就增加了一個Sprite的類型。如果導入到Unity3D中的貼圖是那種打好的小圖的圖集,那么Unity3D能夠自動識別為Sprite類型。可是對於那種單張的貼圖,Unity3D默認還是按照默認的設置來處理,因此如果每次需要用到這些圖片,就必須手動地將其TextureType設為sprite,如果貼圖數量比較少,那么手動修改也沒有什么了。可是如果項目中的貼圖數量較多的話,這樣一張一張地去調整TextureType可能會浪費大量的時間啊!怎么辦呢?簡單!寫代碼!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
using
UnityEngine;
using
UnityEditor;
using
System.Collections;
public
class
ImportSprite
{
/// <summary>
/// 批量將貼圖格式轉換為Sprite
/// </summary>
[MenuItem(
"Tools/ConvertToSprite"
)]
static
void
ConvertToSprite()
{
//獲取所有被選中的物體
Object[] selection=(Object[])Selection.objects;
//合法性處理
if
(selection.Length==0)
return
;
//批量導入貼圖
foreach
(Object obj
in
selection)
{
//取得每一張貼圖
Texture texture=(Texture)obj;
//獲得貼圖路徑
string
localpath=AssetDatabase.GetAssetPath(texture);
//貼圖導入
TextureImporter importer=(TextureImporter)AssetImporter.GetAtPath(localpath);
//設置貼圖類型
importer.textureType=TextureImporterType.Sprite;
//導入項目資源
AssetDatabase.ImportAsset(localpath);
}
//刷新項目資源
AssetDatabase.Refresh();
}
}
|
我們將這個腳本放到Editor文件夾中,如果不出現什么意外的話,Unity3D的菜單欄中會增加一個Tools的菜單項,該菜單項目前只有一個子菜單項ConvertToSprite。好了,現在我們要做的事情就是在項目資源文件夾中選中要轉換成sprite類型的貼圖,然后單擊Tools->ConvertToSprite。很快(具體有多快可以自己在編輯器窗口中去嘗試,總之就是很快就對了,哈哈)所有的貼圖的都如我們所願地被轉換成了sprite類型,此時此刻你有沒有懊悔當年手動創建的92個空物體,反正博主是后悔當初做塔防游戲的時候手動創建了92個空物體,如果那個時候我知道Unity3D可以做這些事情,我打死都不會手動去創建92個空物體的,現在想想都佩服當時自己的勇氣啊。好了,作為第一個編輯器擴展程序,我們稍微總結下主要的內容:
- 在Unity3D中我們可以通過TextureImporter、ModelImporter、AudioImporter、MovieImporter等來分別向Unity3D中導入貼圖、模型、音頻、視頻等等,經過設置后最終通過AssetDatabase.ImportAsset()來將其添加到項目中熱完全,最后需要使用AssetDatabase.Refresh()方法來刷新本地資源,使導入的資源生效。
- Selection.objects取得的物體無法區分是從場景中選取的還是從項目資源文件夾中選取的,如果需要從場景中來選取,建議使用Selection.transforms來代替。
案例2 動態生成Prefab
首先讓我們來回顧一下大家平時制作Prefab的流程:
- 在項目資源文件夾中選取素材拖放到場景中
- 在場景中調整名稱、位置、縮放、組件等等
- 將物體拖放到Prefabs文件夾下生成Prefab
盡管這是Unity3D官方推薦的一種做法,可是如果我們現在有大量的Prefab要制作怎么辦呢?一個最直觀的例子就是游戲里的敵人。在一個中等規模的游戲中,敵人的種類通常很多,而且每一個敵人的行為可能都不相同。然后從宏觀的角度來看,敵人的大部分特征都是相同的,因此我們這里考慮使用程序動態生成Prefab,這里假定Prefab不需要附加腳本,因為如何給Prefab附加腳本博主還沒有研究出來。好了,下面我們來看代碼:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
|
using
UnityEngine;
using
UnityEditor;
using
System.Collections;
public
class
PrefabWrap : EditorWindow {
//預設物體名稱
private
string
prefabName;
//預設物體tag
private
static
string
prefabTag;
//預設物體Layer
private
static
int
prefabLayer;
//當前插件窗口實例
private
static
PrefabWrap instance;
/// <summary>
/// 顯示插件窗口
/// </summary>
[MenuItem(
"Tools/PrefabWrapTool"
)]
static
void
PrefabWrapTool()
{
//獲取當前窗口實例
instance=EditorWindow.GetWindow<PrefabWrap>();
//顯示窗口
instance.Show();
}
/// <summary>
/// 在OnGUI方法中實現界面定制
/// </summary>
private
void
OnGUI()
{
//繪制一個文本框
prefabName=EditorGUILayout.TextField(
"預設物體名稱:"
,prefabName);
//繪制預設物體標簽選擇框
prefabTag=EditorGUILayout.TagField(
"預設物體tag:"
,prefabTag);
//繪制預設物體層級選擇框
prefabLayer=EditorGUILayout.LayerField(
"預設物體Layer:"
,prefabLayer);
//繪制一個按鈕
if
(GUILayout.Button(
"生成預設物體"
,GUILayout.Height(20)))
{
if
(prefabName!=
string
.Empty)
{
CreatePrefab(prefabName);
}
}
}
/// <summary>
/// 批量創建Prefab
/// </summary>
static
void
CreatePrefab(
string
name)
{
//獲取所有被選中的物體
Object[] selection=(Object[])Selection.objects;
//合法性處理
if
(selection.Length==0)
return
;
//批量處理
foreach
(Object obj
in
selection)
{
//生成預設
GameObject prefab=(GameObject)PrefabUtility.CreatePrefab(
"Assets/Prefabs/"
+name+
".prefab"
,(GameObject)obj);
//設置tag和Layer
prefab.tag=prefabTag;
prefab.layer=prefabLayer;
//導入項目
AssetDatabase.ImportAsset(AssetDatabase.GetAssetPath(prefab));
}
//刷新本地資源
AssetDatabase.Refresh();
}
//當界面發生變化時重繪
void
OnInspectorUpdate()
{
Repaint();
}
}
|
首先我們讓這個腳本繼承自EditorWindow,這樣它將在Unity3D中顯示一個窗口。在OnGUI()方法中我們定義了窗口需要繪制的內容為一個文本框、兩個選擇框和一個按鈕,當單擊按鈕后會執行CreatePrefab()方法。當界面發生變化的時候,需要對窗口進行重繪。最終的程序演示效果如下:
當我們在場景中選擇好物體后,只要填好預設物體的名稱、tag、Layer就可以直接生成Prefab了,不過這里有個問題,因為生成Prefab必須要傳入一個GameObject,因此如果直接選擇項目資源文件夾里的內容可能會報錯,因為你選擇的不是一個GameObject。博主做這樣一個功能的初衷原本是想直接為每一個精靈圖片生成預設文件,現在看來需要尋找其它的方法了,不過基本思路是創建一個空物體,然后向這個空物體中增加子物體,如果大家對此有興趣的話,可以結合本文的方法自行去嘗試。
案例3 快速為Sprite設置圖集tag
接下來這個案例呢,同樣是和貼圖有關的內容。我們知道在沒有UGUI以前,我們使用NGUI的時候要做的第一件事情就是把要用到的貼圖打成圖集,現在在Unity3D里面我們可以通過貼圖的Packing Tag來實現圖集打包,就是說具有相同Packing Tag的物體會被打到一張大圖上,這樣做的好處是節省資源。如果大家對這部分內容不太熟悉,可以了解下我的這篇文章。既然明白了原理,那么我們為什么不來嘗試着通過程序將這件事情一次完成呢?好了,直接給出代碼:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
|
using
UnityEngine;
using
UnityEditor;
using
System.Collections;
public
class
PackageTools : EditorWindow
{
/// <summary>
/// 圖集標簽
/// </summary>
private
string
tagName;
/// <summary>
/// 當前實例
/// </summary>
private
static
PackageTools instance;
/// <summary>
/// 在OnGUI方法中實現界面定制
/// </summary>
private
void
OnGUI()
{
//繪制一個文本框
tagName=EditorGUILayout.TextField(
"PackageTagName:"
,tagName);
//繪制一個按鈕
if
(GUILayout.Button(
"Package"
,GUILayout.Height(20)))
{
if
(tagName!=
string
.Empty)
{
PackgeTextureWidthTag(tagName);
}
}
}
/// <summary>
/// 顯示插件窗口
/// </summary>
[MenuItem(
"Tools/ShowPackageTools"
)]
static
void
ShowPackageTools()
{
//獲取當前窗口實例
instance=EditorWindow.GetWindow<PackageTools>();
//顯示窗口
instance.Show();
}
/// <summary>
/// 快速為圖片生成圖集
/// </summary>
static
void
PackgeTextureWidthTag(
string
tagName)
{
//獲取所有被選中的物體
Object[] selection=(Object[])Selection.objects;
//合法性處理
if
(selection.Length==0)
return
;
//批量處理貼圖
foreach
(Object obj
in
selection)
{
//取得每一張貼圖
Texture texture=(Texture)obj;
//獲得貼圖路徑
string
localpath=AssetDatabase.GetAssetPath(texture);
//貼圖導入
TextureImporter importer=(TextureImporter)AssetImporter.GetAtPath(localpath);
//判斷貼圖類型,只有貼圖類型為Sprite且精靈類型為SpriteMode
if
(importer.textureType==TextureImporterType.Sprite)
{
importer.spritePackingTag=tagName;
//導入項目資源
AssetDatabase.ImportAsset(localpath);
}
}
//刷新本地資源
AssetDatabase.Refresh();
}
//當界面發生變化時重繪
void
OnInspectorUpdate()
{
Repaint();
}
}
|
因為打包圖集只需要一個參數,因此這個打包工具只需要一個文本框和一個按鈕,整個過程和案例2是一樣的,這里就不做分析了。這個擴展程序的演示效果如下:
好了,這就是今天的內容了,今天的內容基本上涵蓋了為Unity3D開發擴展程序的基本內容,我們接下來要做的就是積極地在平時生活、工作和學習中尋找問題和解決問題,”授人以魚不如授人以漁”,向他人傳授知識和技能,這件事情本身對博主而言就是是快樂的,博主希望今天的內容大家能夠喜歡。好了,本篇unity教程到此結束,謝謝大家!