放大和缩小 PyGame window,所有对象仍然在原位

Zooming in and out of a PyGame window with all objects still in place

我遇到了 PyGame 无法解决的问题。所以: 我的想法是我有一张可以放大 in/out 的地图。放大效果很好。但是缩小显示图片的其余部分已被删除,现在只存在以前在 window 上可见的图像部分。 这是我的代码:

import pygame
from pygame.locals import *
import os

class App:
    def __init__(self):
        self.running = True
        self.size = (800,600)

         #create window
        self.window = pygame.display.set_mode(self.size, pygame.DOUBLEBUF | pygame.HWSURFACE | pygame.RESIZABLE)

        #create map
        currentdir = os.path.dirname(os.path.realpath(__file__))
        imagedir = currentdir+'/images/europe.png'
        self.map =  pygame.image.load(imagedir)
        self.maprect = self.map.get_rect()
        self.mapsurface = pygame.Surface(self.size)
        self.mapsurface.blit(pygame.transform.scale(self.map,(self.size)),(0,0))
        self.window.blit(self.mapsurface,(0,0))
        self.scale = 1

        #create window
        pygame.display.flip()

    def on_init(self):
        self.country = Country()

    def on_cleanup(self):
        pygame.quit()
        
    def check_event(self,event):
        if event.type == pygame.QUIT:
            self.running = False
        elif event.type == pygame.VIDEORESIZE:
            self.window = pygame.display.set_mode(event.dict['size'], pygame.DOUBLEBUF | pygame.HWSURFACE | pygame.RESIZABLE)
            self.window.blit(pygame.transform.scale(self.map,(event.dict['size'])),(0,0))
        elif event.type == pygame.MOUSEBUTTONDOWN:
            if event.button == 4:
                zoom = 2
                wnd_w,wnd_h = self.window.get_size()
                zoom_size = (round(wnd_w/zoom), round(wnd_h/zoom))
                zoom_area = pygame.Rect(0,0, *zoom_size)
                pos_x,pos_y = pygame.mouse.get_pos()
                zoom_area.center = (pos_x, pos_y)
                zoom_surf = pygame.Surface(zoom_area.size)
                zoom_surf.blit(self.window, (0, 0), zoom_area)
                zoom_surf = pygame.transform.smoothscale(zoom_surf, (wnd_w, wnd_h))
                self.window.blit(zoom_surf, (0, 0))

            elif event.button == 5:
                zoom = 0.5
                wnd_w,wnd_h = self.window.get_size()
                zoom_size = (round(wnd_w/zoom), round(wnd_h/zoom))
                zoom_area = pygame.Rect(0,0,*zoom_size)
                pos_x,pos_y = pygame.mouse.get_pos()
                zoom_area.center = (pos_x, pos_y)
                zoom_surf = pygame.Surface(zoom_area.size)
                zoom_surf.blit(self.window, (0, 0), zoom_area)
                zoom_surf = pygame.transform.smoothscale(zoom_surf, (wnd_w, wnd_h))
                self.window.blit(zoom_surf, (0, 0))
            pygame.display.flip()

        pygame.display.update()
    def on_execute(self):
        while self.running == True:
            for event in pygame.event.get():
                self.check_event(event)
        self.on_cleanup()

class Country(App):
    def __init__(self):
        super().__init__()
    

start = App()
start.on_init()
start.on_execute()

这是我的问题的截图:

到目前为止一切顺利:

放大效果很好:

缩小会导致:

缩放时需要缩放 blit 原始图像。使用属性 maprect 定义地图的比例大小和相对位置。添加一个方法 blit 地图,可以缩放和 blit 地图。在 class App:

的构造函数中使用方法
class App:
    def __init__(self):
        # [...]

        self.map =  pygame.image.load(imagedir)
        self.maprect = self.map.get_rect(center = self.window.get_rect().center)
        self.blitmap()

        #create window
        pygame.display.flip()

    def blitmap(self):
        self.mapsurface = pygame.transform.smoothscale(self.map, self.maprect.size)
        self.window.fill(0)
        self.window.blit(self.mapsurface, self.maprect)

    # [...]

图片缩放后,计算新的映射矩形(self.maprect),再次调用方法:

class App:
    # [...]

    def check_event(self,event):
        if event.type == pygame.QUIT:
            self.running = False

        elif event.type == pygame.VIDEORESIZE:
            self.window = pygame.display.set_mode(event.dict['size'], pygame.DOUBLEBUF | pygame.HWSURFACE | pygame.RESIZABLE)
            self.blitmap()

        elif event.type == pygame.MOUSEBUTTONDOWN:
            if event.button == 4 or event.button == 5:
                zoom = 2 if event.button == 4 else 0.5
                mx, my = event.pos
                left   = mx + (self.maprect.left - mx) * zoom
                right  = mx + (self.maprect.right - mx) * zoom
                top    = my + (self.maprect.top - my) * zoom
                bottom = my + (self.maprect.bottom - my) * zoom
                self.maprect = pygame.Rect(left, top, right-left, bottom-top)
                self.blitmap()

另见 Scale and zoom window


完整示例:

repl.it/@Rabbid76/PyGame-ZoomInAndOut

import pygame
from pygame.locals import *
import os

class App:
    def __init__(self):
        self.running = True
        self.size = (800,600)

         #create window
        self.window = pygame.display.set_mode(self.size, pygame.DOUBLEBUF | pygame.HWSURFACE | pygame.RESIZABLE)

        #create map
        currentdir = os.path.dirname(os.path.realpath(__file__))
        imagedir = currentdir+'/images/europe.png'
        self.map =  pygame.image.load(imagedir)
        self.maprect = self.map.get_rect(center = self.window.get_rect().center)
        self.blitmap()
        
        #create window
        pygame.display.flip()

    def blitmap(self):
        self.mapsurface = pygame.transform.smoothscale(self.map, self.maprect.size)
        self.window.fill(0)
        self.window.blit(self.mapsurface, self.maprect)

    def on_init(self):
        self.country = Country()

    def on_cleanup(self):
        pygame.quit()
        
    def check_event(self,event):
        if event.type == pygame.QUIT:
            self.running = False
        
        elif event.type == pygame.VIDEORESIZE:
            self.window = pygame.display.set_mode(event.dict['size'], pygame.DOUBLEBUF | pygame.HWSURFACE | pygame.RESIZABLE)
            self.blitmap()
        
        elif event.type == pygame.MOUSEBUTTONDOWN:
            if event.button == 4 or event.button == 5:
                zoom = 2 if event.button == 4 else 0.5
                mx, my = event.pos
                left   = mx + (self.maprect.left - mx) * zoom
                right  = mx + (self.maprect.right - mx) * zoom
                top    = my + (self.maprect.top - my) * zoom
                bottom = my + (self.maprect.bottom - my) * zoom
                self.maprect = pygame.Rect(left, top, right-left, bottom-top)
                self.blitmap()

        pygame.display.update()

    def on_execute(self):
        while self.running == True:
            for event in pygame.event.get():
                self.check_event(event)
        self.on_cleanup()

class Country(App):
    def __init__(self):
        super().__init__()
    

start = App()
start.on_init()
start.on_execute()