在过去十年中,Unity一直是开发游戏的绝佳平台,为开发人员提供大量工具:渲染引擎,物理引擎,动画系统,音频混合器等。但是,在创建关卡或生成游戏内数据时,Unity不足,因为每个游戏都是独一无二的,需要不同种类的工具。值得庆幸的是,Unity为我们的开发人员提供了一个API,可以创建我们自己的编辑器窗口,自定义抽屉和检查器面板。在这一系列的博客文章中,我将向您展示我们如何在Gram Games中为设计师和艺术家在Unity中开发自定义编辑器。我们将首先创建一个编辑器窗口,在其中放置两个面板,然后使其可调整大小。在下一篇文章中,我们将该窗口转换为Unity自己的控制台窗口的克隆。
那么,让我们开始吧!首先,我们将创建一个空的Unity项目,并在Assets下添加一个名为Editor的文件夹。如果您在Unity中为文件夹指定某些名称,它将以特殊方式处理其内容。编辑器就是其中一个名称,此文件夹中的脚本将成为Unity自己编辑器的一部分。那么,让我们构建一个编辑器窗口脚本:进入刚刚创建的Editor文件夹,右键单击,然后选择Create - > C#Script。您也可以选择Editor Test C#Script,但我们会改变所有内容,所以它实际上并不重要。将脚本命名为ResizablePanels,然后在您喜欢的文本编辑器中打开它(在Gram Games中我们更喜欢Xamarin)。
由于这将是一个编辑器窗口,因此该类应该来自EditorWindow而不是MonoBehaviour。 EditorWindow类驻留在UnityEditor命名空间中,因此我们也需要添加它。
using
UnityEngine;
using
UnityEditor;
public
class
ResizablePanels :
EditorWindow
{
}
编辑器窗口需要初始化静态方法。 在这个方法中,我们将构建窗口并(可选)给它一个标题。 GetWindow()是一个EditorWindow方法,它创建窗口(如果它不存在),或者查找并关注它(如果存在)。
但是,静态方法本身是不够的,我们还需要添加一个按钮或类似的东西,让我们的用户在Unity中打开窗口。 值得庆幸的是,已经有一个名为MenuItem的属性,它将一个菜单项添加到Unity的菜单栏并运行它应用的方法。 因此,以下代码将创建您可以在Unity中创建的最基本的编辑器窗口。
using
UnityEngine;
using
UnityEditor;
public
class
ResizablePanels :
EditorWindow
{
MenuItem(
"Window/Resizable Panels")]
private
static
void
OpenWindow()
{
ResizablePanels
window =
GetWindow<
ResizablePanels>();
window.
titleContent =
new
GUIContent(
"Resizable Panels");
}
}
这是菜单项:
这是我们点击它时打开的窗口:
为了在这个窗口中绘图,我们将使用OnGUI()方法(是的,Unity自己的编辑器系统仍然使用旧的GUI系统,它可能不会长时间改变)。但首先,我们需要两个矩形来定义我们的面板。我还将在他们自己的方法中绘制这些面板,所以当我们处理它时,我们也应该添加这些方法。
using
UnityEngine;
using
UnityEditor;
public
class
ResizablePanels :
EditorWindow
{
private
Rect
upperPanel;
private
Rect
lowerPanel;
MenuItem(
"Window/Resizable Panels")]
private
static
void
OpenWindow()
{
ResizablePanels
window =
GetWindow<
ResizablePanels>();
window.
titleContent =
new
GUIContent(
"Resizable Panels");
}
private
void
OnGUI()
{
DrawUpperPanel();
DrawLowerPanel();
}
private
void
DrawUpperPanel()
{
}
private
void
DrawLowerPanel()
{
}
}
我们的编辑窗口开始形成。我们现在需要做的就是绘制面板并检查它们是否正常工作。GUILayout.BeginArea(Rect rect)将创建一个要绘制的矩形区域,GUILayout.EndArea()标记结束。这些区域将定义我们的面板。我还要在这两个区域添加标签,以便我们可以看到它们的外观。
using
UnityEngine;
using
UnityEditor;
public
class
ResizablePanels :
EditorWindow
{
private
Rect
upperPanel;
private
Rect
lowerPanel;
MenuItem(
"Window/Resizable Panels")]
private
static
void
OpenWindow()
{
ResizablePanels
window =
GetWindow<
ResizablePanels>();
window.
titleContent =
new
GUIContent(
"Resizable Panels");
}
private
void
OnGUI()
{
DrawUpperPanel();
DrawLowerPanel();
}
private
void
DrawUpperPanel()
{
upperPanel =
new
Rect(
0,
0,
position.
width,
position.
height *
0.5f);
GUILayout.
BeginArea(
upperPanel);
GUILayout.
Label(
"Upper Panel");
GUILayout.
EndArea();
}
private
void
DrawLowerPanel()
{
lowerPanel =
new
Rect(
0,
position.
height *
0.5f,
position.
width,
position.
height *
0.5f);
GUILayout.
BeginArea(
lowerPanel);
GUILayout.
Label(
"Lower Panel");
GUILayout.
EndArea();
}
}
好吧,他们似乎在工作:
然而,这两个面板被设置为覆盖窗口的一半,但是如果我们希望它们可以调整大小,则它们需要具有可变高度。所以,我要添加一个大小比例变量; 这样,当一个面板覆盖一定量的窗口时,另一个面板可以覆盖剩余部分。
using
UnityEngine;
using
UnityEditor;
public
class
ResizablePanels :
EditorWindow
{
private
Rect
upperPanel;
private
Rect
lowerPanel;
private
float
sizeRatio =
0.5f;
MenuItem(
"Window/Resizable Panels")]
private
static
void
OpenWindow()
{
ResizablePanels
window =
GetWindow<
ResizablePanels>();
window.
titleContent =
new
GUIContent(
"Resizable Panels");
}
private
void
OnGUI()
{
DrawUpperPanel();
DrawLowerPanel();
}
private
void
DrawUpperPanel()
{
upperPanel =
new
Rect(
0,
0,
position.
width,
position.
height *
sizeRatio);
GUILayout.
BeginArea(
upperPanel);
GUILayout.
Label(
"Upper Panel");
GUILayout.
EndArea();
}
private
void
DrawLowerPanel()
{
lowerPanel =
new
Rect(
0, (
position.
height *
sizeRatio),
position.
width,
position.
height * (
1 -
sizeRatio));
GUILayout.
BeginArea(
lowerPanel);
GUILayout.
Label(
"Lower Panel");
GUILayout.
EndArea();
}
}
好的,两个面板都有可变的高度......但是我们仍然无法调整它们的大小!但不要担心,我们离最终产品并不太远。现在我们只需要另一个矩形区域,这样当用户点击那里时我们就可以开始调整大小。我将在两个面板之间添加该区域。这将是10像素高,因为,为什么不呢?我喜欢10(我喜欢1010!甚至更好:))。
我们还需要向用户显示鼠标指针位于调整大小区域中。EditorGUIUtility.AddCursorRect(Rect rect,MouseCursor cursor)就是这样做的,所以我们将在绘图方法中使用它。
我们需要向用户展示另一件事:调整大小区域的开始位置。这看起来像魔术,但我保证,它不是:我们将使用Unity自己的一个图标并将其拉伸并放置,使其看起来像上下面板之间的线。
using
UnityEngine;
using
UnityEditor;
public
class
ResizablePanels :
EditorWindow
{
private
Rect
upperPanel;
private
Rect
lowerPanel;
private
Rect
resizer;
private
float
sizeRatio =
0.5f;
private
bool
isResizing;
private
GUIStyle
resizerStyle;
MenuItem(
"Window/Resizable Panels")]
private
static
void
OpenWindow()
{
ResizablePanels
window =
GetWindow<
ResizablePanels>();
window.
titleContent =
new
GUIContent(
"Resizable Panels");
}
private
void
OnEnable()
{
resizerStyle =
new
GUIStyle();
resizerStyle.
normal.
background =
EditorGUIUtility.
Load(
"icons/d_AvatarBlendBackground.png")
as
Texture2D;
}
private
void
OnGUI()
{
DrawUpperPanel();
DrawLowerPanel();
DrawResizer();
}
private
void
DrawUpperPanel()
{
upperPanel =
new
Rect(
0,
0,
position.
width,
position.
height *
sizeRatio);
GUILayout.
BeginArea(
upperPanel);
GUILayout.
Label(
"Upper Panel");
GUILayout.
EndArea();
}
private
void
DrawLowerPanel()
{
lowerPanel =
new
Rect(
0, (
position.
height *
sizeRatio) +
5,
position.
width,
position.
height * (
1 -
sizeRatio) -
5);
GUILayout.
BeginArea(
lowerPanel);
GUILayout.
Label(
"Lower Panel");
GUILayout.
EndArea();
}
private
void
DrawResizer()
{
resizer =
new
Rect(
0, (
position.
height *
sizeRatio) -
5f,
position.
width,
10f);
GUILayout.
BeginArea(
new
Rect(
resizer.
position + (
Vector2.
up *
5f),
new
Vector2(
position.
width,
2)),
resizerStyle);
GUILayout.
EndArea();
EditorGUIUtility.
AddCursorRect(
resizer,
MouseCursor.
ResizeVertical);
}
}
就快结束了:
现在,我们将通过添加实际的交互来最终确定它。我们将处理传入的事件,如果事件是鼠标按下事件并且它在调整大小区域,我们将开始调整大小。
using
UnityEngine;
using
UnityEditor;
public
class
ResizablePanels :
EditorWindow
{
private
Rect
upperPanel;
private
Rect
lowerPanel;
private
Rect
resizer;
private
float
sizeRatio =
0.5f;
private
bool
isResizing;
private
GUIStyle
resizerStyle;
MenuItem(
"Window/Resizable Panels")]
private
static
void
OpenWindow()
{
ResizablePanels
window =
GetWindow<
ResizablePanels>();
window.
titleContent =
new
GUIContent(
"Resizable Panels");
}
private
void
OnEnable()
{
resizerStyle =
new
GUIStyle();
resizerStyle.
normal.
background =
EditorGUIUtility.
Load(
"icons/d_AvatarBlendBackground.png")
as
Texture2D;
}
private
void
OnGUI()
{
DrawUpperPanel();
DrawLowerPanel();
DrawResizer();
ProcessEvents(
Event.
current);
if (
GUI.
changed)
Repaint();
}
private
void
DrawUpperPanel()
{
upperPanel =
new
Rect(
0,
0,
position.
width,
position.
height *
sizeRatio);
GUILayout.
BeginArea(
upperPanel);
GUILayout.
Label(
"Upper Panel");
GUILayout.
EndArea();
}
private
void
DrawLowerPanel()
{
lowerPanel =
new
Rect(
0, (
position.
height *
sizeRatio) +
5,
position.
width,
position.
height * (
1 -
sizeRatio) -
5);
GUILayout.
BeginArea(
lowerPanel);
GUILayout.
Label(
"Lower Panel");
GUILayout.
EndArea();
}
private
void
DrawResizer()
{
resizer =
new
Rect(
0, (
position.
height *
sizeRatio) -
5f,
position.
width,
10f);
GUILayout.
BeginArea(
new
Rect(
resizer.
position + (
Vector2.
up *
5f),
new
Vector2(
position.
width,
2)),
resizerStyle);
GUILayout.
EndArea();
EditorGUIUtility.
AddCursorRect(
resizer,
MouseCursor.
ResizeVertical);
}
private
void
ProcessEvents(
Event
e)
{
switch (
e.
type)
{
case
EventType.
MouseDown:
if (
e.
button ==
0 &&
resizer.
Contains(
e.
mousePosition))
{
isResizing =
true;
}
break;
case
EventType.
MouseUp:
isResizing =
false;
break;
}
Resize(
e);
}
private
void
Resize(
Event
e)
{
if (
isResizing)
{
sizeRatio =
e.
mousePosition.
y /
position.
height;
Repaint();
}
}
}
这是最终版本: