Python自我娱乐小游戏:跳跳糖
跳跳糖跑酷游戏,自己设置最简单的参数你能跑多少米
1、效果图
2、源码
一、效果图
二、代码
这里有一个智能家居项目可以看看(开源)
# -*- coding:utf-8 -*-
"""
作者:杨桃清
日期: 2025年04日28 02:50:40
"""
import pygame
import tkinter as tk
from tkinter import ttk, messagebox
import random
import json
import os
import sys
from typing import Dict, List, Tuple, Optional# 常量定义
SCREEN_SIZE = (400, 600) # 统一使用游戏界面尺寸
RECORD_FILE = "flappy_bird_record.json"
DEFAULT_SETTINGS = {"gravity": 0.2,"bird_speed": -7,"pipe_frequency": 1500,"pipe_gap_min": 120,"pipe_gap_max": 200,"pipe_speed": 2,"difficulty_step": 0.1,
}class GameSettingsWindow:"""参数设置窗口"""def __init__(self, parent):self.top = tk.Toplevel(parent)self.top.title(" 游戏参数设置")self.top.resizable(False, False)self.settings = DEFAULT_SETTINGS.copy()self._create_widgets()def _create_widgets(self):# 重力设置ttk.Label(self.top, text="重力:").grid(row=0, column=0, padx=5, pady=5, sticky="e")self.gravity_entry = ttk.Entry(self.top)self.gravity_entry.insert(0, str(self.settings["gravity"]))self.gravity_entry.grid(row=0, column=1, padx=5, pady=5)# 跳跃力度ttk.Label(self.top, text="跳跃力度:").grid(row=1, column=0, padx=5, pady=5, sticky="e")self.bird_speed_entry = ttk.Entry(self.top)self.bird_speed_entry.insert(0, str(self.settings["bird_speed"]))self.bird_speed_entry.grid(row=1, column=1, padx=5, pady=5)# 柱子生成频率ttk.Label(self.top, text="柱子频率(ms):").grid(row=2, column=0, padx=5, pady=5, sticky="e")self.pipe_freq_entry = ttk.Entry(self.top)self.pipe_freq_entry.insert(0, str(self.settings["pipe_frequency"]))self.pipe_freq_entry.grid(row=2, column=1, padx=5, pady=5)# 柱子间隙ttk.Label(self.top, text="柱子最小间隙:").grid(row=3, column=0, padx=5, pady=5, sticky="e")self.pipe_gap_min_entry = ttk.Entry(self.top)self.pipe_gap_min_entry.insert(0, str(self.settings["pipe_gap_min"]))self.pipe_gap_min_entry.grid(row=3, column=1, padx=5, pady=5)ttk.Label(self.top, text="柱子最大间隙:").grid(row=4, column=0, padx=5, pady=5, sticky="e")self.pipe_gap_max_entry = ttk.Entry(self.top)self.pipe_gap_max_entry.insert(0, str(self.settings["pipe_gap_max"]))self.pipe_gap_max_entry.grid(row=4, column=1, padx=5, pady=5)# 柱子移动速度ttk.Label(self.top, text="柱子速度:").grid(row=5, column=0, padx=5, pady=5, sticky="e")self.pipe_speed_entry = ttk.Entry(self.top)self.pipe_speed_entry.insert(0, str(self.settings["pipe_speed"]))self.pipe_speed_entry.grid(row=5, column=1, padx=5, pady=5)# 难度增幅ttk.Label(self.top, text="难度增幅:").grid(row=6, column=0, padx=5, pady=5, sticky="e")self.difficulty_step_entry = ttk.Entry(self.top)self.difficulty_step_entry.insert(0, str(self.settings["difficulty_step"]))self.difficulty_step_entry.grid(row=6, column=1, padx=5, pady=5)# 按钮区域btn_frame = ttk.Frame(self.top)btn_frame.grid(row=7, column=0, columnspan=2, pady=10)ttk.Button(btn_frame, text="保存", command=self._save_settings).pack(side=tk.LEFT, padx=5)ttk.Button(btn_frame, text="恢复默认", command=self._reset_defaults).pack(side=tk.LEFT, padx=5)ttk.Button(btn_frame, text="取消", command=self.top.destroy).pack(side=tk.RIGHT, padx=5)def _save_settings(self):"""保存参数设置"""try:self.settings = {"gravity": float(self.gravity_entry.get()),"bird_speed": float(self.bird_speed_entry.get()),"pipe_frequency": int(self.pipe_freq_entry.get()),"pipe_gap_min": int(self.pipe_gap_min_entry.get()),"pipe_gap_max": int(self.pipe_gap_max_entry.get()),"pipe_speed": int(self.pipe_speed_entry.get()),"difficulty_step": float(self.difficulty_step_entry.get()),}self.top.destroy()except ValueError:messagebox.showerror(" 错误", "请输入有效的数值参数")def _reset_defaults(self):"""恢复默认设置"""self.settings = DEFAULT_SETTINGS.copy()self.gravity_entry.delete(0, tk.END)self.gravity_entry.insert(0, str(self.settings["gravity"]))self.bird_speed_entry.delete(0, tk.END)self.bird_speed_entry.insert(0, str(self.settings["bird_speed"]))self.pipe_freq_entry.delete(0, tk.END)self.pipe_freq_entry.insert(0, str(self.settings["pipe_frequency"]))self.pipe_gap_min_entry.delete(0, tk.END)self.pipe_gap_min_entry.insert(0, str(self.settings["pipe_gap_min"]))self.pipe_gap_max_entry.delete(0, tk.END)self.pipe_gap_max_entry.insert(0, str(self.settings["pipe_gap_max"]))self.pipe_speed_entry.delete(0, tk.END)self.pipe_speed_entry.insert(0, str(self.settings["pipe_speed"]))self.difficulty_step_entry.delete(0, tk.END)self.difficulty_step_entry.insert(0, str(self.settings["difficulty_step"]))def get_settings(self) -> Dict:"""获取当前设置"""return self.settingsclass MainMenu:"""游戏主菜单"""def __init__(self):pygame.init()self.screen = pygame.display.set_mode(SCREEN_SIZE)pygame.display.set_caption("Flappy Bird")self.clock = pygame.time.Clock()self.settings = DEFAULT_SETTINGS.copy()self._load_resources()def _load_resources(self):"""加载游戏资源"""# 字体try:self.title_font = pygame.font.SysFont("simhei", 48)self.menu_font = pygame.font.SysFont("simhei", 36)self.info_font = pygame.font.SysFont("simhei", 24)except:self.title_font = pygame.font.SysFont(None, 48)self.menu_font = pygame.font.SysFont(None, 36)self.info_font = pygame.font.SysFont(None, 24)# 颜色self.colors = {"background": (240, 240, 240),"title": (30, 30, 30),"button": (70, 130, 180),"button_hover": (100, 160, 210),"text": (255, 255, 255),"shadow": (100, 100, 100),}# 加载记录self.max_distance = self._load_record()def _load_record(self) -> float:"""加载历史记录"""if os.path.exists(RECORD_FILE):try:with open(RECORD_FILE, "r") as f:return json.load(f).get("max_distance", 0)except:return 0return 0def _draw_menu(self):"""绘制主菜单"""self.screen.fill(self.colors["background"])# 绘制标题title = self.title_font.render("Flappy Bird", True, self.colors["title"])title_rect = title.get_rect(center=(SCREEN_SIZE[0] // 2, 120))self.screen.blit(title, title_rect)# 绘制按钮button_data = [("开始游戏", (SCREEN_SIZE[0] // 2, 250)),("退出游戏", (SCREEN_SIZE[0] // 2, 350)),("设置", (SCREEN_SIZE[0] - 80, 40)),]self.buttons = []mouse_pos = pygame.mouse.get_pos()for text, pos in button_data:if text == "设置":button_rect = pygame.Rect(0, 0, 80, 40)else:button_rect = pygame.Rect(0, 0, 200, 60)button_rect.center = pos# 按钮状态hover = button_rect.collidepoint(mouse_pos)color = self.colors["button_hover"] if hover else self.colors["button"]# 绘制按钮pygame.draw.rect(self.screen, color, button_rect, border_radius=10)pygame.draw.rect(self.screen, self.colors["shadow"], button_rect, 2, border_radius=10)# 绘制按钮文字if text == "设置":text_surf = self.info_font.render(text, True, self.colors["text"])else:text_surf = self.menu_font.render(text, True, self.colors["text"])text_rect = text_surf.get_rect(center=button_rect.center)self.screen.blit(text_surf, text_rect)self.buttons.append((text, button_rect))# 显示最高记录if self.max_distance > 0:record_text = self.info_font.render(f" 最远记录: {int(self.max_distance)} 米", True, self.colors["title"])record_rect = record_text.get_rect(midleft=(20, SCREEN_SIZE[1] - 30))self.screen.blit(record_text, record_rect)pygame.display.flip()def run(self):"""运行主菜单循环"""running = Truewhile running:for event in pygame.event.get():if event.type == pygame.QUIT:running = Falseif event.type == pygame.MOUSEBUTTONDOWN:mouse_pos = pygame.mouse.get_pos()for text, rect in self.buttons:if rect.collidepoint(mouse_pos):if text == "开始游戏":self._start_game()elif text == "退出游戏":running = Falseelif text == "设置":self._open_settings()self._draw_menu()self.clock.tick(60)pygame.quit()sys.exit()def _open_settings(self):"""打开设置窗口"""root = tk.Tk()root.withdraw() # 隐藏主窗口settings_window = GameSettingsWindow(root)root.wait_window(settings_window.top)if hasattr(settings_window, 'settings'):self.settings = settings_window.settingsdef _start_game(self):"""启动游戏"""game = GameEngine(self.settings)game.run()# 更新记录new_record = self._load_record()if new_record > self.max_distance:self.max_distance = new_recordclass GameEngine:"""游戏引擎"""def __init__(self, settings: Dict):self.settings = settingsself.screen = pygame.display.set_mode(SCREEN_SIZE)pygame.display.set_caption("Flappy Bird - 游戏中")self.clock = pygame.time.Clock()# 游戏状态self.bird = pygame.Rect(100, 250, 30, 30)self.bird_speed = 0self.pipes = []self.distance = 0self.score = 0self.last_pipe_time = 0self.max_distance = self._load_record()# 资源加载self._load_resources()def _load_resources(self):"""加载游戏资源"""# 字体try:self.font = pygame.font.SysFont("simhei", 24)self.large_font = pygame.font.SysFont("simhei", 36)except:self.font = pygame.font.SysFont(None, 24)self.large_font = pygame.font.SysFont(None, 36)# 颜色self.colors = {"background": (255, 255, 255),"bird": (0, 0, 255),"pipe": (0, 180, 0),"text": (0, 0, 0),"game_over_bg": (240, 240, 240),"button": (70, 130, 180),"button_hover": (100, 160, 210),"button_text": (255, 255, 255),"overlay": (0, 0, 0, 180),}def _load_record(self) -> float:"""加载历史记录"""if os.path.exists(RECORD_FILE):try:with open(RECORD_FILE, "r") as f:return json.load(f).get("max_distance", 0)except:return 0return 0def _save_record(self):"""保存新记录"""if self.distance > self.max_distance:self.max_distance = self.distancewith open(RECORD_FILE, "w") as f:json.dump({"max_distance": self.max_distance}, f)def _handle_events(self) -> bool:"""处理游戏事件"""for event in pygame.event.get():if event.type == pygame.QUIT:return Falseif event.type == pygame.KEYDOWN:if event.key == pygame.K_SPACE:self.bird_speed = self.settings["bird_speed"]return Truedef _update_game_state(self):"""更新游戏状态"""# 更新小鸟位置self.bird_speed += self.settings["gravity"]self.bird.y += self.bird_speedself.distance += 0.05# 更新记录if self.distance > self.max_distance:self._save_record()# 动态难度调整difficulty = 1 + self.settings["difficulty_step"] * (self.distance // 100)pipe_freq = max(500, self.settings["pipe_frequency"] / difficulty)gap_min = max(80, self.settings["pipe_gap_min"] - (difficulty - 1) * 10)gap_max = max(150, self.settings["pipe_gap_max"] - (difficulty - 1) * 15)pipe_speed = min(5, self.settings["pipe_speed"] + (difficulty - 1) * 0.5)# 生成新柱子current_time = pygame.time.get_ticks()if current_time - self.last_pipe_time > pipe_freq:gap = random.randint(int(gap_min), int(gap_max))height = random.randint(50, SCREEN_SIZE[1] - gap - 50)self.pipes.append([pygame.Rect(SCREEN_SIZE[0], 0, 50, height),pygame.Rect(SCREEN_SIZE[0], height + gap, 50, SCREEN_SIZE[1] - height - gap)])self.last_pipe_time = current_time# 移动柱子for pipe in self.pipes[:]:pipe[0].x -= pipe_speedpipe[1].x -= pipe_speedif pipe[0].x < -50:self.pipes.remove(pipe)self.score += 1def _check_collisions(self) -> bool:"""检测碰撞"""if self.bird.y <= 0 or self.bird.y >= SCREEN_SIZE[1]:return Truereturn any(self.bird.colliderect(pipe[0]) or self.bird.colliderect(pipe[1])for pipe in self.pipes)def _draw_game(self):"""绘制游戏画面"""self.screen.fill(self.colors["background"])# 绘制小鸟pygame.draw.rect(self.screen, self.colors["bird"], self.bird)# 绘制柱子for pipe in self.pipes:pygame.draw.rect(self.screen, self.colors["pipe"], pipe[0])pygame.draw.rect(self.screen, self.colors["pipe"], pipe[1])# 显示游戏信息info = [f"得分: {self.score}",f"距离: {int(self.distance)} 米",f"记录: {int(self.max_distance)} 米"]for i, text in enumerate(info):text_surface = self.font.render(text, True, self.colors["text"])self.screen.blit(text_surface, (10, 10 + i * 30))pygame.display.flip()def _show_game_over(self) -> bool:"""显示结束界面并返回是否重玩"""overlay = pygame.Surface(SCREEN_SIZE, pygame.SRCALPHA)overlay.fill(self.colors["overlay"])self.screen.blit(overlay, (0, 0))# 游戏结果文本texts = [("游戏结束!", 36, (SCREEN_SIZE[0] // 2, 150)),(f"本次得分: {self.score}", 30, (SCREEN_SIZE[0] // 2, 220)),(f"飞行距离: {int(self.distance)} 米", 30, (SCREEN_SIZE[0] // 2, 270)),(f"最远记录: {int(self.max_distance)} 米", 30, (SCREEN_SIZE[0] // 2, 320))]for text, size, pos in texts:text_surface = self.large_font.render(text, True, (255, 255, 255))text_rect = text_surface.get_rect(center=pos)self.screen.blit(text_surface, text_rect)# 创建按钮buttons = [("重新开始", (SCREEN_SIZE[0] // 2 - 110, 400)),("返回菜单", (SCREEN_SIZE[0] // 2 + 110, 400))]button_rects = []mouse_pos = pygame.mouse.get_pos()for text, pos in buttons:rect = pygame.Rect(0, 0, 150, 50)rect.center = pos# 按钮悬停效果btn_color = self.colors["button_hover"] if rect.collidepoint(mouse_pos) else self.colors["button"]pygame.draw.rect(self.screen, btn_color, rect, border_radius=10)pygame.draw.rect(self.screen, (255, 255, 255), rect, 2, border_radius=10)text_surface = self.font.render(text, True, self.colors["button_text"])text_rect = text_surface.get_rect(center=rect.center)self.screen.blit(text_surface, text_rect)button_rects.append((text, rect))pygame.display.flip()# 处理按钮点击while True:for event in pygame.event.get():if event.type == pygame.QUIT:return Falseif event.type == pygame.MOUSEBUTTONDOWN:pos = pygame.mouse.get_pos()for text, rect in button_rects:if rect.collidepoint(pos):if text == "重新开始":return Trueelse:return Falseself.clock.tick(60)def run(self):"""运行游戏主循环"""running = Truewhile running:self.clock.tick(60)if not self._handle_events():breakself._update_game_state()if self._check_collisions():if self._show_game_over():# 重新开始游戏self.__init__(self.settings)continueelse:breakself._draw_game()pygame.display.set_caption("Flappy Bird")return Falseif __name__ == "__main__":menu = MainMenu()menu.run()