【问题标题】:ValueError: subsurface rectangle outside surface areaValueError:表面区域外的次表面矩形
【发布时间】:2021-07-31 23:35:30
【问题描述】:

我正在制作一款相机跟随玩家的平台游戏。我试图通过在整个地图上拥有一个大的表面表面并且只对放大的部分进行 blitting 来实现这一点。但是我只获得 30 fps(最小化)和 8 fps(全屏)。

所以我尝试优化它是在 blitting 之前对其进行裁剪,但我得到 ValueError: subsurface rectangle outside surface area

代码

class screen_handler:
    def __init__(self, screen=False, mapSize=[3, 3]):
        if not screen:  # if screen isn't open
            init()  # initialize pygame
            user32 = ctypes.windll.user32  # set user32
            os.environ['SDL_VIDEO_WINDOW_POS'] = "%d,%d" % (user32.GetSystemMetrics(0) / 4, user32.GetSystemMetrics(1) / 4)  # center future screen
            screen = display.set_mode((640, 512),  RESIZABLE)  # create screen

        self.screen = screen  # save screen
        self.blit_surf = Surface((640 * mapSize[0], 512 * mapSize[1]))  # create blit_surf
        self.clock = time.Clock() # create clock
        self.neutralizerZoom = min(self.blit_surf.get_width() / 640, self.blit_surf.get_height() / 512)  # reset zoom

        self.zoom = 2
        self.mousePos = [0, 0]
        self.cameraPos = [0, 0]

        self.fit_to_rect = self.blit_surf.get_rect().fit(self.screen.get_rect())  # fit the surface to the screen
        self.fit_to_rect.size = self.fit_to_rect.width * self.neutralizerZoom * self.zoom, self.fit_to_rect.height * self.neutralizerZoom * self.zoom  # add zoom



    def video_resize(self):
        self.fit_to_rect = self.blit_surf.get_rect().fit(self.screen.get_rect())  # fit the surface to the screen
        self.fit_to_rect.size = self.fit_to_rect.width * self.neutralizerZoom * self.zoom, self.fit_to_rect.height * self.neutralizerZoom * self.zoom  # add zoom

    def update(self):
        scaled = transform.scale(self.blit_surf, (self.fit_to_rect.width, self.fit_to_rect.height))  # scale surface to screen
        self.fit_to_rect.topleft = self.screen.get_rect().top + self.cameraPos[0], self.screen.get_rect().left + self.cameraPos[1]  # center surface & camera pos

        self.mousePos[0] = (mouse.get_pos()[0] / (scaled.get_width() / self.blit_surf.get_width())) - (self.cameraPos[0] / (scaled.get_width() / self.blit_surf.get_width()))  # scale x axis mouse pos
        self.mousePos[1] = (mouse.get_pos()[1] / (scaled.get_height() / self.blit_surf.get_height()))  # scale y axis mouse pos
        scaled = scaled.subsurface(self.fit_to_rect.x, self.fit_to_rect.y, self.fit_to_rect.x + self.fit_to_rect.width, self.fit_to_rect.y + self.fit_to_rect.height)
        self.screen.blit(scaled ,(0, 0))  # blit surface to screen
        #self.screen.blit(scaled, self.fit_to_rect)
        display.flip()  # update screen
        self.clock.tick(60)
        print(self.clock.get_fps())

注意:请告诉我是否有更好/更快的方式来实现相机

【问题讨论】:

  • 错误出现在哪一行?
  • @atanay 第 33 行 - scaled = scaled.subsurface(self.fit_to_rect)
  • 我认为问题在于您在地下函数中传递的矩形的形式是 (x,y,width,height) 而不是 (x,y,x+width,y+height )。一种检查方法是打印 self.fit_to_rect 并自己检查
  • 这样吗? (编辑代码)
  • 尝试运行它,我们会看到:)

标签: python pygame


【解决方案1】:

这是我的相机移动方式:

WINDOW_WIDTH, WINDOW_HEIGHT = ...
window = pygame.display.set_mode((WINDOW_WIDTH, WINDOW_HEIGHT), RESIZABLE)
screen = pygame.Surface(your_resolution)
...
scroll_x, scroll_y = player_position  # get the scroll
...
screen.blit(image, (x_pos + scroll_x, y_pos + scroll_y))
...
for event in pygame.event.get():
    if event.type == VIDEORESIZE:
        WINDOW_WIDTH, WINDOW_HEIGHT = event.size
...
window.blit(pygame.transform.scale(screen, (WINDOW_WIDTH, WINDOW_HEIGHT)), (0, 0))
pygame.display.update()

每次你想展示一些东西时,你需要将它放到屏幕上而不是窗口上。

如果你想拥有相同的规模,我会推荐以下课程:

class Window:
    def __init__(self, surf, width, height):
        self.screen = pygame.display.set_mode((width, height), RESIZABLE)
        self.surf = surf
        self.orig_w, self.orig_h = surf.get_size()
        self.set_sizes(width, height)

    def set_sizes(self, width, height):
        self.rate = min(width / self.orig_w, height / self.orig_h)
        self.width = int(self.orig_w * self.rate)
        self.x_off = int((width - self.width) / 2)
        self.height = int(self.orig_h * self.rate)
        self.y_off = int((height - self.height) / 2)

    def get_mouse_pos(self):
        mouse_x, mouse_y = pygame.mouse.get_pos()
        return int((mouse_x - self.x_off) / self.rate), int((mouse_y - self.y_off) / self.rate)

    def show(self):
        self.screen.fill((50, 50, 50))
        self.screen.blit(pygame.transform.scale(self.surf, (self.width, self.height)), (self.x_off, self.y_off))
        pygame.display.flip()

编辑:优化

以下代码将替换导致您出现问题的行:
而不是

scaled = scaled.subsurface(...)
self.screen.blit(scaled, (0, 0))

self.screen.blit(scaled, (0, 0), self.fit_to_rect)

这更有效,因为它不需要创建次表面,但 blits 直接在屏幕上。 优化提示: 避免每帧都重新创建表面。
您的大表面只需要在加载地图时创建,并且不再需要。如果您正在旋转图像,您可以在程序开始时简单地创建一个旋转图像的列表或字典,并且只需要调用它。规模变化也是如此。

使用img = img.convert()
这是一个非常简单的优化技巧。

【讨论】:

  • 我确实尝试过这样做,但我只得到 8fps
  • 如果性能低,你应该考虑优化。您提到的是一个包含整个关卡的大表面。 (这个表面不需要每帧都重新创建)你可以通过使用第三个区域参数来选择你想要对这个巨大表面的哪个部分进行 blit。我将编辑我的答案以包含不同的优化
  • @lukan 我添加了优化,还包括了一个可能的修复。如果您能检查这是否有帮助,我将不胜感激。
  • 非常感谢,如果我不每帧都对地图进行 blit,我如何更新角色?
  • 你仍然需要每帧blit它。你可以优化的是创建复制表面(我不知道你如何加载你的地图并做物理和东西,如果你能提供更多细节的话我可以(希望)提供适合您情况的解决方案。)
猜你喜欢
  • 1970-01-01
  • 2022-12-15
  • 1970-01-01
  • 1970-01-01
  • 2018-07-12
  • 1970-01-01
  • 1970-01-01
  • 2013-04-14
  • 1970-01-01
相关资源
最近更新 更多