在 pygame 中渲染多行文本

2025-02-18 09:23:00
admin
原创
45
摘要:问题描述:我正在尝试制作一款游戏,并尝试渲染大量文本。当文本渲染时,其余文本会从屏幕上消失。有没有简单的方法可以让文本转到 pygame 窗口的下一行?helpT = sys_font.render \n ("This game is a combination of ...

问题描述:

我正在尝试制作一款游戏,并尝试渲染大量文本。当文本渲染时,其余文本会从屏幕上消失。有没有简单的方法可以让文本转到 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 的简单文本换行。

我扩展了该函数并添加了一个附加参数,它提供对齐或对齐文本、居中文本甚至模式。

最小示例:![IT科技](https://i.sstatic.net/5jD0C.png) repl.it/@Rabbid76/PyGame-TextWrap

IT科技IT科技

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()

pygame 多行文本

解决方案 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只需按照以下步骤操作:

  1. pip uninstall pygame(为避免冲突,只有已安装后才可以执行此操作pygame

  2. pip install pygame-ce

  3. 享受全新且出色的功能

关于此分支的更多信息可以在这里找到: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
相关推荐
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1325  
  IPD(Integrated Product Development)流程作为一种先进的产品开发管理模式,在众多企业中得到了广泛应用。它涵盖了从产品概念产生到产品退市的整个生命周期,通过整合跨部门团队、优化流程等方式,显著提升产品开发的效率和质量,进而为项目的成功奠定坚实基础。深入探究IPD流程的五个阶段与项目成功之间...
IPD流程分为几个阶段   4  
  华为作为全球知名的科技企业,其成功背后的管理体系备受关注。IPD(集成产品开发)流程作为华为核心的产品开发管理模式,其中的创新管理与实践更是蕴含着丰富的经验和深刻的智慧,对众多企业具有重要的借鉴意义。IPD流程的核心架构IPD流程旨在打破部门墙,实现跨部门的高效协作,将产品开发视为一个整体的流程。它涵盖了从市场需求分析...
华为IPD是什么   3  
  IPD(Integrated Product Development)研发管理体系作为一种先进的产品开发模式,在众多企业的发展历程中发挥了至关重要的作用。它不仅仅是一套流程,更是一种理念,一种能够全方位提升企业竞争力,推动企业持续发展的有效工具。深入探究IPD研发管理体系如何助力企业持续发展,对于众多渴望在市场中立足并...
IPD管理流程   3  
  IPD(Integrated Product Development)流程管理旨在通过整合产品开发流程、团队和资源,实现产品的快速、高质量交付。在这一过程中,有效降低成本是企业提升竞争力的关键。通过优化IPD流程管理中的各个环节,可以在不牺牲产品质量和性能的前提下,实现成本的显著降低,为企业创造更大的价值。优化产品规划...
IPD流程分为几个阶段   4  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用