当前位置: 代码网 > it编程>前端脚本>Python > 基于Python和Pygame打造一个有趣的化学配对记忆游戏

基于Python和Pygame打造一个有趣的化学配对记忆游戏

2026年02月11日 Python 我要评论
这篇文章主要介绍了一款使用python和pygame开发的化学配对教育游戏,通过创新的记忆配对机制帮助学生学习化学知识。游戏包含元素和化合物两种模式,分别涵盖28种化学元素和32种常见化合物,玩家需要

这篇文章主要介绍了一款使用python和pygame开发的化学配对教育游戏,通过创新的记忆配对机制帮助学生学习化学知识。游戏包含元素和化合物两种模式,分别涵盖28种化学元素和32种常见化合物,玩家需要在网格中点击匹配中文名称与对应化学式。游戏设计了三个难度级别(4×4、6×6、8×8网格),具有自适应界面布局、智能字体调整、震动反馈效果和完整的游戏流程管理,既能让学生在轻松互动中掌握化学术语,又展示了如何将编程技术应用于教育领域,是一款兼具教育价值和编程学习意义的开源项目,因为化合物中的下标弄不出来,所以h₂o这种是h2o,也可以自己增加元素和化合物的种类。

游戏效果如下:

完整代码如下:

import pygame
import random
import sys
import math
import os
from pygame.locals import *
 
# 初始化pygame
pygame.init()
 
# 获取屏幕尺寸并设置窗口居中
screen_width = 1000
screen_height = 800
screen = pygame.display.set_mode((screen_width, screen_height))
pygame.display.set_caption("化学配对游戏")
 
# 颜色定义
background = (240, 245, 249)
grid_bg = (230, 240, 255)
text_color = (30, 45, 70)
selected_color = (100, 180, 255)
correct_color = (100, 220, 150)
wrong_color = (255, 150, 150)
empty_color = (220, 230, 240)
button_color = (70, 130, 180)
button_hover = (90, 150, 200)
title_color = (20, 60, 120)
 
# 单元格边距
cell_margin = 8
 
 
# 字体加载函数
def load_font(font_size=32):
    """安全加载字体,返回一个可用的字体对象"""
    try:
        # 尝试加载系统字体
        font_names = [
            'microsoft yahei',  # windows 中文
            'simhei',  # windows 黑体
            'simsun',  # windows 宋体
            'arial unicode ms',  # 通用unicode字体
            'dejavu sans',  # linux通用字体
            'arial'  # 通用字体
        ]
 
        for font_name in font_names:
            try:
                font = pygame.font.sysfont(font_name, font_size)
                # 测试字体是否能渲染
                test_surface = font.render("测试", true, (0, 0, 0))
                if test_surface.get_width() > 0:
                    return font
            except:
                continue
 
        # 如果都失败,使用默认字体
        return pygame.font.font(none, font_size)
    except:
        # 终极备用方案
        return pygame.font.font(none, font_size)
 
 
# 加载字体
try:
    chinese_font = load_font(32)
    chinese_font_small = load_font(24)
    font_medium = load_font(36)
    font_small = load_font(28)
except:
    # 如果字体加载失败,使用默认字体
    chinese_font = pygame.font.font(none, 32)
    chinese_font_small = pygame.font.font(none, 24)
    font_medium = pygame.font.font(none, 36)
    font_small = pygame.font.font(none, 28)
 
# 化学元素数据 - 中文名称和对应的化学式
elements = [
    {"chinese": "氢", "formula": "h"},
    {"chinese": "氦", "formula": "he"},
    {"chinese": "锂", "formula": "li"},
    {"chinese": "铍", "formula": "be"},
    {"chinese": "硼", "formula": "b"},
    {"chinese": "碳", "formula": "c"},
    {"chinese": "氮", "formula": "n"},
    {"chinese": "氧", "formula": "o"},
    {"chinese": "氟", "formula": "f"},
    {"chinese": "氖", "formula": "ne"},
    {"chinese": "钠", "formula": "na"},
    {"chinese": "镁", "formula": "mg"},
    {"chinese": "铝", "formula": "al"},
    {"chinese": "硅", "formula": "si"},
    {"chinese": "磷", "formula": "p"},
    {"chinese": "硫", "formula": "s"},
    {"chinese": "氯", "formula": "cl"},
    {"chinese": "氩", "formula": "ar"},
    {"chinese": "钾", "formula": "k"},
    {"chinese": "钙", "formula": "ca"},
    {"chinese": "铁", "formula": "fe"},
    {"chinese": "铜", "formula": "cu"},
    {"chinese": "银", "formula": "ag"},
    {"chinese": "金", "formula": "au"},
    {"chinese": "汞", "formula": "hg"},
    {"chinese": "铅", "formula": "pb"},
    {"chinese": "锡", "formula": "sn"},
    {"chinese": "锌", "formula": "zn"},
]
 
# 化合物数据 - 使用标准字符,避免特殊字符
compounds = [
    {"chinese": "水", "formula": "h2o"},
    {"chinese": "二氧化碳", "formula": "co2"},
    {"chinese": "食盐", "formula": "nacl"},
    {"chinese": "氨气", "formula": "nh3"},
    {"chinese": "甲烷", "formula": "ch4"},
    {"chinese": "硫酸", "formula": "h2so4"},
    {"chinese": "葡萄糖", "formula": "c6h12o6"},
    {"chinese": "乙醇", "formula": "c2h5oh"},
    {"chinese": "碳酸钙", "formula": "caco3"},
    {"chinese": "氢氧化钠", "formula": "naoh"},
    {"chinese": "盐酸", "formula": "hcl"},
    {"chinese": "硝酸", "formula": "hno3"},
    {"chinese": "醋酸", "formula": "ch3cooh"},
    {"chinese": "氧化铁", "formula": "fe2o3"},
    {"chinese": "硫酸铜", "formula": "cuso4"},
    {"chinese": "氯化银", "formula": "agcl"},
    {"chinese": "氧化铝", "formula": "al2o3"},
    {"chinese": "硫化氢", "formula": "h2s"},
    {"chinese": "氢氧化钙", "formula": "ca(oh)2"},
    {"chinese": "碳酸钠", "formula": "na2co3"},
    {"chinese": "氯化钾", "formula": "kcl"},
    {"chinese": "硫酸钠", "formula": "na2so4"},
    {"chinese": "硝酸银", "formula": "agno3"},
    {"chinese": "硫酸亚铁", "formula": "feso4"},
    {"chinese": "氧化铜", "formula": "cuo"},
    {"chinese": "二氧化硅", "formula": "sio2"},
    {"chinese": "氯化钙", "formula": "cacl2"},
    {"chinese": "氯化镁", "formula": "mgcl2"},
    {"chinese": "硫酸镁", "formula": "mgso4"},
    {"chinese": "氯化锌", "formula": "zncl2"},
    {"chinese": "硫酸锌", "formula": "znso4"},
    {"chinese": "硝酸钾", "formula": "kno3"},
]
 
 
class gamecell:
    def __init__(self, x, y, width, height, text, cell_type, pair_id):
        self.rect = pygame.rect(x, y, width, height)
        self.text = text
        self.cell_type = cell_type  # 'chinese' 或 'formula'
        self.pair_id = pair_id  # 配对id,相同id的可以配对
        self.selected = false
        self.matched = false
 
    def draw(self, screen):
        if self.matched:
            color = empty_color
        elif self.selected:
            color = selected_color
        else:
            color = grid_bg
 
        pygame.draw.rect(screen, color, self.rect, border_radius=8)
        pygame.draw.rect(screen, text_color, self.rect, 2, border_radius=8)
 
        if self.text and not self.matched:
            # 根据单元格大小选择合适的字体大小
            cell_size = min(self.rect.width, self.rect.height)
 
            if cell_size > 100:  # 大单元格
                font_size = 32
            elif cell_size > 70:  # 中等单元格
                font_size = 24
            else:  # 小单元格
                font_size = 18
 
            # 使用安全的字体加载
            try:
                font = load_font(font_size)
                text_surface = font.render(self.text, true, text_color)
            except:
                # 如果字体渲染失败,使用默认字体
                font = pygame.font.font(none, font_size)
                text_surface = font.render(self.text, true, text_color)
 
            # 如果文本太长,缩小字体
            max_width = self.rect.width - 10
            while text_surface.get_width() > max_width and font_size > 12:
                font_size -= 2
                try:
                    font = load_font(font_size)
                    text_surface = font.render(self.text, true, text_color)
                except:
                    font = pygame.font.font(none, font_size)
                    text_surface = font.render(self.text, true, text_color)
 
            text_rect = text_surface.get_rect(center=self.rect.center)
            screen.blit(text_surface, text_rect)
 
    def is_clicked(self, pos):
        return self.rect.collidepoint(pos) and not self.matched
 
 
class button:
    def __init__(self, x, y, width, height, text):
        self.rect = pygame.rect(x, y, width, height)
        self.text = text
        self.hovered = false
 
    def draw(self, screen):
        color = button_hover if self.hovered else button_color
        pygame.draw.rect(screen, color, self.rect, border_radius=8)
        pygame.draw.rect(screen, (255, 255, 255), self.rect, 2, border_radius=8)
 
        # 渲染按钮文本
        try:
            text_surface = chinese_font_small.render(self.text, true, (255, 255, 255))
        except:
            font = pygame.font.font(none, 24)
            text_surface = font.render(self.text, true, (255, 255, 255))
 
        text_rect = text_surface.get_rect(center=self.rect.center)
        screen.blit(text_surface, text_rect)
 
    def is_clicked(self, pos):
        return self.rect.collidepoint(pos)
 
 
class difficultybutton:
    def __init__(self, x, y, width, height, text, difficulty):
        self.rect = pygame.rect(x, y, width, height)
        self.text = text
        self.difficulty = difficulty
        self.hovered = false
        self.selected = false
 
    def draw(self, screen):
        if self.selected:
            color = (150, 200, 100)
        elif self.hovered:
            color = button_hover
        else:
            color = button_color
 
        pygame.draw.rect(screen, color, self.rect, border_radius=8)
        pygame.draw.rect(screen, (255, 255, 255), self.rect, 2, border_radius=8)
 
        # 渲染按钮文本
        try:
            text_surface = chinese_font_small.render(self.text, true, (255, 255, 255))
        except:
            font = pygame.font.font(none, 24)
            text_surface = font.render(self.text, true, (255, 255, 255))
 
        text_rect = text_surface.get_rect(center=self.rect.center)
        screen.blit(text_surface, text_rect)
 
    def is_clicked(self, pos):
        return self.rect.collidepoint(pos)
 
 
class game:
    def __init__(self):
        self.mode = "elements"  # "elements" 或 "compounds"
        self.difficulty = "easy"  # "easy", "medium", "hard"
        self.cells = []
        self.selected_cells = []
        self.matched_pairs = 0
        self.total_pairs = 0
        self.game_over = false
        self.shake_time = 0
        self.shake_offset = (0, 0)
 
        # 创建按钮
        self.create_buttons()
        self.init_game()
 
    def calculate_cell_size(self):
        """根据难度计算单元格大小和网格大小"""
        if self.difficulty == "easy":
            self.grid_size = 4
            available_width = screen_width - 100
            available_height = screen_height - 250
            cell_size = min(available_width // self.grid_size, available_height // self.grid_size)
            self.cell_width = cell_size - cell_margin
            self.cell_height = self.cell_width
 
        elif self.difficulty == "medium":
            self.grid_size = 6
            available_width = screen_width - 100
            available_height = screen_height - 250
            cell_size = min(available_width // self.grid_size, available_height // self.grid_size)
            self.cell_width = cell_size - cell_margin
            self.cell_height = self.cell_width
 
        else:  # hard
            self.grid_size = 8
            available_width = screen_width - 100
            available_height = screen_height - 250
            cell_size = min(available_width // self.grid_size, available_height // self.grid_size)
            self.cell_width = cell_size - cell_margin
            self.cell_height = self.cell_width
 
    def create_buttons(self):
        """创建游戏按钮"""
        button_width = 150
        button_height = 40
        button_margin = 20
        button_y = screen_height - 70
 
        self.restart_button = button(
            screen_width // 2 - button_width - button_margin // 2,
            button_y,
            button_width,
            button_height,
            "重新开始"
        )
 
        self.mode_button = button(
            screen_width // 2 + button_margin // 2,
            button_y,
            button_width,
            button_height,
            "切换模式"
        )
 
        # 创建难度按钮
        diff_button_width = 100
        diff_button_margin = 10
        diff_button_y = 90
 
        self.easy_button = difficultybutton(
            screen_width // 2 - diff_button_width - diff_button_margin,
            diff_button_y,
            diff_button_width,
            button_height,
            "简单",
            "easy"
        )
 
        self.medium_button = difficultybutton(
            screen_width // 2,
            diff_button_y,
            diff_button_width,
            button_height,
            "中等",
            "medium"
        )
 
        self.hard_button = difficultybutton(
            screen_width // 2 + diff_button_width + diff_button_margin,
            diff_button_y,
            diff_button_width,
            button_height,
            "困难",
            "hard"
        )
 
        # 设置默认难度按钮选中状态
        self.easy_button.selected = true
 
    def init_game(self):
        self.cells = []
        self.selected_cells = []
        self.matched_pairs = 0
        self.game_over = false
        self.shake_time = 0
        self.shake_offset = (0, 0)
 
        # 根据模式和难度确定使用的数据
        if self.mode == "elements":
            data = elements
        else:
            data = compounds
 
        # 计算单元格大小
        self.calculate_cell_size()
 
        # 根据网格大小确定需要的配对数量
        total_cells = self.grid_size * self.grid_size
        num_pairs_needed = total_cells // 2
 
        # 确保有足够的数据
        if len(data) < num_pairs_needed:
            # 如果数据不足,重复使用数据
            selected_data = []
            while len(selected_data) < num_pairs_needed:
                remaining = num_pairs_needed - len(selected_data)
                selected_data.extend(random.sample(data, min(remaining, len(data))))
        else:
            selected_data = random.sample(data, num_pairs_needed)
 
        # 创建配对列表
        pairs = []
        for i, item in enumerate(selected_data):
            pairs.append((item["chinese"], "chinese", i))
            pairs.append((item["formula"], "formula", i))
 
        # 随机打乱顺序
        random.shuffle(pairs)
 
        # 计算网格位置使其居中
        grid_width = self.grid_size * (self.cell_width + cell_margin) + cell_margin
        grid_height = self.grid_size * (self.cell_height + cell_margin) + cell_margin
        start_x = max(50, (screen_width - grid_width) // 2)  # 确保不超出左边界
        start_y = 150
 
        # 创建单元格
        self.cells = []
        for i in range(self.grid_size):
            for j in range(self.grid_size):
                index = i * self.grid_size + j
                if index < len(pairs):
                    text, cell_type, pair_id = pairs[index]
                    x = start_x + j * (self.cell_width + cell_margin) + cell_margin
                    y = start_y + i * (self.cell_height + cell_margin) + cell_margin
 
                    # 确保不超出屏幕边界
                    if x + self.cell_width > screen_width - 50:
                        self.cell_width = (screen_width - 50 - x) - cell_margin
 
                    cell = gamecell(x, y, self.cell_width, self.cell_height, text, cell_type, pair_id)
                    self.cells.append(cell)
 
        self.total_pairs = len(selected_data)
 
    def handle_click(self, pos):
        # 游戏结束后仍然可以点击按钮重新开始
        # 检查按钮点击(放在最前面,这样游戏结束后也能点击)
        if self.restart_button.is_clicked(pos):
            self.init_game()
            return
 
        if self.mode_button.is_clicked(pos):
            self.mode = "compounds" if self.mode == "elements" else "elements"
            self.init_game()
            return
 
        # 检查难度按钮点击
        if self.easy_button.is_clicked(pos) and self.difficulty != "easy":
            self.difficulty = "easy"
            self.easy_button.selected = true
            self.medium_button.selected = false
            self.hard_button.selected = false
            self.init_game()
            return
 
        if self.medium_button.is_clicked(pos) and self.difficulty != "medium":
            self.difficulty = "medium"
            self.easy_button.selected = false
            self.medium_button.selected = true
            self.hard_button.selected = false
            self.init_game()
            return
 
        if self.hard_button.is_clicked(pos) and self.difficulty != "hard":
            self.difficulty = "hard"
            self.easy_button.selected = false
            self.medium_button.selected = false
            self.hard_button.selected = true
            self.init_game()
            return
 
        # 如果游戏结束,不再处理单元格点击
        if self.game_over:
            return
 
        # 检查单元格点击
        for cell in self.cells:
            if cell.is_clicked(pos):
                if cell.selected or cell.matched:
                    return
 
                cell.selected = true
                self.selected_cells.append(cell)
 
                # 如果选中了两个单元格
                if len(self.selected_cells) == 2:
                    cell1, cell2 = self.selected_cells
 
                    # 检查是否配对成功
                    if cell1.pair_id == cell2.pair_id and cell1.cell_type != cell2.cell_type:
                        # 配对成功
                        cell1.matched = true
                        cell2.matched = true
                        self.selected_cells.clear()
                        self.matched_pairs += 1
 
                        # 检查游戏是否结束
                        if self.matched_pairs == self.total_pairs:
                            self.game_over = true
                    else:
                        # 配对失败,触发震动效果
                        self.shake_time = pygame.time.get_ticks()
                        self.shake_offset = (random.randint(-10, 10), random.randint(-10, 10))
 
                        # 短暂显示后取消选择
                        pygame.time.set_timer(pygame.userevent, 800)
 
                return
 
    def cancel_selection(self):
        for cell in self.selected_cells:
            cell.selected = false
        self.selected_cells.clear()
 
    def update(self):
        current_time = pygame.time.get_ticks()
 
        # 处理震动效果
        if self.shake_time > 0:
            shake_duration = 300
            if current_time - self.shake_time < shake_duration:
                elapsed = current_time - self.shake_time
                progress = elapsed / shake_duration
                intensity = 10 * (1 - progress)
                angle = elapsed * 0.1
                self.shake_offset = (
                    intensity * math.sin(angle * 2),
                    intensity * math.cos(angle * 3)
                )
            else:
                self.shake_time = 0
                self.shake_offset = (0, 0)
 
        # 更新按钮悬停状态
        mouse_pos = pygame.mouse.get_pos()
        self.restart_button.hovered = self.restart_button.rect.collidepoint(mouse_pos)
        self.mode_button.hovered = self.mode_button.rect.collidepoint(mouse_pos)
        self.easy_button.hovered = self.easy_button.rect.collidepoint(mouse_pos)
        self.medium_button.hovered = self.medium_button.rect.collidepoint(mouse_pos)
        self.hard_button.hovered = self.hard_button.rect.collidepoint(mouse_pos)
 
    def draw(self, screen):
        # 应用震动偏移
        if self.shake_time > 0:
            screen_scroll = screen.copy()
            screen.fill(background)
            screen.blit(screen_scroll, self.shake_offset)
        else:
            screen.fill(background)
 
        # 绘制标题 - 将模式和标题合并显示
        if self.mode == "elements":
            title = "化学元素配对游戏(模式:元素)"
        else:
            title = "化学化合物配对游戏(模式:化合物)"
 
        try:
            title_surface = chinese_font.render(title, true, title_color)
        except:
            font = pygame.font.font(none, 32)
            title_surface = font.render(title, true, title_color)
 
        screen.blit(title_surface, (screen_width // 2 - title_surface.get_width() // 2, 20))
 
        # 绘制进度
        progress_text = f"配对进度: {self.matched_pairs}/{self.total_pairs}"
        try:
            progress_surface = chinese_font.render(progress_text, true, text_color)
        except:
            font = pygame.font.font(none, 24)
            progress_surface = font.render(progress_text, true, text_color)
 
        screen.blit(progress_surface, (20, 20))
 
        # 绘制难度按钮
        self.easy_button.draw(screen)
        self.medium_button.draw(screen)
        self.hard_button.draw(screen)
 
        # 绘制所有单元格
        for cell in self.cells:
            cell.draw(screen)
 
        # 绘制按钮(在遮罩之前绘制,这样按钮不会被遮住)
        self.restart_button.draw(screen)
        self.mode_button.draw(screen)
 
        # 如果游戏结束,显示胜利消息
        if self.game_over:
            # 创建一个透明遮罩,但不要太暗,让玩家能看到下面的按钮
            overlay = pygame.surface((screen_width, screen_height), pygame.srcalpha)
            overlay.fill((0, 0, 0, 100))  # 降低透明度,让按钮可见
            screen.blit(overlay, (0, 0))
 
            win_text = "恭喜!游戏胜利!"
            try:
                win_surface = chinese_font.render(win_text, true, (255, 255, 255))
            except:
                font = pygame.font.font(none, 32)
                win_surface = font.render(win_text, true, (255, 255, 255))
 
            screen.blit(win_surface, (screen_width // 2 - win_surface.get_width() // 2, screen_height // 2 - 50))
 
            restart_text = "点击重新开始按钮或按enter键再来一局"
            try:
                restart_surface = chinese_font.render(restart_text, true, (255, 255, 255))
            except:
                font = pygame.font.font(none, 24)
                restart_surface = font.render(restart_text, true, (255, 255, 255))
 
            screen.blit(restart_surface,
                        (screen_width // 2 - restart_surface.get_width() // 2, screen_height // 2 + 30))
 
 
def main():
    game = game()
    clock = pygame.time.clock()
    running = true
 
    while running:
        for event in pygame.event.get():
            if event.type == quit:
                running = false
 
            elif event.type == mousebuttondown:
                if event.button == 1:
                    game.handle_click(event.pos)
 
            elif event.type == keydown:
                # 游戏结束后按任意键重新开始
                if game.game_over:
                    game.init_game()
 
            elif event.type == pygame.userevent:
                # 计时器事件,用于取消错误选择
                game.cancel_selection()
                pygame.time.set_timer(pygame.userevent, 0)
 
        game.update()
        game.draw(screen)
 
        pygame.display.flip()
        clock.tick(60)
 
    pygame.quit()
    sys.exit()
 
 
if __name__ == "__main__":
    main()

到此这篇关于基于python和pygame打造一个有趣的化学配对记忆游戏的文章就介绍到这了,更多相关python pygame游戏开发内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2026  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com