Unity Editor: Preview window,繪製自定組件的貼圖預覽編輯器介面
驗證需求,最近在專案中,我們建立能根據語言設定,自動切換不同語系貼圖的功能。在這些組件 (Component) 中,我們必須預先設定多張貼圖,怎麼在 Unity 編輯器中檢查這些貼圖設定是否正確,變成一個待解的問題。
在本文章中,紀錄如何使用 Unity Preview 機制,建立自訂該組件專屬的預覽視窗,讓設計人員能在編輯器中確認資料是否正確。
預覽介面,使用 ObjectPreview 或是 Editor
在 Unity 中可以使用 Editor 或者是 ObjectPreview 來建置客製化預覽,Editor 通常用於其組件 (Component) 在 inspector 編輯器的樣子,客製化讓操作者更容易調整組件參數。ObjectPreview 僅僅針對組件建立預覽,而不能修改其編輯器。
由於沒計畫修改組件的編輯器,僅要客製化預覽功能,故使用 ObjectPreview,後續範例都是用 ObjectPreview。
關於範例資料結構
自訂組件的資料結構,建立 private struct config,放置對應語言切換哪張 sprite:
using UnityEngine;
using UnityEngine.UI;
[RequireComponent(typeof(Image))]
public partial class LanguageImageSwitcher : MonoBehaviour
{
[SerializeField]
Config[] configs = new Config[0];
public Config[] Configs
{
get
{
return this.configs;
}
set
{
if (value == null)
{
throw new System.ArgumentNullException();
}
this.configs = value;
}
}
[System.Serializable]
public struct Config
{
[SerializeField]
Language language;
[SerializeField]
Sprite sprite;
public Language Language
{
get { return this.language; }
set { this.language = value; }
}
public Sprite Sprtie
{
get { return this.sprite; }
set { this.sprite = value; }
}
}
}
public enum Language
{
English,
TraditionalChinese,
Japanese,
}
預設編輯頁面:
使用 ObjectPreview 建立客製化預覽介面
參考範例以及官方文件後,完成第一版本的預覽:
該編輯器程式碼以及註解:
using UnityEngine;
using UnityEditor;
[CustomPreview(typeof(LanguageImageSwitcher))]
public class LanguageImageSwitcherPreview : ObjectPreview
{
Language displayLanguage;
public override bool HasPreviewGUI()
{
return true;
}
public override GUIContent GetPreviewTitle()
{
return new GUIContent("Language Images");
}
public override void OnPreviewSettings()
{
base.OnPreviewSettings();
this.displayLanguage = (Language)EditorGUILayout.EnumPopup(this.displayLanguage);
}
public override void OnInteractivePreviewGUI(Rect r, GUIStyle background)
{
var target = this.target as LanguageImageSwitcher;
var configs = target.Configs;
foreach (var config in configs)
{
if (config.Language == this.displayLanguage)
{
if (config.Sprtie != null)
{
EditorGUI.DrawTextureTransparent(r, config.Sprtie.texture, ScaleMode.ScaleToFit);
}
return;
}
}
}
}
- Line 4: CustomPreview 標示此預覽是為哪個類型
- Line 9: HasPreviewGUI 回傳是否有預覽,可以根據參數進行判斷,在這個範例中永遠都會有
- Line 14: GetPreviewTitle 回傳該預覽標題內容
- Line 22: OnPreviewSettings 繪製預覽功能列,在這個範例中畫出語言選擇 (EnumPopup)
- Line 25: 有兩種模式 OnPreviewGUI 以及 OnInteractivePreviewGUI,後者能支援使用者操作互動 (例如滑鼠右建額外的 Context-menu),雖然我們沒有額外的互動機制,但我們還是使用 OnInteractivePreviewGUI
- Line 27: target 表示目前 inspector 選擇的目標
- Line 36: 取得目前語言所對應 sprite,並且畫在預覽畫面區域
Rect r
中
再調整預覽畫面,自行計算繪製區域
但這個版本不怎麼方便除錯,檢查所有語言需要一個一個點來看,實在是太麻煩。因此我們修改一下 Preview,把所有語言對應的貼圖,直接塞在同一個預覽畫面中:
其程式碼修改如下,根據資料以及參數計算每個設定 Rect 大小:
using UnityEngine;
using UnityEditor;
[CustomPreview(typeof(LanguageImageSwitcher))]
public class LanguageImageSwitcherPreview : ObjectPreview
{
Language displayLanguage;
public override bool HasPreviewGUI()
{
return true;
}
public override GUIContent GetPreviewTitle()
{
return new GUIContent("Language Images");
}
public override void OnInteractivePreviewGUI(Rect r, GUIStyle background)
{
var target = this.target as LanguageImageSwitcher;
var configs = target.Configs;
var space = 4f;
var texturePadding = 4f;
var rowHeight = (r.height - ((configs.Length - 1) * space)) / configs.Length;
var labelHeight = EditorGUIUtility.singleLineHeight;
var textureHeight = rowHeight - labelHeight - texturePadding;
var rect = r;
foreach (var config in configs)
{
// Draw label
rect.height = labelHeight;
EditorGUI.DropShadowLabel(rect, config.Language.ToString());
rect.y += rect.height + texturePadding;
// Draw texture or warning
rect.height = textureHeight;
if (config.Sprtie != null)
{
EditorGUI.DrawTextureTransparent(rect, config.Sprtie.texture, ScaleMode.ScaleToFit);
}
else
{
EditorGUI.HelpBox(rect, "No data", MessageType.Warning);
}
rect.y += rect.height + space;
}
}
}
小感
寫一個簡單的預覽工具不難,要不是這次美術圖片檔案命名一樣 (放置在不同資料夾),且還有另外一個更複雜要設定更多貼圖的組件,不然也不會有如此起心動念要製作這個工具啊。
只是…Unity 編輯器在切換選擇不同的 GameObject 時,其 Preview 都會重新設定,如此檢查也是挺不方便的。後來才注意到可以選擇多個 GameObject,其預覽介面能顯示多個物件。
另外好奇搜尋一下如何在 Preview 繪製 3D 物件,注意到這篇文章介紹所提到的 PreviewRenderUtility,從 ILSpy 中反編譯 UnityEngine 得知這個是 public class,但搜尋官方文件確沒有說明,以後如果有機會再來嘗試吧。
Reference
- ScriptReference: ObjectPreview
- Sprite Animation Preview,裡頭所寫的 2D 動畫預覽,是一個相當好的範例
沒有留言: