Fork me on GitHub

Unity-BulletinBoard

UGUI-BulletinBoard

github
video

IMGUI 和 UGUI

以前我们的 button,text等等都是用 OnGUI 画出来的,这些都属于 IMGUI。这次我们使用 UGUI 来完成一个可展开消息的公告牌。
首先,我们来了解一下 IMGUI 和 UGUI:

  • IMGUI(Immediate Mode GUI)及时模式图形界面。它是代码驱动的 UI 系统,即没有图形化设计界面,只能在 OnGUI 阶段用 GUI 系列的类绘制各种 UI 元素。
  • Unity GUI / UGUI 是面向对象的 UI 系统。所有 UI 元素都是游戏对象,有友好的图形化设计界面, 在场景渲染阶段渲染这些 UI 元素。

两个 GUI 都是 unity 中的 UI 设计方案,前者偏向代码,受程序员喜爱,而后者偏向图形化,普通设计者也可以直接参与开发,各有优劣,但 unity 官方都视为重要模型。

BulletinBoard

可展开公告牌是这次要完成的目标。但是,在许多开放式 RPG 游戏中,印象最深刻的还是那种古旧羊皮纸的书信。所以我将公告牌换成了可展开信息的信纸,类似玩家背包中实时发布任务的信纸(深度游戏重度患者的嗜好)。

创建信纸及任务信息

我们通过 UGUI 来构建本次 UI 界面。首先,添加一个 canvas 画布,然后给画布添加 ScrollView 子对象,作为滚动展示区域。接着在 SrcollView 控件中的 Content 对象中添加 Button 和 Text 对象,其中 Button 实现点击展开与收缩信息的按钮,Text 作为信息。最终结构如下图:
structure

马上,我们给我们贴上信纸背景:给 Canvas 和 ScrollView 的 Image 组件设置 SourceImage 即可(没有组件可以自己添加)。这里的 SourceImage 接受图片精灵,我们导入图片的 Assets 后,将它的 Texture Type 设置成 Sprite(2D and UI),即可。
sprite
scrollview

接下里给 Content 添加 Vertical Layout Group 组件,使其中的对象呈垂直显示。参数设置如下:
verticallayoutgroup
参数的含义如下:

参数 含义
padding 与父对象的间距(学习过 css 的小伙伴应该比较熟悉)
Spacing 子物体之间的间隔
Child Alignment 子物体整体位于什么方位
Child Controls Size 子物体是否控制大小
Child Force Expand 是否强制拉伸长宽到父物体大小

然后,将 Button 的 Transition 设置成 None,因为我们不希望点击按钮有过渡动画。对于信息 Text,就自己设置样式了,不过这里是支持 Rich Text 的,所以可以做出各种样式的文本。下面是 Rich Text 的部分常用表达式:

表达式 功能 结果
\<color=#ff0000ff>text\ 字体颜色,前6个十六进制符号表示 RGB,后六个表示 alpha text
\text\ 字体加粗 text
\<size=20>text\ 字体大小 text

至此,运行结果应该是所有信息可以正常展示,如果滚动条或信息无法正常展示,可以调整 Text 和 Content 的 Height 让其显示完全。

信息展开动画

UGUI 也可以完成这个动画设置,使用 animator 进行动画设置,这里我选择使用代码完成。

我们使用协程技术来完成这个动画效果(模仿帧动画)。

这里说明两点:

  1. yield return null
    yield return null 和 yield return num 都是表示暂缓一帧。所以无论是 yield return 10 还是 yield return 0,都表示暂缓一帧。
  2. RectTransform 的大小设置
    我们有三种设置大小的方法,它们中的两个与锚点位置有关,这里我采用与锚点无关的设置方法。
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
public class FolderScript : MonoBehaviour {

private Button btn;
public Text text;
public int frame;
public float height;

// Use this for initialization
void Start() {
btn = this.gameObject.GetComponent<Button>();
btn.onClick.AddListener(OnClick);
StartCoroutine(TextCollapsed());
}

IEnumerator TextCollapsed() {
float y = height;
for (int i = 0; i < frame; ++i) {
y -= height / frame;
text.rectTransform.SetInsetAndSizeFromParentEdge(RectTransform.Edge.Right, 0, text.rectTransform.sizeDelta.x);
text.rectTransform.SetInsetAndSizeFromParentEdge(RectTransform.Edge.Bottom, 0, y);
if (i == frame - 1) {
text.gameObject.SetActive(false);
}
yield return null;
}
}

IEnumerator TextVisible() {
float y = 0;
for (int i = 0; i < frame; ++i) {
y += height / frame;
text.rectTransform.SetInsetAndSizeFromParentEdge(RectTransform.Edge.Right, 0, text.rectTransform.sizeDelta.x);
text.rectTransform.SetInsetAndSizeFromParentEdge(RectTransform.Edge.Bottom, 0, y);
if (i == 0) {
text.gameObject.SetActive(true);
}
yield return null;
}
}

void OnClick() {
if (text.gameObject.activeSelf) {
StartCoroutine(TextCollapsed());
} else {
StartCoroutine(TextVisible());
}
}
}

贴合滚动条

上面的步骤都做完了,运行就可以看到可展开信息的信纸系统了。美中不足的是,我们的滚动条是固定长度的,而且初始也显示在界面上。我们来处理一下:给 Content 添加 Content Size Fitter 组件
content
设置 Vertical Fit 为 Min Size,这样 Content 的数值高度就与其中的子对象显示高度保持一致。
这样一来,我们的滚动条就会根据 Content 高度实时变化了。