import pygame
import random
# 初始化 Pygame
pygame.init()
# 游戏常量
SCREEN_SIZEX = 800
SCREEN_SIZEY = 800
GRID_SIZE = 40
FPS = 60
MACRO_COLORS = [
(255, 102, 102), # 红色 (尺寸1)
(102, 178, 255), # 蓝色 (尺寸2)
(255, 178, 102), # 橙色 (尺寸3)
(102, 255, 178), # 绿色 (尺寸4)
(178, 102, 255) # 紫色 (尺寸5)
]
MACRO_SIZES = [ # 五种基础尺寸(宽x高,单位:网格数)
(3, 4), # 尺寸1
(2, 3), # 尺寸2
(1, 3), # 尺寸3
(1, 2), # 尺寸4
(2, 2) # 尺寸5
]
MACRO_NUMS = [
4, # 尺寸1
8, # 尺寸2
6, # 尺寸3
8, # 尺寸4
8 # 尺寸5
]
class MacroUnit:
existing_rects = []
def __init__(self, size_type, color):
self.size_type = size_type
self.width = MACRO_SIZES[size_type][0] * GRID_SIZE
self.height = MACRO_SIZES[size_type][1] * GRID_SIZE
self.color = color
self.x, self.y = self.generate_valid_position()
self.selected = False
self.offset_x = 0
self.offset_y = 0
def generate_valid_position(self, max_attempts=500):
"""生成不与其他宏单元重叠的位置"""
for _ in range(max_attempts):
x = random.randint(0, SCREEN_SIZEX - self.width)
y = random.randint(0, SCREEN_SIZEY - self.height)
new_rect = pygame.Rect(x, y, self.width, self.height)
# 检查是否与已有位置重叠
overlap = False
for rect in MacroUnit.existing_rects:
if new_rect.colliderect(rect):
overlap = True
break
if not overlap:
MacroUnit.existing_rects.append(new_rect)
return x, y
# 如果无法找到合适位置,返回随机位置
return (random.randint(0, SCREEN_SIZEX - self.width),
random.randint(0, SCREEN_SIZEY - self.height))
def draw(self, surface):
pygame.draw.rect(surface, self.color,
(self.x, self.y, self.width, self.height))
if self.selected:
pygame.draw.rect(surface, (0,0,0),
(self.x-2, self.y-2, self.width+4, self.height+4), 3)
def check_click(self, mouse_pos):
return (self.x <= mouse_pos[0] <= self.x + self.width and
self.y <= mouse_pos[1] <= self.y + self.height)
def move(self, dx, dy, macros):
new_x = self.x + dx
new_y = self.y + dy
# 临时计算新位置
temp_rect = pygame.Rect(new_x, new_y, self.width, self.height)
# 边界检查
if temp_rect.left < 0 or temp_rect.right > SCREEN_SIZEX:
return
if temp_rect.top < 0 or temp_rect.bottom > SCREEN_SIZEY:
return
# 碰撞检测
for macro in macros:
if macro is not self and temp_rect.colliderect(
pygame.Rect(macro.x, macro.y, macro.width, macro.height)):
return
# 执行移动
self.x = new_x
self.y = new_y
# 添加自动对齐功能
def snap_to_grid(self):
self.x = round(self.x / GRID_SIZE) * GRID_SIZE
self.y = round(self.y / GRID_SIZE) * GRID_SIZE
class LayoutGame:
def __init__(self):
# 创建35个宏单元(每种尺寸7个)
self.macros = []
for size_type in range(5):
color = MACRO_COLORS[size_type]
for _ in range(MACRO_NUMS[size_type]):
macro = MacroUnit(size_type, color)
self.macros.append(macro)
self.selected_macro = None
self.running = True
self.show_grid = True
self.font = pygame.font.Font(None, 24)
self.screen = pygame.display.set_mode((SCREEN_SIZEX+200, SCREEN_SIZEY))
pygame.display.set_caption("宏单元布局游戏(35单元版)")
def draw_grid(self):
for x in range(0, SCREEN_SIZEX, GRID_SIZE):
pygame.draw.line(self.screen, (200,200,200), (x,0), (x,SCREEN_SIZEX))
for y in range(0, SCREEN_SIZEY, GRID_SIZE):
pygame.draw.line(self.screen, (200,200,200), (0,y), (SCREEN_SIZEY,y))
def draw_info_panel(self):
# 绘制右侧信息面板
pygame.draw.rect(self.screen, (220,220,220),
(SCREEN_SIZEX, 0, SCREEN_SIZEX+200, SCREEN_SIZEY))
# 统计各尺寸数量
size_counts = {i:0 for i in range(5)}
for macro in self.macros:
size_counts[macro.size_type] += 1
y = 20
for size_type in range(5):
text = self.font.render(
f"size {size_type+1}: {size_counts[size_type]}",
True, MACRO_COLORS[size_type])
self.screen.blit(text, (SCREEN_SIZEX+10, y))
y += 30
def handle_events(self):
for event in pygame.event.get():
if event.type == pygame.QUIT:
self.running = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_q:
self.running = False
elif event.key == pygame.K_g:
self.show_grid = not self.show_grid
elif event.key == pygame.K_r:
self.reset_layout()
if event.type == pygame.MOUSEBUTTONDOWN:
for macro in reversed(self.macros): # 优先选择上层元素
if macro.check_click(event.pos):
self.selected_macro = macro
macro.selected = True
macro.offset_x = event.pos[0] - macro.x
macro.offset_y = event.pos[1] - macro.y
# 将被选中的宏单元移到列表最后(显示在最上层)
self.macros.remove(macro)
self.macros.append(macro)
break
if event.type == pygame.MOUSEBUTTONUP:
if self.selected_macro:
self.selected_macro.selected = False
self.selected_macro = None
if event.type == pygame.MOUSEMOTION and self.selected_macro:
dx = (event.pos[0] - self.selected_macro.offset_x -
self.selected_macro.x) * 0.5
dy = (event.pos[1] - self.selected_macro.offset_y -
self.selected_macro.y) * 0.5
self.selected_macro.move(dx, dy, self.macros)
# 在handle_events中添加快捷键
if event.type == pygame.KEYUP:
if event.key == pygame.K_s: # 按S键对齐到网格
for macro in self.macros:
macro.snap_to_grid()
def reset_layout(self):
"""重置所有宏单元位置"""
self.macros = []
for size_type in range(5):
color = MACRO_COLORS[size_type]
for _ in range(MACRO_NUMS[size_type]):
macro = MacroUnit(size_type, color)
self.macros.append(macro)
def run(self):
clock = pygame.time.Clock()
while self.running:
self.screen.fill((240, 240, 240))
if self.show_grid:
self.draw_grid()
for macro in self.macros:
macro.draw(self.screen)
self.draw_info_panel()
self.handle_events()
pygame.display.flip()
clock.tick(FPS)
if __name__ == "__main__":
game = LayoutGame()
game.run()
pygame.quit()
运行示例:
按下s键后: