在大部分游戏中都会有对话框的存在,能推动剧情发展,能让玩家玩懂游戏。
那么在Pygame中,应该怎么制作这种对话框呢?
Pygame中基础的文字渲染和绘制:
#创建文字库
my_font = pygame.font.Font(font_family,size)
#前后参数分别为字体,大小
my_text = my_font.render(text,color,bool)
#渲染文字,参数分别为 文字内容,颜色(R,G,B),是否抗锯齿(True/False)
surface.blit(my_text,(x,y))
#绘制文字
但是问题是:渲染文字这一块十分的耗费性能,就和加载导入图片一样,是很吃帧数的。于是,我就通过提前渲染并储存在一个列表里来尽量做到优化。
竟然对话框的字是一个一个出现的,而且还有彩色文字,那么,我想到的方法是将每一个字拆分开放在列表中,并且写入对应坐标,就比如:
import pygame,sys
from pygame.locals import *
pygame.init()
#初始化pygame,否则用到字体这一块会报错
window_w,window_h = 1280,720
screen = pygame.display.set_mode((window_w,window_h))
#创建宽1280高720的屏幕
my_font = pygame.font.Font("font.ttf",40)
#这里是直接导入字体文件而并非系统自带字体
t1 = my_font.render("你",(128,128,128),True)
t2 = my_font.render("好",(255,255,255),True)
lst = [(t1,(0,0)),(t2,(40,0))]
while True:#开启循环
screen.fill((0,0,0))#填充背景为纯黑
for event in pygame.event.get(): #监听事件,比如退出
if event.type == pygame.QUIT:
sys.exit()
for i in lst:#遍历列表绘制文字
screen.blit(i[0],i[1])
pygame.display.flip()#刷新屏幕
这样就能绘制出两个紧密连接并且颜色不同的字了
不过我会把特殊效果和基础输出分开讲的,这篇主要讲文字输出
竟然要做出一个方便,好用的对话框,
我们可以将整一个对话框存储在一个类里边,先搞参数:
class WordsOutput():
def __init__(self,text,name,portrait):
self.text = text #对话框内容
self.name = name #名字
self.portrait = portrait #头像,存储surface对象
self.start_x = 0
if self.portrait != None:
self.start_x = self.portrait.get_size()[0]
self.start_y = 0
if self.name != None:
self.start_y = self.name.get_size()[1]
那么,光有内容可是不行的啊,要有头像,名字的对吧,于是就添加了两个参数:name名字,portrait头像
无头像:
有头像:
区别在于,文字渲染的起始位置是不同的
所以,我添加了一个参数start_x,值为0,若有头像则设置为头像的宽度
有无名字的区别也是这样,就不多bb了
接着,就是分析模块,因为当文字长度到达一定值时,就会超出屏幕边缘,所以需要换行。
所以就有了参数:start_y绘制高度,和start_x是一个原理
文字分析器的原理:
遍历一整段文字,将每个文字都单独分开,单个渲染出来,获取当行文字长度,检测是否超出边缘。
若超出边缘:换行。没有超出:继续渲染并储存在列表里,记录其位置信息。
最后,绘制时只需遍历列表即可。
如果看不懂的话,上代码,看注释,研究一下
def makeWords(txt, size, color):#文字渲染,返回 渲染的对象和尺寸
my_font = pygame.font.Font('../font.ttf', size)
r = my_font.render(str(txt), True, color)
return (r, r.get_size())
class WordsOutput():
def __init__(self, text, name, portrait):
self.startx = 0 # 起始位置
self.endx = 1280 # 边缘位置
self.text = text
self.name = name
self.portrait = portrait #头像为surface对象
self.portrait = pygame.transform.scale(self.portrait,(192,192))#压缩图片尺寸到192×192
if self.portrait != None: # 如果有头像则改变起始位置
self.startx = 192#将起始x位置设置为头像宽度
self.words = [] # 储存渲染出来的文字的列表
self.analyzed = False # 是否分析过
self.interval = time.time() # 计时器
self.output_speed = 0.02 # 输出文字的间隔时间
self.finished_writing = False # 是否结束输出文字
self.out_num = 0 #输出的文字编号
def analyze(self):
txt = self.text # 将txt赋值为内容
height = makeWords(txt[0], 35, (255, 255, 255))[1][1] # 单个文字高度,用第一个字的高度
txt_list = [] # 渲染出来的文字列表
w_last = 0 # 叠加文字后的整体长度
#初始高度位置
start_y = 0
#检测是否有头像
if self.portrait != None:
txt_list.append((self.portrait, (0, start_y)))
#若有名字则将名字渲染出来存储进列表,改变排版
if self.name != None:
a = makeWords(self.name, 35, (255, 255, 0))
txt_list.append((a[0], (self.startx, start_y)))
start_y += a[1][1]
#开始遍历文字
for i in range(len(txt)):
a = makeWords(txt[i], 35, (255, 255, 255)) # 渲染文字
if self.startx+w_last+a[1][0] <= self.endx: # 检测是否超出设定边缘
#未超出则记录位置,渲染文字,存储进列表
pos = (self.startx+w_last, start_y)
txt_list.append((a[0], pos))
w_last += a[1][0]
else:
#超出:换行,将x坐标设置为初始值,y则增加一个字的高度
start_split = i-1
start_y += a[1][1]
w_last = 0
a = makeWords(txt[i], 35, (255,255,255))
pos = (self.startx+w_last, start_y)
txt_list.append((a[0], pos))
w_last += a[1][0]
self.words = txt_list
self.analyzed = True
def paint(self):
if self.analyzed == False:
self.analyze()
if time.time() >= self.interval + self.output_speed and self.out_num < len(self.words):#检测是否到输出下一个文字的时间并且还未输出完
self.out_num+=1
self.interval = time.time()#重置计时器
for i in range(self.out_num):#绘制到达的文字
img, pos = self.words[i]
screen.blit(img, pos)
if self.out_num == len(self.words): #如果绘制完了
self.finished_writing = True #将变量设置为True
这一版的自由度并不高,只能自定义内容,名字,头像,其他的功能实现在下一篇会讲到。
使用方式:
#创建计时器,通俗讲就是帧数限制器
clock = pygame.time.Clock()
#创建对话框列表,存储对话框
w1 = []
#实例化类,写入参数,分别为 内容,名字,头像
w1.append(WordsOutput("Hello pygame",None,None))
w1.append(WordsOutput("Hello pygame, let's go", "名字", None))
while True:
screen.fill((0,0,0))#填充背景
for event in pygame.event.get():
if event.type == pygame.QUIT:#检测退出
exit()
if event.type == MOUSEBUTTONDOWN:#检测鼠标是否按下
if event.button == 1 and len(w1) > 0:#检测是否按下了左键,并且w1不是空列表
if w1[0].finished_writing:#如果文字输出完成了,则删除显示的这一项
w1.pop(0)
if len(w1) > 0:#如果w1不是空列表,则调用其第一项
w1[0].paint()
clock.tick(0)#将帧数限制设为无限制
pygame.display.flip()#刷新屏幕
用到的模块有:pygame,time
导入图片代码:
img = pygame.image.load(path).convert_alpha()#path:路径
#convert_alpha()通俗来讲就是优化,提高帧数,并且保留图片的透明像素
#convert()不保留透明像素