🚞

实现基本功能

显示所有的 Prefab

Prefab Center 首先必须可以显示所有的 Prefab ,这里默认都存放在路径 Assets/Prefabs 下。Unity 内提供了一套查找资源的方式,主要通过 AssetDatabase 实现。

在此之前,为了便于后期对 Prefab 的分类,在所有要显示的 Prefab 上添加一个组件 PrefabCenterItem ,由脚本 Assets/Scripts/PrefabCenter/PrefabCenterItem.cs 定义:

using UnityEngine;

namespace EditorWindowExtension.PrefabCenter {
		public enum PrefabCategory {
				None = 0,
				Players,
				Enemies,
				Collectables,
				Blocks
		};

		public class PrefabCenterItem : MonoBehaviour {
				[SerializeField]
				PrefabCategory _prefabType = PrefabCategory.None;
		
				public PrefabCategory Type {
						get { return _prefabType; }
				}
		}
}

这样就可以通过 GetComponent<PrefabCenterItem> 确认是否要显示在 Prefab Center 中。具体通过下边的函数实现:

private List<GameObject> GetPrefabsFromAssetDatabase() {
		string [] guids = AssetDatabase.FindAssets ("t:Prefab", new string [] { "Assets/Prefabs" });
		List<GameObject> prefabs = new List<GameObject> ();
	
		foreach (string guid in guids) {
				var assetPath = AssetDatabase.GUIDToAssetPath (guid);
				var asset = AssetDatabase.LoadAssetAtPath (assetPath, typeof (GameObject)) as GameObject;
				if (asset.GetComponent<PrefabCenterItem> ()) {
						prefabs.Add (asset);
				}
		}

		return prefabs;
}

上述代码首先通过 AssetDatabase.FindAssets 获取所有 Prefab 的 GUID,然后根据 GUID 获取到资源的路径,再通过路径找到资源本身,最后判断资源下是否存在 PrefabCenterItem 组件,从而筛选出所有要显示的 Prefab 并以列表的形式返回。

获取到所有的 Prefab 后就可以具体实现绘制函数 OnGUI

private void OnGUI () {
		DrawToolBar ();

		var buttonWidth = (position.width - 25) / 4;
		var buttonHeight = buttonWidth + 15;
		
		GUIStyle guiStyle = new GUIStyle (GUI.skin.button);
		guiStyle.fontStyle = FontStyle.Bold;
		guiStyle.alignment = TextAnchor.MiddleCenter;
		guiStyle.imagePosition = ImagePosition.ImageAbove;
		
		int i = 0;
		foreach (var item in _prefabItems) {
		    var buttonLeft = 5 + (i % 4) * (buttonWidth + 5);
		    var buttonTop = 27 + (i / 4) * (buttonHeight + 5);
				Rect buttonPos = new Rect (buttonLeft, buttonTop, buttonWidth, buttonHeight);
        
				DrawPrefabItem (item, buttonPos, guiStyle);
		    i++;
		}
}

绘制单个 Prefab 的函数 DrawPrefabItem 定义如下:

private void DrawPrefabItem (GameObject item, Rect pos, GUIStyle style) {
    Texture2D texture = AssetPreview.GetAssetPreview (item);
    GUI.Button (
        pos,
        new GUIContent (item.name, texture),
        style
    );
}

根据种类筛选

用户想要显示的 Prefab 种类由之前绘制的工具栏返回,对应变量 _toolBarIndex;Prefab 实际的种类包含在组件 PrefabCenterItem 中。如果二者相等则予以绘制,否则不绘制。此外,当用户选择的是 All (即 _toolBarIndex 为 0)时,一律都予以绘制:

private void OnGUI () {
		// ...
		foreach (var item in _prefabItems) {
		    if (_toolBarIndex == 0 || (int)item.GetComponent<PrefabCenterItem> ().Type == _toolBarIndex) {
		        // ...
		        DrawPrefabItem (item, buttonPos, guiStyle);
		        // ...
		    }
		}
}

添加基本操作

目前所有的 Prefab 都以按钮的形式显示在编辑器窗口中。通过判断按钮是否被按下,即可添加对应的响应事件。

DrawPrefabItem 中添加 if 语句:

if (GUI.Button (
    pos,
    new GUIContent (item.name, texture),
    style
)) {
    _showDetail = true;
    _shownItem = item;
    _detailPosition = pos;
}

即当某个 Prefab 对应的按钮被按下时,表示接下来要进行 _showDetail 的操作,并记录要显示详情的 _shownItem 和要显示的位置 _detailPosition

然后在绘制函数中添加显示详细操作的逻辑:

private void OnGUI () {
		// ...
		foreach (var item in _prefabItems) {
		    if (_toolBarIndex == 0 || (int)item.GetComponent<PrefabCenterItem> ().Type == _toolBarIndex) {
		        var buttonLeft = 5 + (i % 4) * (buttonWidth + 5);
		        var buttonTop = 27 + (i / 4) * (buttonHeight + 5);
		        Rect buttonPos = new Rect (buttonLeft, buttonTop, buttonWidth, buttonHeight);
		
		        if (buttonPos != _detailPosition || !_showDetail)
		            DrawPrefabItem (item, buttonPos, guiStyle);
		
		        i++;
		    }
		}

		DrawDetail ();
}
private void DrawDetail () {
		if (_showDetail) {
				BeginWindows ();
				GUI.Window (1, _detailPosition, OnDetailGUI, _shownItem.name);
				EndWindows ();
		}
}

其中由 OnDetailGUI 定义详情窗口的绘制方法,包含一个添加该 Prefab 到场景中的按钮和关闭按钮:

private void OnDetailGUI (int id) {
		if (GUI.Button (new Rect (5, 20, _detailPosition.width - 10, 17), "Add to scene")) {
				Instantiate (_shownItem).name = _shownItem.name;
		}
	
		if (GUI.Button (new Rect (_detailPosition.width - 16, _detailPosition.height - 16, 12, 12), GUIContent.none, "WinBtnCloseMac")) {
				_showDetail = false;
		}
		GUI.FocusWindow (1);
}

完善细节

上述步骤完成后已经可以达到基本的效果,打开 Prefab Center 后可以看到所有的 Prefab 并且可以进行分类和简单操作。但是可以发现打开一个 Prefab 的详细菜单后,更换 Prefab 种类或者改变窗口的大小后,菜单会以原来的尺寸留在原地,视觉效果不好。为解决这个问题,在代码中添加以下简单逻辑: