在 pygame 中呈现多行文本

Rendering text with multiple lines in pygame

我正在尝试制作游戏,并且正在尝试渲染大量文本。当文本呈现时,其余文本会从屏幕上消失。有什么简单的方法可以让文本转到 pygame window 的下一行?

helpT = sys_font.render \
                ("This game is a combination of all of the trends\n 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))

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

正如我在评论中所说;您必须分别呈现每个单词并计算文本的宽度是否扩展了表面(或屏幕)的宽度。这是一个例子:

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.\nSometimes it will break even if there isn't a break " \
       "in the sentence, but that's because the text is too long to fit the screen.\nIt 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 " \
       "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()

结果

我就是这样做的

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)

您可以做的一件事是使用等宽字体。它们的所有字符都具有相同的大小,因此深受程序员的喜爱。这将是我处理 height/width 问题的解决方案。

您可以使用 .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

结果:

希望对您有所帮助!

我推荐 ptext 库,它可以识别换行符 (\n)。您只需要调用 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()

基于之前的答案,我制作了一个比较全面的 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)

创建的这个函数可能有点帮助:)只需确保每个新段落都是您调用此函数的列表中的新项目。

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

没有自动解决方案。你必须自己实现文本换行,逐行逐字绘制文本。
幸运的是 PyGame wiki 为这个任务提供了一个功能。请参阅 PyGame 维基 Simple Text Wrapping for pygame

我扩展了函数并添加了一个额外的参数,它提供 leftright 对齐的文本, centered 文本甚至 block 模式。

最小示例: 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()

这与其他人发布的内容类似,但我想我也会上传自己的代码:

    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() + 2
            else:
                y += word_t.get_height() + 4
                x = x_start
                surface.blit(word_t, (x, y))
                x += word_t.get_width() + 2

我认为这很容易解释,您输入您希望文本开始的位置 (x_start) 和文本应该结束的位置,然后它几乎会下降,直到您输入的字符串完成。

我在个人项目中使用的例子:

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)

不过,如果您希望文本合理,则必须进行一些小的更改。