调整屏幕大小时按钮点击框混乱 Pygame
Button hitbox messing up when resizing screen Pygame
我的代码基本如下:
我有一个带有按钮的菜单,它们绘制在主屏幕的副本上,可以平滑缩放,绘制的按钮位于 class.
中
一旦按钮被选中或被鼠标触摸,它就会有一个 'hover' 动画,可以单击以执行命令。问题是一旦调整屏幕大小;变大或变小,按钮的碰撞点乱了,与实际按钮不在一条直线上,无法直接点击。
主要代码:
import pygame
import sys
from pygame.locals import *
from buttonClass import Button
running = True
pygame.init()
mainClock = pygame.time.Clock()
win = pygame.display.set_mode((1300, 800), HWSURFACE|DOUBLEBUF|RESIZABLE) # Sets the height and width of window, anything on the screen goes on 'win'
fakeMenuScreen = win.copy()
# Load background images
bar = pygame.image.load("menuimages/bar.png").convert_alpha()
sprites = pygame.sprite.Group()
singleplayerButton = sprites.add(Button(pygame.Color((0, 0, 0, 0)),
pygame.Color((53, 74, 110, 50)), pygame.Rect(85, 250, 375, 50), lambda x : print('HELLO'), "Singleplayer"))
multiplayerButton = sprites.add(Button(pygame.Color((0, 0, 0, 0)), pygame.Color((53, 74, 110, 50)),
pygame.Rect(85, 320, 375, 50), lambda b: print(f"Button '{b.text}' was clicked"), 'Multiplayer',))
def main_menu():
global win
while running == True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == VIDEORESIZE:
win = pygame.display.set_mode(event.size, RESIZABLE)
win.blit(pygame.transform.smoothscale(fakeMenuScreen, win.get_rect().size), (0, 0)) # Smooths and scales out the screen
fakeMenuScreen.blit(bar, (85, 0))
sprites.update(pygame.event.get()) # Buttons
sprites.draw(fakeMenuScreen) # draw all sprites/Buttons
pygame.display.update()
if __name__ == "__main__":
main_menu()
buttonClass.py:
import pygame
pygame.init()
font = pygame.font.SysFont('Microsoft New Tai Lue', 23)
font2 = pygame.font.SysFont('Tw Cen MT Condensed Extra Bold', 18)
class Button(pygame.sprite.Sprite):
def __init__(self, color, color_hover, rect, callback, text='', outline=None):
super().__init__()
self.text = text
# a temporary Rect to store the size of the button
tmp_rect = pygame.Rect(0, 0, *rect.size)
# create two Surfaces here, one the normal state, and one for the hovering state
self.org = self._create_image(color, outline, text, tmp_rect)
self.hov = self._create_image(color_hover, outline, text, tmp_rect)
# in Sprites, the image attribute holds the Surface to be displayed...
self.image = self.org
# ...and the rect holds the Rect that defines it position
self.rect = rect
self.callback = callback
def _create_image(self, color, outline, text, rect):
# function to create the actual surface
img = pygame.Surface(rect.size, pygame.SRCALPHA)
if outline:
# 'inflate' is used to 'shrink' the rect
img.fill(outline)
img.fill(color, rect.inflate(-4, -4))
else:
img.fill(color)
# render the text once here instead of every frame
if text != '':
text_surf = font.render(text, 1, pygame.Color('white'))
text_rect = text_surf.get_rect(center=rect.center)
img.blit(text_surf, text_rect)
return img
def update(self, events):
# here we handle all the logic of the Button
pos = pygame.mouse.get_pos()
print(pos)
hit = self.rect.collidepoint(pos)
self.image = self.hov if hit else self.org
for event in events:
# if this Button is clicked, it runs the callback function
if event.type == pygame.MOUSEBUTTONDOWN and hit:
self.callback(self)
非常感谢任何帮助。
问题是,您没有为调整后的大小修改鼠标数据 window。
幸运的是我曾经遇到过同样的问题并写了以下内容 class:
class Window:
def __init__(self, surf, width, height):
self.screen = pygame.display.set_mode((width, height), RESIZABLE) # add your flags here
self.surf = surf
# self.orig_w, self.orig_h = surf.get_size()
# self.x_off = 0
# self.y_off = 0
self.width = width
self.height = height
# self.rate = 1
# 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)
##### code below inserted
self.width = width
self.height = height
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)
###### code below inserted
return mouse_x / self.width * self.orig_width, mouse_y / self.height * self.orig_height
def update(self):
self.screen.fill((50, 50, 50))
# self.screen.blit(pygame.transform.smoothscale(self.surf, (self.width, self.height)), (self.x_off, self.y_off))
# code below inserted
###### self.screen.blit(pygame.transform.smoothscale(self.surf, (self.width, self.height)), (0, 0))
###### code below original
pygame.display.flip()
用法:
- 使用
fakeMenuScreen = pygame.Surface((1300, 800))
和 win = Window(fakeMenuScreen, 1300, 800)
- 在
VIDEORESIZE
事件中你应该调用 win.set_sizes(event.w, event.h)
- 获取鼠标位置时应使用
wind.get_mouse_pos()
- 要更新 window 您需要调用
win.update()
附加功能:
调整大小时,显示的 window 将保持其 width/height 比例,并在两侧显示灰色条。
所以基本上我使用比例因子来缩放屏幕坐标:
scaleX = screenWidth / infoObject.current_w
finalPosX = int(round(pygame.mouse.get_pos()[0] * scaleX))
scaleY = screenHeight / infoObject.current_h
finalPosY = int(round(pygame.mouse.get_pos()[1] * scaleY))
sprites.update(events, (finalPosX, finalPosY))
然后将坐标传递给按钮 class 中的 update
函数,该函数更新碰撞框。
我的代码基本如下: 我有一个带有按钮的菜单,它们绘制在主屏幕的副本上,可以平滑缩放,绘制的按钮位于 class.
中一旦按钮被选中或被鼠标触摸,它就会有一个 'hover' 动画,可以单击以执行命令。问题是一旦调整屏幕大小;变大或变小,按钮的碰撞点乱了,与实际按钮不在一条直线上,无法直接点击。
主要代码:
import pygame
import sys
from pygame.locals import *
from buttonClass import Button
running = True
pygame.init()
mainClock = pygame.time.Clock()
win = pygame.display.set_mode((1300, 800), HWSURFACE|DOUBLEBUF|RESIZABLE) # Sets the height and width of window, anything on the screen goes on 'win'
fakeMenuScreen = win.copy()
# Load background images
bar = pygame.image.load("menuimages/bar.png").convert_alpha()
sprites = pygame.sprite.Group()
singleplayerButton = sprites.add(Button(pygame.Color((0, 0, 0, 0)),
pygame.Color((53, 74, 110, 50)), pygame.Rect(85, 250, 375, 50), lambda x : print('HELLO'), "Singleplayer"))
multiplayerButton = sprites.add(Button(pygame.Color((0, 0, 0, 0)), pygame.Color((53, 74, 110, 50)),
pygame.Rect(85, 320, 375, 50), lambda b: print(f"Button '{b.text}' was clicked"), 'Multiplayer',))
def main_menu():
global win
while running == True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == VIDEORESIZE:
win = pygame.display.set_mode(event.size, RESIZABLE)
win.blit(pygame.transform.smoothscale(fakeMenuScreen, win.get_rect().size), (0, 0)) # Smooths and scales out the screen
fakeMenuScreen.blit(bar, (85, 0))
sprites.update(pygame.event.get()) # Buttons
sprites.draw(fakeMenuScreen) # draw all sprites/Buttons
pygame.display.update()
if __name__ == "__main__":
main_menu()
buttonClass.py:
import pygame
pygame.init()
font = pygame.font.SysFont('Microsoft New Tai Lue', 23)
font2 = pygame.font.SysFont('Tw Cen MT Condensed Extra Bold', 18)
class Button(pygame.sprite.Sprite):
def __init__(self, color, color_hover, rect, callback, text='', outline=None):
super().__init__()
self.text = text
# a temporary Rect to store the size of the button
tmp_rect = pygame.Rect(0, 0, *rect.size)
# create two Surfaces here, one the normal state, and one for the hovering state
self.org = self._create_image(color, outline, text, tmp_rect)
self.hov = self._create_image(color_hover, outline, text, tmp_rect)
# in Sprites, the image attribute holds the Surface to be displayed...
self.image = self.org
# ...and the rect holds the Rect that defines it position
self.rect = rect
self.callback = callback
def _create_image(self, color, outline, text, rect):
# function to create the actual surface
img = pygame.Surface(rect.size, pygame.SRCALPHA)
if outline:
# 'inflate' is used to 'shrink' the rect
img.fill(outline)
img.fill(color, rect.inflate(-4, -4))
else:
img.fill(color)
# render the text once here instead of every frame
if text != '':
text_surf = font.render(text, 1, pygame.Color('white'))
text_rect = text_surf.get_rect(center=rect.center)
img.blit(text_surf, text_rect)
return img
def update(self, events):
# here we handle all the logic of the Button
pos = pygame.mouse.get_pos()
print(pos)
hit = self.rect.collidepoint(pos)
self.image = self.hov if hit else self.org
for event in events:
# if this Button is clicked, it runs the callback function
if event.type == pygame.MOUSEBUTTONDOWN and hit:
self.callback(self)
非常感谢任何帮助。
问题是,您没有为调整后的大小修改鼠标数据 window。
幸运的是我曾经遇到过同样的问题并写了以下内容 class:
class Window:
def __init__(self, surf, width, height):
self.screen = pygame.display.set_mode((width, height), RESIZABLE) # add your flags here
self.surf = surf
# self.orig_w, self.orig_h = surf.get_size()
# self.x_off = 0
# self.y_off = 0
self.width = width
self.height = height
# self.rate = 1
# 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)
##### code below inserted
self.width = width
self.height = height
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)
###### code below inserted
return mouse_x / self.width * self.orig_width, mouse_y / self.height * self.orig_height
def update(self):
self.screen.fill((50, 50, 50))
# self.screen.blit(pygame.transform.smoothscale(self.surf, (self.width, self.height)), (self.x_off, self.y_off))
# code below inserted
###### self.screen.blit(pygame.transform.smoothscale(self.surf, (self.width, self.height)), (0, 0))
###### code below original
pygame.display.flip()
用法:
- 使用
fakeMenuScreen = pygame.Surface((1300, 800))
和win = Window(fakeMenuScreen, 1300, 800)
- 在
VIDEORESIZE
事件中你应该调用win.set_sizes(event.w, event.h)
- 获取鼠标位置时应使用
wind.get_mouse_pos()
- 要更新 window 您需要调用
win.update()
附加功能:
调整大小时,显示的 window 将保持其 width/height 比例,并在两侧显示灰色条。
所以基本上我使用比例因子来缩放屏幕坐标:
scaleX = screenWidth / infoObject.current_w
finalPosX = int(round(pygame.mouse.get_pos()[0] * scaleX))
scaleY = screenHeight / infoObject.current_h
finalPosY = int(round(pygame.mouse.get_pos()[1] * scaleY))
sprites.update(events, (finalPosX, finalPosY))
然后将坐标传递给按钮 class 中的 update
函数,该函数更新碰撞框。