【问题标题】:Overloading pygame.Surface in order to dynamically augment the size of the surface重载 pygame.Surface 以动态增加表面的大小
【发布时间】:2020-04-24 10:51:57
【问题描述】:

我正在尝试构建一个需要用户绘制东西的应用程序。

为此,我创建了一个画布(一个 pygame.Surface 对象),在该画布上注册了绘图,然后将其粘贴到窗口上。我希望画布是 infinite,这样当用户滚动时他可以继续绘图(当然只有一小部分画布会显示在窗口上)。但是,实际上,pygame 中的 Surface 需要有限的宽度和高度,最重要的是,它并没有那么大! (我认为那是因为它实际上锁定了内存中的空间)。

所以,我尝试创建:每个块都有固定的大小(例如,屏幕大小的两倍),并且为每个块分配了一个特定的表面。块是按需动态创建的,总体上运行良好。

我的问题是,当我尝试绘制跨越多个块的线时,需要付出很大的努力来计算该线实际上应该绘制到哪些块上,以及应该在哪些部分中断开。我什至没有尝试绘制矩形,因为真的让“画线”功能工作起来很痛苦。

那时我认为我所做的从根本上是错误的:与其尝试重写所有 pygame.draw 和 pygame.gfxdraw 函数以便它们基本上执行每个块的工作,我真的应该重载 pygame.Surface (例如,创建 Surface 的 MySurface 子类)因此,每当修改像素时,我都会在内部选择它所属的块并实际在该块上更改它,并将新的 Surface 对象传递给 pygame 函数。

我在the pygame doc 进行了很多搜索,但没有说明如何执行此操作。我什至不知道当我对它进行 blit/draw 时,内部调用了 Surface 对象的哪些方法!我也用谷歌搜索了它,但没有发现有人试图做那种事情(也许我走错路了?)。

所以,我的问题是:这是正确的方法吗?如果是,我应该如何实现?

我不发布代码,因为我需要更多的是解释在哪里可以找到我尝试做的文档,而不是代码审查。

【问题讨论】:

    标签: python-3.x pygame pygame-surface


    【解决方案1】:

    你不能只继承Surface,因为它不是用python写的,而是用C写的。Here's the source code;自己找。

    您可以采用另一种方法,而不是计算在哪里绘制内容,而是先将其 blit 到一个临时的 Surface 上,然后将其 blit 到相对于块位置的块。

    这是我一起破解的简单示例:

     import pygame
    
    class Chunk(pygame.sprite.Sprite):
        def __init__(self, grid_pos, size, color):
            super().__init__()
            self.image = pygame.Surface(size)
            self.rect = self.image.get_rect(
                x = grid_pos[0] * size[0],
                y = grid_pos[1] * size[1]
            )
            self.image.fill(pygame.Color(color))
    
        def patch(self, surface):
            self.image.blit(surface, (-self.rect.x, -self.rect.y))
    
    def main():
        pygame.init()
        size = 800, 600
        screen = pygame.display.set_mode(size)
        chunks = pygame.sprite.Group(
            Chunk((0,0), size, 'green'),
            Chunk((1,0), size, 'red'),
            Chunk((0,1), size, 'blue'),
            Chunk((1,1), size, 'yellow')
        )
        dragging = None
        drawing = None
        tmp_s = pygame.Surface(size, pygame.SRCALPHA)
    
        while True:
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    return
                if event.type == pygame.MOUSEBUTTONDOWN:
                    if event.button == 3:
                        dragging = event.pos
                    if event.button == 1:
                        drawing = event.pos
    
                if event.type == pygame.MOUSEBUTTONUP:
                    if event.button == 3:
                        dragging = None
                    if event.button == 1:
                        drawing = None
                        for chunk in chunks:
                            chunk.patch(tmp_s)
    
                if event.type == pygame.MOUSEMOTION:
                    if dragging:
                        for chunk in chunks:
                            chunk.rect.move_ip(event.rel)
    
            screen.fill((0, 0, 0))
            chunks.draw(screen)
    
            tmp_s.fill((0,0,0,0))
            if drawing:
                size = pygame.Vector2(pygame.mouse.get_pos()) - drawing
                pygame.draw.rect(tmp_s, pygame.Color('white'), (*drawing, *size), 10)
    
            screen.blit(tmp_s, (0, 0))
            chunks.update()
            pygame.display.flip()
    
    main()
    

    如您所见,画布由 4 个块组成。使用鼠标右键移动画布,使用鼠标左键开始绘制矩形。

    【讨论】:

    • 拥抱...我想我可以看到两个选择:进入 C 级别或简单地接受您的出色解决方案(我已经做出了选择:P)。顺便说一句,如果我理解得很好,我不必对我的块做任何特别的事情,但我仍然需要为标准绘图函数创建包装器?无论如何,感谢您的回答和反应!
    • 您不必自己包装绘图函数。只是不要直接绘制到屏幕表面。
    • 好的,我已经实现了它,但是我在性能上遭受了巨大的损失。我从 ~300fps 以 ~30fps 传递,只是因为我在每一帧将临时表面粘贴到块上。
    • 当你想永久改变它们时,你只需要将临时表面blit到块上。此外,由于您的块与屏幕一样大或更大,因此最多有四个块需要更改。你应该知道哪些块当前是可见的,并且只将临时表面 blit 到那些。
    • 这实际上是我后来尝试提高效率的方法,(要求块大于屏幕,生成所见块的坐标,检索这些块或如果它们尚不存在则创建它们,最后 blit到它们上),并且只有一次,当用户尝试滚动时,然后重置临时表面。所以我认为我的性能问题来自我的程序中的一个错误,而不是来自你的解决方案,我会修复它(迟早)。感谢您的帮助!
    猜你喜欢
    • 2018-12-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-12
    • 1970-01-01
    • 2013-05-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多