You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

195 lines
10 KiB
Markdown

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 第16讲 | 如何在游戏中载入UI和菜单
今天我们要在游戏中载入UI和菜单在开始之前我们先调整一下我们之前讲过的游戏代码部分的内容。
首先我们需要更改游戏的背景图片,使之看起来更像是一款打飞机的游戏,而不是最早之前我们随便用的一幅山水图。我们先将游戏背景修改为正常的游戏背景,并且贴上飞机图像。
![](https://static001.geekbang.org/resource/image/3c/2c/3cbd72b8968c23eaeb57737a9393072c.jpg)
这里,我想到一个问题,之前有人留言问我,程序员需不需要有美术功底。我在这里说一下我的看法。如果你只是要做一个程序员,那我可以告诉你,不需要。但是,如果你不是只想做一个“码农”,你想有更多的发展,那各方面的知识,比如策划、美术,你都需要去了解。
## UI的两种呈现形式
言归正传我们需要在这个游戏画面上面加一系列的内容来代表UI的呈现。UI的呈现有两种形式
* 第一种就是美术画好UI图片然后直接贴图用鼠标控制一系列的点击和按下操作
* 另外一种是自己画UI界面比如画一个框并且在框里面写上文字。
这两种方式各有利弊。
如果使用美术UI图贴图的方式**优点**就是可以减少程序员的工作量。这样在版本迭代的时候,美术改一幅图就可以修改界面,方便快捷,你就不需要做多余的工作。但是这样的**缺点**就是增加了游戏安装包的大小毕竟UI是一幅图只要是图就会有一定的体积就会增加安装包的大小。
如果是程序员自己绘制的UI界面**好处**就是主程序体积变得稍大一点,而游戏安装包不会变大。但是这样的**缺点**也很明显就是程序员的工作量会增加很多。而且当游戏需要迭代的时候或者界面需要更新的时候程序员需要重新绘制或者重新编写UI源代码大大增加了工作量。
我们现在是自己来开发那我就讲一讲程序员绘制UI的方法。
我们通过模拟按钮的方式来摆放UI界面。首先我们要在UI界面上摆放一系列字符我们要实现的效果是只要使用鼠标点击到这个字符就会变换字符的内容。这个过程我们会用到鼠标操作、绘制矩形、字体和字符绘制相关的知识下面我就来具体给你讲。
## 鼠标操作
我们先来看一下鼠标操作的知识。在Pygame中鼠标操作用到的模块是pygame.mouse。在这个模块里面点击事件的函数是get\_pressed.假如有返回按钮1、按钮2和按钮3等等很多按钮,我们随便选一个点假如说选了按钮1那代码可以这么写
```
pygame.mouse.get_pressed()[0]
```
这条语句不需要在事件语句里面操作,写在别的地方也可以,而且鼠标的操作在循环里一直是实时监测的。
## 绘制矩形
随后,我们要绘制矩形。 **绘制矩形的目的是为了模拟一个按钮。** 矩形绘制的代码是Rect但是我们需要绘制在一个surface上这个surface需要新建然而在pygame中如果使用pygame.surface.surface初始化一个surface的话不能指定位置x值是从0开始的。
所以在屏幕上看到的新建的surface是一个长条状的图层所以我们需要将生成一个图层的子图层并且如果使用这个子图层的话在blit的时候将会提示被锁定所以我们还需要将这个子图层进行拷贝。所以我们的代码看起来是这个样子的。
```
the_rect = Rect(200, 100, 150, 40)
block_surface = screen.subsurface(the_rect).copy()
```
首先第一行代码是建立一个矩形分别是左侧起始值是200顶部从100开始宽度150长度40。随后使用screen这个图层来建立一个子图层子图层的大小按照the\_rect这个矩形大小来建立。随后的一个copy函数是将子图层进行拷贝在后续的使用中不会出现锁定图层的情况。
## 绘制字体和字符
之后我们要开始编写文字处理的代码字体我们要用到pygame.font模块我们先初始化一个字体这个字体在安装pygame游戏库的时候就包含在了pygame里面我们直接就可以拿来使用。现在我们初始化字体并且将字体大小调整到25
```
fnt = pygame.font.Font('freesansbold.ttf',25)
```
其中freesansbold.ttf是pygame安装的时候默认存在的ttf字体文件随后我们在第二个参数设置为大小25。
我们拿到了fnt对象然后使用这个对象调用render函数。这其实就是渲染将文字渲染在屏幕并且形成一个文字图层函数原型是这样的
```
Font.render(text, antialias, color, background=None)
```
其中第一个参数text是文字内容第二个参数antialias是抗锯齿第三个内容color是文字颜色最后一个是背景颜色默认可以忽略。
```
tsurf = fnt.render(text, True, (255,255,255))
```
我们将颜色设置为白色所以是255255255
tsurf是render返回的一个文字的图层surface我们之后要按照这个图层来确定它的矩形框。
```
trect = tsurf.get_rect()
```
随后我们需要将文字摆在这个trect矩形框的中央所以我们要进一步将trect确定在中央的位置计算完中央的坐标值并且赋值过去。
```
trect.center = ((block_surface.get_width()/2),(block_surface.get_height()/2))
```
我们将最开始的复制的子图层的宽度和高度除以2就得到了中心点的位置。
最后我们要做的就是在封装的函数内将blit部分包含进去现在我们来看一下完整的包装函数代码。
```
def text_out(text):
fnt = pygame.font.Font('freesansbold.ttf',25)
tsurf = fnt.render(text, True, (255,255,255))
trect = tsurf.get_rect()
trect.center = ((block_surface.get_width()/2),(block_surface.get_height()/2))
block_surface.blit(tsurf, trect)
```
我们看到在函数最后我们blit了block\_surface这个被拷贝的图层。
随后我们在游戏的大循环里面,需要判断鼠标的点击事件,我们之前所定义的矩形,代码是这样:
```
the_rect = Rect(200, 100, 150, 40)
```
所以这x的起始位置和结束位置是200和350y轴的起始位置和结束位置是200和240为什么y轴也是200开始呢因为起始点 200既是x轴开始的点也是y轴开始的点。我们在代码里面这么判断鼠标的点
```
txt = "Pause"
x, y = pygame.mouse.get_pos()
if pygame.mouse.get_pressed()[0]:
if x >=200 and x <= 350 and y >= 200 and y <= 240:
txt = "Clicked
```
我们将txt定义为Pause字符串并且判断是不是鼠标左键点击的。如果是的话判断是不是在 x 轴和y轴进行点击如果不是的话就将txt改为Clicked。
随后我们绘制按钮框,并且将按钮框背景设置为绿色,然后输出文字,并且绘制。
```
screen.blit(block_surface, (200, 200))
block_surface.fill([0,20,0])
text_out(txt)
```
将block\_surface这个图层绘制在200,200的坐标点并且将之涂为绿色最后调用text\_out函数由于text\_out里面已经编写了blit函数所以不需要再次blit了。
我们来看一下效果图:
![](https://static001.geekbang.org/resource/image/fb/a4/fb4e358092d3ad315e54f61cb984fca4.jpg)
我们看到了一个绿色的按钮放置在屏幕上并且有一个白色的Pause字样放在按钮上如果是鼠标左键点击在这个按钮上就会变成Clicked字样。
到这里,你可能会问了,为什么没有解释怎么输出中文呢?
在这种情况下,输出中文有两种解决方案。
* 第一种是比较底层的方案,就是**根据中文进行点阵绘制**,这需要很底层的代码操作,效率也不太高,所以这种方案我们不作讨论。
* 第二种就是**改变字体**我们可以在初始化字体的时候下载一些网上的中文字体先进行尝试或者我们可以使用系统字体。在使用系统字体的时候我们可以使用SysFont来初始化字体。
```
fnt = pygame.font.SysFont('微软雅黑',32)
```
另外还需要修改一个地方。我们需要在Python代码最开始的地方添加编码方式并且将中文文字前面添加u字样来告诉解释器是Unicode的。
```
# encoding: utf-8
txt = u"暂停"
```
我们来看一下效果:
![](https://static001.geekbang.org/resource/image/bb/d2/bba42a0cb409eedaab6a78f02cc456d2.jpg)
至此,中文的输出也已经完成了。
到现在为止我们编写完了UI的按钮部分。至于菜单部分我们也可以通过相同的方式来编写菜单效果。
作为菜单这些高级操作,比如点击出现下级菜单、隐藏菜单这些动态效果,可以使用图片的方式来制作菜单,并且进行载入。如果使用程序来编写菜单的效果,工作量就太大了。而如果是图片形式的话,只需要载入并且控制鼠标点击和绘制子菜单就可以了。
## 小结
今天我们学习了UI部分的编写以及文字的输出、鼠标的移动和抓取鼠标按键的实现我来总结一下今天的内容。
1. 不管何种类型的游戏引擎鼠标的操作基本有3个值需要控制左键、中键和右键。
2. 按钮可以用常规的方法来绘制。如果想做出更好的效果,比如画框,可以在框里面再画一个小框,看起来像是有凸出感觉的样子。这就需要你平时多注意观察一些细节,然后去分析如何用我们讲过的简单的操作来实现这些内容。
3. 在2D游戏中很多游戏引擎都不支持中文的输出。如果要输出中文假如你的引擎支持那你可以使用系统字体或者其他中文字体如果引擎不支持可以使用一个一个点阵绘制的方式在屏幕上绘制中文。最后一种方式也就是比较极端的方式那就是使用图片来直接贴上中文字符这种方式直接粗暴但是图片资源量太大而且如果你要在游戏中进行网络聊天这里面其实还是没有从根本上解决中文输出的问题。
现在给你留一个小问题。
如果让你在上述的代码中,将按钮变成菜单,也就是点击按钮,就在下方出下一个下拉框,你会如何实现?
欢迎留言说出你的看法。我在下一节的挑战中等你!