Python Pygame 入门:开发简易拼图游戏

下面我将创建一个支持自定义图片的简易拼图游戏,使用Python的Pygame库实现。游戏包含完整功能:图片加载、网格分割、碎片随机打乱、拖拽交互和胜利判定。

完整代码实现

import pygame
import sys
import random
import os
from pygame.locals import *

# 初始化pygame
pygame.init()

# 游戏常量
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
GRID_SIZE = 3  # 3x3网格
TILE_SIZE = 150  # 每个拼图块大小
MARGIN = 10     # 拼图块间距

# 颜色定义
BACKGROUND = (40, 44, 52)
GRID_COLOR = (86, 98, 112)
TEXT_COLOR = (220, 220, 220)
HIGHLIGHT = (97, 175, 239)

# 创建游戏窗口
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption("简易拼图游戏")

# 字体设置
font = pygame.font.SysFont(None, 36)

class PuzzleGame:
    def __init__(self):
        self.tiles = []
        self.empty_pos = (GRID_SIZE-1, GRID_SIZE-1)  # 空白位置在右下角
        self.selected_tile = None
        self.game_complete = False
        self.image_path = ""
        
    def load_image(self, image_path):
        """加载并分割自定义图片"""
        try:
            # 加载图片并调整大小
            original_image = pygame.image.load(image_path)
            img_width = GRID_SIZE * TILE_SIZE
            img_height = GRID_SIZE * TILE_SIZE
            scaled_image = pygame.transform.scale(original_image, (img_width, img_height))
            
            # 清空现有拼图块
            self.tiles = []
            
            # 分割图片为网格块
            for row in range(GRID_SIZE):
                for col in range(GRID_SIZE):
                    # 创建拼图块表面
                    tile_surface = pygame.Surface((TILE_SIZE, TILE_SIZE), pygame.SRCALPHA)
                    
                    # 计算源矩形区域(最后一块留空)
                    if row == GRID_SIZE-1 and col == GRID_SIZE-1:
                        # 空白块(透明)
                        tile_surface.fill((0, 0, 0, 0))
                    else:
                        # 从缩放后的图片中截取相应区域
                        src_rect = pygame.Rect(col * TILE_SIZE, row * TILE_SIZE, TILE_SIZE, TILE_SIZE)
                        tile_surface.blit(scaled_image, (0, 0), src_rect)
                    
                    # 添加边框
                    pygame.draw.rect(tile_surface, GRID_COLOR, (0, 0, TILE_SIZE, TILE_SIZE), 2)
                    
                    # 存储拼图块信息
                    self.tiles.append({
                        'surface': tile_surface,
                        'correct_pos': (row, col),  # 正确位置
                        'current_pos': (row, col)   # 当前位置
                    })
            
            # 记录图片路径
            self.image_path = image_path
            # 打乱拼图
            self.shuffle_tiles()
            self.game_complete = False
            return True
        except Exception as e:
            print(f"加载图片失败: {e}")
            return False
    
    def shuffle_tiles(self):
        """随机打乱拼图块(确保可解)"""
        # 进行100次随机移动
        for _ in range(100):
            # 获取空白位置周围的可行移动方向
            row, col = self.empty_pos
            possible_moves = []
            
            # 检查四个方向
            if row > 0: possible_moves.append((row-1, col))  # 上
            if row < GRID_SIZE-1: possible_moves.append((row+1, col))  # 下
            if col > 0: possible_moves.append((row, col-1))  # 左
            if col < GRID_SIZE-1: possible_moves.append((row, col+1))  # 右
            
            # 随机选择一个方向移动
            if possible_moves:
                move_pos = random.choice(possible_moves)
                self.move_tile(move_pos)
    
    def move_tile(self, tile_pos):
        """移动拼图块"""
        # 检查是否与空白位置相邻
        row, col = tile_pos
        empty_row, empty_col = self.empty_pos
        
        # 检查是否在相邻位置(上、下、左、右)
        if ((abs(row - empty_row) == 1 and col == empty_col) or 
            (abs(col - empty_col) == 1 and row == empty_row)):
            
            # 找到要移动的拼图块
            for tile in self.tiles:
                if tile['current_pos'] == tile_pos:
                    # 更新拼图块位置
                    tile['current_pos'] = self.empty_pos
                    # 更新空白位置
                    self.empty_pos = tile_pos
                    break
            
            # 检查游戏是否完成
            self.check_completion()
    
    def check_completion(self):
        """检查拼图是否完成"""
        self.game_complete = True
        for tile in self.tiles:
            if tile['current_pos'] != tile['correct_pos']:
                self.game_complete = False
                break
    
    def draw(self, screen):
        """绘制游戏界面"""
        # 绘制背景
        screen.fill(BACKGROUND)
        
        # 计算拼图区域位置(居中)
        total_width = GRID_SIZE * (TILE_SIZE + MARGIN) - MARGIN
        total_height = GRID_SIZE * (TILE_SIZE + MARGIN) - MARGIN
        start_x = (SCREEN_WIDTH - total_width) // 2
        start_y = (SCREEN_HEIGHT - total_height) // 2
        
        # 绘制所有拼图块
        for tile in self.tiles:
            row, col = tile['current_pos']
            
            # 跳过空白块
            if row == self.empty_pos[0] and col == self.empty_pos[1]:
                continue
            
            # 计算绘制位置
            x = start_x + col * (TILE_SIZE + MARGIN)
            y = start_y + row * (TILE_SIZE + MARGIN)
            
            # 绘制拼图块
            screen.blit(tile['surface'], (x, y))
            
            # 高亮选中的拼图块
            if self.selected_tile == (row, col):
                highlight_rect = pygame.Rect(x-5, y-5, TILE_SIZE+10, TILE_SIZE+10)
                pygame.draw.rect(screen, HIGHLIGHT, highlight_rect, 3)
        
        # 绘制游戏标题
        title_text = font.render("简易拼图游戏", True, TEXT_COLOR)
        screen.blit(title_text, (SCREEN_WIDTH//2 - title_text.get_width()//2, 20))
        
        # 绘制操作说明
        controls = [
            "操作说明:",
            "鼠标点击 - 选择拼图块",
            "方向键 - 移动选中的拼图块",
            "R键 - 重新打乱",
            "L键 - 加载新图片"
        ]
        
        for i, text in enumerate(controls):
            ctrl_text = font.render(text, True, TEXT_COLOR)
            screen.blit(ctrl_text, (50, 70 + i*40))
        
        # 显示当前图片路径
        if self.image_path:
            path_text = font.render(f"图片: {os.path.basename(self.image_path)}", True, TEXT_COLOR)
            screen.blit(path_text, (50, SCREEN_HEIGHT - 50))
        
        # 如果游戏完成,显示胜利信息
        if self.game_complete:
            # 半透明覆盖层
            overlay = pygame.Surface((SCREEN_WIDTH, SCREEN_HEIGHT), pygame.SRCALPHA)
            overlay.fill((0, 0, 0, 180))
            screen.blit(overlay, (0, 0))
            
            # 胜利文本
            win_text = font.render("恭喜! 拼图完成!", True, (76, 209, 55))
            restart_text = font.render("按R键重新开始", True, TEXT_COLOR)
            
            screen.blit(win_text, (SCREEN_WIDTH//2 - win_text.get_width()//2, 
                                  SCREEN_HEIGHT//2 - 50))
            screen.blit(restart_text, (SCREEN_WIDTH//2 - restart_text.get_width()//2, 
                                      SCREEN_HEIGHT//2 + 10))
    
    def handle_event(self, event):
        """处理游戏事件"""
        if event.type == QUIT:
            return False
        
        if self.game_complete and event.type == KEYDOWN and event.key == K_r:
            self.shuffle_tiles()
            self.game_complete = False
            return True
        
        if event.type == KEYDOWN:
            if event.key == K_r:  # 重新打乱
                self.shuffle_tiles()
                self.game_complete = False
            elif event.key == K_l:  # 加载新图片
                self.load_new_image()
            elif self.selected_tile:  # 方向键移动选中的拼图块
                row, col = self.selected_tile
                if event.key == K_UP:
                    self.move_tile((row-1, col))
                elif event.key == K_DOWN:
                    self.move_tile((row+1, col))
                elif event.key == K_LEFT:
                    self.move_tile((row, col-1))
                elif event.key == K_RIGHT:
                    self.move_tile((row, col+1))
        
        if event.type == MOUSEBUTTONDOWN:
            # 计算点击位置
            total_width = GRID_SIZE * (TILE_SIZE + MARGIN) - MARGIN
            total_height = GRID_SIZE * (TILE_SIZE + MARGIN) - MARGIN
            start_x = (SCREEN_WIDTH - total_width) // 2
            start_y = (SCREEN_HEIGHT - total_height) // 2
            
            # 获取鼠标位置
            mouse_x, mouse_y = pygame.mouse.get_pos()
            
            # 检查是否在拼图区域内
            if (start_x <= mouse_x < start_x + total_width and 
                start_y <= mouse_y < start_y + total_height):
                
                # 计算点击的网格位置
                grid_col = (mouse_x - start_x) // (TILE_SIZE + MARGIN)
                grid_row = (mouse_y - start_y) // (TILE_SIZE + MARGIN)
                
                # 检查是否点击了有效的拼图块(不是空白块)
                if (grid_row, grid_col) != self.empty_pos:
                    self.selected_tile = (grid_row, grid_col)
        
        return True
    
    def load_new_image(self):
        """加载新图片(示例中为内置图片,实际使用可替换)"""
        # 在实际应用中,这里可以改为打开文件对话框
        # 示例使用内置图片路径
        images = [
            "image1.jpg",  # 替换为实际图片路径
            "image2.png",
            "image3.bmp"
        ]
        new_image = random.choice(images)
        self.load_image(new_image)

# 创建游戏实例
game = PuzzleGame()

# 加载初始图片(替换为你的图片路径)
game.load_image("default_puzzle.jpg")  # 替换为你的图片路径

# 游戏主循环
clock = pygame.time.Clock()
running = True

while running:
    # 处理事件
    for event in pygame.event.get():
        running = game.handle_event(event)
    
    # 绘制游戏
    game.draw(screen)
    
    # 更新显示
    pygame.display.flip()
    
    # 控制帧率
    clock.tick(60)

# 退出游戏
pygame.quit()
sys.exit()

游戏功能说明

  1. 核心功能

    • 支持自定义图片加载
    • 3x3网格拼图
    • 随机打乱拼图块
    • 鼠标选择拼图块
    • 键盘方向键移动拼图
    • 自动检测拼图完成状态
  2. 操作方式

    • 鼠标点击选择拼图块
    • 方向键移动选中的拼图块
    • R键:重新打乱拼图
    • L键:加载新图片(示例中为随机选择)
  3. 界面元素

    • 居中显示拼图网格
    • 高亮显示选中的拼图块
    • 显示操作说明
    • 显示当前图片名称
    • 拼图完成时显示胜利画面

如何自定义图片

  1. 准备一张图片(JPG、PNG或BMP格式)
  2. 修改代码中的图片路径:
    # 初始加载图片
    game.load_image("your_image.jpg")  # 替换为你的图片路径
    

  3. load_new_image()方法中添加你的图片列表

扩展建议

  1. 增加难度

    # 修改网格大小
    GRID_SIZE = 4  # 4x4网格
    

  2. 添加动画效果

    • 实现拼图块移动动画
    • 添加胜利动画
  3. 添加计时功能

    • 记录玩家完成拼图的时间
    • 保存最佳成绩
  4. 添加音效

    • 移动拼图时的音效
    • 完成拼图时的胜利音效

这个拼图游戏项目展示了Pygame的基本应用,包括图像处理、事件处理、用户交互和游戏状态管理。通过修改代码中的常量和添加新功能,你可以进一步扩展和完善这个游戏。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐