在 pygame 中渲染多行文本
- 2025-02-18 09:23:00
- admin 原创
- 45
问题描述:
我正在尝试制作一款游戏,并尝试渲染大量文本。当文本渲染时,其余文本会从屏幕上消失。有没有简单的方法可以让文本转到 pygame 窗口的下一行?
helpT = sys_font.render \n ("This game is a combination of all of the trends
of 2016. When you press 'Start Game,' a menu will pop up. In order to beat the game, you must get a perfect score on every single one of these games.",0,(hecolor))
screen.blit(helpT,(0, 0))
解决方案 1:
正如我在评论中所说;您必须单独渲染每个单词,并计算文本的宽度是否延伸到表面(或屏幕)的宽度。以下是一个例子:
import pygame
pygame.init()
SIZE = WIDTH, HEIGHT = (1024, 720)
FPS = 30
screen = pygame.display.set_mode(SIZE, pygame.RESIZABLE)
clock = pygame.time.Clock()
def blit_text(surface, text, pos, font, color=pygame.Color('black')):
words = [word.split(' ') for word in text.splitlines()] # 2D array where each row is a list of words.
space = font.size(' ')[0] # The width of a space.
max_width, max_height = surface.get_size()
x, y = pos
for line in words:
for word in line:
word_surface = font.render(word, 0, color)
word_width, word_height = word_surface.get_size()
if x + word_width >= max_width:
x = pos[0] # Reset the x.
y += word_height # Start on new row.
surface.blit(word_surface, (x, y))
x += word_width + space
x = pos[0] # Reset the x.
y += word_height # Start on new row.
text = "This is a really long sentence with a couple of breaks.
Sometimes it will break even if there isn't a break " \n "in the sentence, but that's because the text is too long to fit the screen.
It can look strange sometimes.
" \n "This function doesn't check if the text is too high to fit on the height of the surface though, so sometimes " \n "text will disappear underneath the surface"
font = pygame.font.SysFont('Arial', 64)
while True:
dt = clock.tick(FPS) / 1000
for event in pygame.event.get():
if event.type == pygame.QUIT:
quit()
screen.fill(pygame.Color('white'))
blit_text(screen, text, (20, 20), font)
pygame.display.update()
结果
解决方案 2:
在 pygame 中没有简单的方法可以在多行上渲染文本,但这个辅助函数可以为您提供一些帮助。只需传入您的文本(带换行符)、x、y 和字体大小即可。
def render_multi_line(text, x, y, fsize)
lines = text.splitlines()
for i, l in enumerate(lines):
screen.blit(sys_font.render(l, 0, hecolor), (x, y + fsize*i))
解决方案 3:
没有自动解决方案。您必须自己实现文本换行,然后逐行逐字地绘制文本。
幸运的是,PyGame wiki 提供了一个用于此任务的功能。请参阅 PyGame wiki pygame 的简单文本换行。
我扩展了该函数并添加了一个附加参数,它提供左对齐或右对齐文本、居中文本甚至块模式。
最小示例: repl.it/@Rabbid76/PyGame-TextWrap
import pygame
pygame.init()
font = pygame.font.SysFont(None, 40)
textAlignLeft = 0
textAlignRight = 1
textAlignCenter = 2
textAlignBlock = 3
def drawText(surface, text, color, rect, font, align=textAlignLeft, aa=False, bkg=None):
lineSpacing = -2
spaceWidth, fontHeight = font.size(" ")[0], font.size("Tg")[1]
listOfWords = text.split(" ")
if bkg:
imageList = [font.render(word, 1, color, bkg) for word in listOfWords]
for image in imageList: image.set_colorkey(bkg)
else:
imageList = [font.render(word, aa, color) for word in listOfWords]
maxLen = rect[2]
lineLenList = [0]
lineList = [[]]
for image in imageList:
width = image.get_width()
lineLen = lineLenList[-1] + len(lineList[-1]) * spaceWidth + width
if len(lineList[-1]) == 0 or lineLen <= maxLen:
lineLenList[-1] += width
lineList[-1].append(image)
else:
lineLenList.append(width)
lineList.append([image])
lineBottom = rect[1]
lastLine = 0
for lineLen, lineImages in zip(lineLenList, lineList):
lineLeft = rect[0]
if align == textAlignRight:
lineLeft += + rect[2] - lineLen - spaceWidth * (len(lineImages)-1)
elif align == textAlignCenter:
lineLeft += (rect[2] - lineLen - spaceWidth * (len(lineImages)-1)) // 2
elif align == textAlignBlock and len(lineImages) > 1:
spaceWidth = (rect[2] - lineLen) // (len(lineImages)-1)
if lineBottom + fontHeight > rect[1] + rect[3]:
break
lastLine += 1
for i, image in enumerate(lineImages):
x, y = lineLeft + i*spaceWidth, lineBottom
surface.blit(image, (round(x), y))
lineLeft += image.get_width()
lineBottom += fontHeight + lineSpacing
if lastLine < len(lineList):
drawWords = sum([len(lineList[i]) for i in range(lastLine)])
remainingText = ""
for text in listOfWords[drawWords:]: remainingText += text + " "
return remainingText
return ""
msg = "Simple function that will draw text and wrap it to fit the rect passed. If there is any text that will not fit into the box, the remaining text will be returned."
textRect = pygame.Rect(100, 100, 300, 300)
window = pygame.display.set_mode((500, 500))
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
window.fill((255, 255, 255))
pygame.draw.rect(window, (0, 0, 0), textRect, 1)
drawTextRect = textRect.inflate(-5, -5)
drawText(window, msg, (0, 0, 0), drawTextRect, font, textAlignBlock, True)
pygame.display.flip()
pygame.quit()
exit()
解决方案 4:
我推荐ptext
能够识别换行符(`)的库。你只需要调用
ptext.draw(text, position)`。
import pygame as pg
import ptext
pg.init()
screen = pg.display.set_mode((640, 480))
clock = pg.time.Clock()
BG_COLOR = pg.Color('gray12')
BLUE = pg.Color('dodgerblue')
# Triple quoted strings contain newline characters.
text = """Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
eiusmod tempor incididunt ut labore et dolore magna aliqua.
Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris
nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in
reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
pariatur. Excepteur sint occaecat cupidatat non proident, sunt in
culpa qui officia deserunt mollit anim id est laborum."""
done = False
while not done:
for event in pg.event.get():
if event.type == pg.QUIT:
done = True
screen.fill(BG_COLOR)
ptext.draw(text, (10, 10), color=BLUE) # Recognizes newline characters.
pg.display.flip()
clock.tick(60)
pg.quit()
解决方案 5:
这与其他人发布的内容类似,但我想我也会上传自己的代码:
def box_text(surface, font, x_start, x_end, y_start, text, colour):
x = x_start
y = y_start
words = text.split(' ')
for word in words:
word_t = font.render(word, True, colour)
if word_t.get_width() + x <= x_end:
surface.blit(word_t, (x, y))
x += word_t.get_width() * 1.1
else:
y += word_t.get_height() * 1.1
x = x_start
surface.blit(word_t, (x, y))
x += word_t.get_width() * 1.1
我认为这是相当不言自明的:
输入文本开始位置(x_start,y_start)和结束位置(x_end)的坐标
代码循环遍历文本中的所有单词,只有当单词宽度小于行中剩余的空间时才将它们添加到屏幕上。如果不是这种情况,则从 x_start 开始一个新行,y 坐标的增加量略大于字体的高度。
我在个人项目中使用的示例:
x_start = self.W / 2 - info_box.get_width() / 2 + 10
self.box_text(self.WINDOW, self.info_font, x_start, x_start + 430, self.H / 3 + 10, self.mage_text, self.white)
如果您希望文本对齐,您可以直接到行尾而不真正将单词添加到屏幕上,计算行尾剩余的空间,然后使用修改后的“空间长度”再次循环遍历该行,从而使最后一个单词准确地到达您的框边缘。
解决方案 6:
从pygame-ce
(的现代发行版pygame
) 版本开始2.1.4
pygame.Font.render
,现在具有换行符 ( `"
"`) 和 wraplength(换到新行之前的宽度(以像素为单位))支持。
因此,有了这些新颖的功能,解决这个问题就变得轻而易举了,您只需传递所需的文本pygame.Font.render
,并指定窗口宽度作为最后一个参数,这样一旦文本达到窗口宽度,它就会换行。由于此方法还不接受关键字参数,因此您还必须提供背景颜色参数,它可以是,None
或者您可以提供一个 alpha 值为 0 的颜色值。
以下是一个简短的代码示例:
import pygame
WIDTH, HEIGHT = 640, 360
FPS = 60
pygame.init()
screen = pygame.display.set_mode((WIDTH, HEIGHT))
clock = pygame.time.Clock()
font = pygame.Font(None, 32)
text_surf = font.render(
"This game is a combination of all of the trends
"
"of 2016. When you press 'Start Game,' a menu will pop up. "
"In order to beat the game, you must get a perfect score on every single one of these games.",
True,
"white",
None, # or (0, 0, 0, 0) for example
WIDTH,
)
running = True
while running:
clock.tick(FPS)
screen.fill("black")
events = pygame.event.get()
for event in events:
if event.type == pygame.QUIT:
running = False
screen.blit(text_surf, (0, 0))
pygame.display.flip()
结果如下:
此外,要安装最新版本,pygame-ce
只需按照以下步骤操作:
pip uninstall pygame
(为避免冲突,只有已安装后才可以执行此操作pygame
)pip install pygame-ce
享受全新且出色的功能
关于此分支的更多信息可以在这里找到:Pygame:社区版公告
,具体版本公告在这里:Pygame-ce 2.1.4 发布
解决方案 7:
这就是我的做法
amfolyt_beskrivelse_text = ['en amfolyt er et stof som både kan være en base, eller syre','så som']
for x in amfolyt_beskrivelse_text:
descriptioncounter += 1
screen.blit((pygame.font.SysFont('constantia',12).render(x, True, BLACK)),(300,10*descriptioncounter))
descriptioncounter = 0
当然,我只能这样做,因为我的文本从屏幕顶部开始一行距离。如果你从屏幕下方开始,你可以这样做
(300,12+12*descriptioncounter)
解决方案 8:
创建这个函数可能会有一点帮助:)只需确保每个新段落都是您在此函数上调用的列表上的新项目。
def multilineText(Surface, textAsList: list, font: str, size: int, colour, antialias: bool, centerTupleCoord: tuple, spaceBetweenLines: int):
xPosition = centerTupleCoord[0]
yPosition = centerTupleCoord[1]
for paragraph in textAsList:
fontObjsrt = pygame.font.SysFont(font, size)
TextSurf = fontObjsrt.render(paragraph, antialias, colour)
TextRect = TextSurf.get_rect()
TextRect.center = (xPosition, yPosition)
Surface.blit(TextSurf, TextRect)
yPosition += spaceBetweenLines
解决方案 9:
你可以做的一件事是使用等宽字体。它们的所有字符大小相同,因此深受程序员的喜爱。这将是我处理高度/宽度问题的解决方案。
解决方案 10:
您可以使用 .json 文件来加载每一行。
.json 文件(名为 first.json):
["Hello!", "How's it going?"]
然后将其加载到文件中:
sys_font = pygame.font.SysFont(("Arial"),30)
def message_box(text):
pos = 560 # depends on message box location
pygame.draw.rect(root, (0,0,0), (100, 550, 800, 200)) #rectangle position varies
for x in range(len(text)):
rendered = sys_font.render(text[x], 0, (255,255,255))
root.blit(rendered, ( 110, pos))
pos += 30 # moves the following line down 30 pixels
with open('first.json') as text:
message_box(json.load(text))
别忘了import json
结果:
希望这有帮助!
解决方案 11:
基于以前的答案,我制作了一个比较全面的 blit 文本函数,我可以用一个简短的命令来使用:
def blittext(text, **kwargs):
#blit text into screen, uses defaults and works for multiline strings
fontface = kwargs.get('font', 'PressStart2P-Regular.ttf')
b = kwargs.get('bold', False)
fontsize = kwargs.get('size', 30)
color = kwargs.get('color', (255, 255, 255))
topleft = kwargs.get('topleft',(w/2,h/2))
center = kwargs.get('center')
textsurf = kwargs.get('surface',surface)
maxwidth = kwargs.get('width',w)
try:
myfont = pygame.font.Font('/storage/emulated/0/games/' + fontface, fontsize)
except:
myfont = pygame.font.SysFont(fontface, fontsize, bold=b)
x,y = topleft
charwidth = myfont.size(' ')[0]
charheight = fontsize + 3
if center:
for l in text.splitlines():
mytext = myfont.render(l, False, color)
textrect = mytext.get_rect(center=center)
center = (center[0],center[1]+charheight)
textsurf.blit(mytext,textrect)
else:
for line in text.splitlines():
for word in line.split(' '):
mytext = myfont.render(word, False, color)
textrect = mytext.get_rect(topleft=(x,y))
wordwidth = textrect.width
if x + wordwidth >= maxwidth:
x,y = (topleft[0], y + charheight)
textsurf.blit(mytext,(x,y))
x += charwidth + wordwidth
x,y = (topleft[0], y + charheight)
解决方案 12:
text_ml_surface创建表面。
重复使用创建的表面以有效节省资源并管理输出,例如滚动。
from pygame import SRCALPHA
from pygame import Color, Surface
from pygame.font import Font
SURFACE_ARGS = dict(flags=SRCALPHA, depth=32)
def text_ml_surface(font_obj: Font, ml_text: str, color: str, width: int) -> Surface:
words_by_lines = [line.split(' ') for line in ml_text.splitlines()]
space_width = font_obj.size(' ')[0]
color_obj = Color(color)
font_line_height = font_obj.get_linesize()
# get height
x, y = 0, 0
for line in words_by_lines:
for word in line:
word_width = font_obj.size(word)[0]
if x + word_width >= width:
x = 0
y += font_line_height
x += word_width + space_width
x = 0
y += font_line_height
# render
surface = Surface((width, y), **SURFACE_ARGS)
x, y = 0, 0
for line in words_by_lines:
for word in line:
word_surface = font_obj.render(word, True, color_obj)
word_width = word_surface.get_size()[0]
if x + word_width >= width:
x = 0
y += font_line_height
surface.blit(word_surface, (x, y))
x += word_width + space_width
x = 0
y += font_line_height
return surface
- 2025年20款好用的项目管理软件推荐,项目管理提效的20个工具和技巧
- 2024年开源项目管理软件有哪些?推荐5款好用的项目管理工具
- 2024年常用的项目管理软件有哪些?推荐这10款国内外好用的项目管理工具
- 项目管理软件有哪些?推荐7款超好用的项目管理工具
- 项目管理软件有哪些最好用?推荐6款好用的项目管理工具
- 项目管理软件哪个最好用?盘点推荐5款好用的项目管理工具
- 项目管理软件排行榜:2024年项目经理必备5款开源项目管理软件汇总
- 项目管理必备:盘点2024年13款好用的项目管理软件
- 项目管理软件有哪些,盘点推荐国内外超好用的7款项目管理工具
- 2024项目管理软件排行榜(10类常用的项目管理工具全推荐)