为何未被引用的default_rect变量值会随rect改变?
问题分析与解决方案
问题描述
我为缩放和滚动功能创建了基准矩形default_rect与绘制用矩形rect,试图单独修改rect的y值,但default_rect的数值也随之改变。编写最小复现示例后发现,调用update_rect()方法时仅读取default_rect.centery,它的值却也被修改,且矩形y值会在数秒内持续变为-1000000。初始化时"Test"仅打印一次,排除__init__多次执行的可能。
根本原因
在Python中,对象赋值是引用传递,原代码中self.default_rect = self.rect这行代码并没有创建新的矩形对象,而是让default_rect和rect指向同一个pygame.Rect实例。所以当你修改rect的属性时,default_rect作为同一个对象的引用,其属性自然会同步变化。这就导致update_rect()每次计算时,都是基于已经被修改过的default_rect.centery累加偏移量,最终y值会无限减小。
修复方案
需要为default_rect创建一个独立的Rect副本,使用pygame.Rect.copy()方法即可实现:
修改Element类的__init__方法:
class Element(pg.sprite.Sprite): def __init__(self, img, x, y, placement="center"): super().__init__() self.image = img if placement == "topright": self.rect = self.image.get_rect(topright=(x, y)) if placement == "center": self.rect = self.image.get_rect(center=(x, y)) # 创建Rect副本,避免引用同一对象 self.default_rect = self.rect.copy() print("Test") def update_rect(self, y_offset): self.rect.centery = self.default_rect.centery + y_offset
完整修复示例代码
import pygame as pg pg.init() screen_width, screen_height = 800, 600 screen = pg.display.set_mode((screen_width, screen_height)) grey = (150, 150, 150) dark_grey = (60, 60, 60) class Element(pg.sprite.Sprite): def __init__(self, img, x, y, placement="center"): super().__init__() self.image = img if placement == "topright": self.rect = self.image.get_rect(topright=(x, y)) if placement == "center": self.rect = self.image.get_rect(center=(x, y)) # 创建Rect副本,避免引用同一对象 self.default_rect = self.rect.copy() print("Test") def update_rect(self, y_offset): self.rect.centery = self.default_rect.centery + y_offset class ScrollBar(Element): def __init__(self, x, y, total_bar_w, total_bar_h, total_h): img = pg.Surface((total_bar_w, total_bar_h)) img.fill(dark_grey) super().__init__(img, x, y, "topright") bar_w = 0.98*total_bar_w bar_h = (total_bar_h / total_h) * total_bar_h bar_img = pg.Surface((bar_w, bar_h)) bar_img.fill(grey) self.bar = Element(bar_img, x, y, "topright") self.total_h = total_h self.ratio = self.total_h / self.rect.h self.offset = 0 self.y_offset = 0 self.pressed = False self.active = False def update(self): pos = pg.mouse.get_pos() click = pg.mouse.get_pressed()[0] # Check if the slider bar was pressed if click and self.rect.collidepoint(pos): self.active = True # Checking if mouse was released or mouse left the allowed zone if not click or abs(pos[0] - self.rect.midleft[0]) > self.rect.w*3: self.active = False if self.active: self.bar.rect.centery = pos[1] if self.bar.rect.top < 0: self.bar.rect.top = 0 if self.bar.rect.bottom > self.rect.top + self.rect.h: self.bar.rect.bottom = self.rect.top + self.rect.h # Calculate y offset for elements. Multiply by negative # to allow us to add the offset rather than subtract self.y_offset = (self.bar.rect.centery - self.bar.rect.h / 2) * -self.ratio def draw(self): screen.blit(self.image, (self.rect.x, self.rect.y)) screen.blit(self.bar.image, (self.bar.rect.x, self.bar.rect.y)) class Button(Element): def __init__(self, x, y, w, h): super().__init__(pg.Surface((w, h)), x, y, "center") self.clicked = False def update(self): self.update_rect(scroll_bar.y_offset) action = False if self.rect.collidepoint(pg.mouse.get_pos()): if pg.mouse.get_pressed()[0] and not self.clicked: self.clicked = True action = True if not pg.mouse.get_pressed()[0]: self.clicked = False return action def draw(self): pg.draw.rect(screen, "blue", self.rect) button1 = Button(400, 100, 200, 150) button2 = Button(400, 300, 200, 150) button3 = Button(400, 500, 200, 150) buttons = [button1, button2, button3] total_height = screen_height * 2 scroll_bar = ScrollBar(screen_width, 0, 0.05 * screen_width, screen_height, total_height) run = True while run: screen.fill("white") for button in buttons: button.draw() if button.update(): print("Button's y value is: " + str(button.rect.y)) scroll_bar.update() scroll_bar.draw() for e in pg.event.get(): if e.type == pg.QUIT: run = False if e.type == pg.KEYDOWN: if e.key == pg.K_SPACE: print(f"{button1.rect.y} / {button2.rect.y} / {button3.rect.y}") if e.key == pg.K_r: print(scroll_bar.y_offset) pg.display.update()
内容的提问来源于stack exchange,提问作者user19706742
相关产品推荐
相关产品推荐

