20254304 实验四 综合实践《Python程序设计》实验报告
20254304 2025-2026-2 《Python程序设计》 综合实践报告
课程:《Python程序设计》
班级: 2543
姓名: 田奇
学号:20254304
实验教师:王志强
实验日期:2026年5月25日
必修/选修: 公选课
1.实验内容
1.1 实验任务
Python综合应用:爬虫、数据处理、可视化、机器学习、神经网络、游戏、网络安全等。
例如:编写从社交网络爬取数据,实现可视化舆情监控或者情感分析。
例如:利用公开数据集,开展图像分类、恶意软件检测等
例如:利用Python库,基于OCR技术实现自动化提取图片中数据,并填入excel中。
例如:爬取天气数据,实现自动化微信提醒
例如:利用爬虫,实现自动化下载网站视频、文件等。
例如:编写小游戏:坦克大战、贪吃蛇、扫雷等等
注:在Windows/Linux系统上使用VIM、PDB、IDLE、Pycharm等工具编程实现。
评分标准:
(1)程序能运行,功能丰富(至少5个功能)。(需求提交源代码,并建议录制程序运行的视频)
(2)综合实践报告,要体现实验分析、设计、实现过程、结果等信息,格式规范,逻辑清晰,结构合理。
(3)在实践报告中,需要对全课进行总结,并写课程感想体会、意见和建议等。
1.2 选题
Python 的小游戏开发因其趣味性强、反馈直观、逻辑清晰,成为检验程序设计能力与算法思维的重要实践方式。且本学期上了python课,加之大模型的发展,都创造了有利条件。经典小游戏如《贪吃蛇》《扫雷》《俄罗斯方块》等在计算机发展史上具有重要地位,其规则简单但逻辑严密,但传统实现往往停留在单一玩法的层面,缺乏个性化设计与功能扩展。因此,这次综合实践,我想选取一款经典python小游戏“跑酷”作为基础原型,在保留其基本玩法的前提下,进行优化,例如引入多难度模式、单人选关闯关、加一些buff或者机制等功能。
1.3 实验工具
操作系统:Windows
开发工具:PyCharm
第三方库:pygame
2. 实验过程及结果
2.1 实验分析
(1)可行性
Python 在游戏开发领域同样具有较强的适用性。借助 Pygame 库,可以快速实现图形渲染、事件处理、碰撞检测等功能。结合本学期所学的 Python 基础知识(函数、面向对象)以及大模型辅助编程能力,开发一个结构完整的单机跑酷游戏在技术上是可行的。
(2)需求
本实验要求实现一个功能不少于5个、可运行的代码。跑酷游戏满足该要求:
账号注册与登录;多状态游戏控制(登录、菜单、游戏、暂停、结束等);玩家角色具备跳跃、滑铲、冲刺等动作;多关卡系统,难度随关卡递增;金币收集、道具系统与商店系统;碰撞检测与生命值管理;界面信息清晰,交互友好。
(3)方法
本实验采用面向对象程序设计思想进行开发,将游戏中的实体抽象为类,降低模块间耦合度。
(4)难点
例如,
游戏状态切换问题,游戏在不同阶段(如暂停、死亡、通关)之间容易出现逻辑混乱。因此定义统一的游戏状态常量,由主循环集中控制状态切换。
碰撞检测准确性问题,玩家滑铲、跳跃时碰撞体积发生变化。因此根据玩家状态动态调整碰撞矩形。
数据一致问题,道具需要在程序重启后仍然存在。
(5)创新点
例如,相比于传统单一玩法的python单机跑酷游戏,本实验在以下方面进行优化:引入账号系统,支持多用户独立存档;设计不同难度关卡,在“高手关”后提供无限闯关模式;增加道具与商店系统;使用粒子特效与动态背景,提升视觉表现力。
(6)预期
完成一个可在 Windows 平台稳定运行、功能完整的 Python 跑酷游戏,并通过这次综合实践报告展示实验分析、设计思路、实现过程与运行结果。
2.2 实验功能设计
下面是较为完整的跑酷小游戏功能设计:
(1)账号系统(注册 / 登录 / 存档)
(2)游戏状态管理(LOGIN登录 / 注册;MENU主菜单 / 关卡选择;PLAYING游戏进行中;PAUSED暂停;GAMEOVER游戏失败;LEVEL_CLEAR关卡通关;END_CHOICE最终通关选择)
(3)UI 界面(展示游戏信息、提供操作入口并增强交互体验。在代码中,UI 包括分数与金币显示、关卡信息、按钮菜单、相关提示以及商店界面,使玩家能够直观地了解游戏状态并进行控制。)
(4)界面背景(粒子、爱心树等)
(5)玩家(各种动作/shop购买道具应用于玩家/玩家与障碍的碰撞检测、金币检测)
(6)障碍物、关卡系统(难度递增。新手关,速度慢,障碍少;进阶关,宽/矮障碍;高手关,空中障碍+位置不定+速度快;三关通过之后也可选择继续无限闯关)
(7)金币、道具、商店系统(不同大小金币代表不同分值和数量;道具包含模拟磁铁(其实就是扩大范围,减缓金币速度)、护盾(碰到障碍物不扣次数,否则碰到三次就gameover)等;shop拿金币coins购买道具)
2.3 实现过程
(1)生成python游戏,先检查pycharm有没有安装过相应的库

(2)刚开始不知从何着手,先输入相关的想法,进行第一轮对话,让大模型生成基本代码框架
代码框架:
import pygame
import math
import random
WIDTH, HEIGHT = 1200, 600
FPS = 60
pygame.init()
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("NEON PARKOUR · LEVEL EDITION")
clock = pygame.time.Clock()
TITLE_FONT = pygame.font.Font(None, 72)
UI_FONT = pygame.font.Font(None, 32)
SMALL_FONT = pygame.font.Font(None, 24)
BG = (8, 8, 18)
NEON_BLUE = (0, 220, 255)
NEON_PINK = (255, 80, 180)
NEON_GREEN = (0, 255, 170)
COIN_COLOR = (255, 215, 0)
UI_BG = (20, 20, 35, 180)
PLAYER_COLOR = (0, 200, 255)
SLIDE_COLOR = (0, 255, 200)
DASH_COLOR = (255, 255, 0)
STATE_MENU = 0
STATE_PLAYING = 1
STATE_PAUSED = 2
STATE_GAMEOVER = 3
game_state = STATE_MENU
class Player:
def __init__(self):
self.x = 140
self.base_y = 420
self.y = self.base_y
self.width = 30
self.height = 50
self.velocity_y = 0
self.gravity = 0.8
self.jump_power = -14
self.jumps_left = 2
self.is_sliding = False
self.slide_timer = 0
self.is_dashing = False
self.dash_timer = 0
self.base_speed = 6
self.speed = self.base_speed
self.coins = 0
self.magnet = False
self.shield = False
self.powerup_timers = {}
self.color_flash = 0
def jump(self):
if self.jumps_left > 0:
self.velocity_y = self.jump_power
self.jumps_left -= 1
def dash(self):
if not self.is_dashing:
self.is_dashing = True
self.dash_timer = 20
self.speed = self.base_speed * 2
def slide(self):
if not self.is_sliding and self.on_ground():
self.is_sliding = True
self.slide_timer = 15
def apply_powerup(self, name):
self.powerup_timers[name] = 600
def on_ground(self):
return self.y + self.height >= 470
def update(self):
self.velocity_y += self.gravity
self.y += self.velocity_y
if self.on_ground():
self.y = 470 - self.height
self.velocity_y = 0
self.jumps_left = 2
if self.is_sliding:
self.slide_timer -= 1
if self.slide_timer <= 0:
self.is_sliding = False
if self.is_dashing:
self.dash_timer -= 1
if self.dash_timer <= 0:
self.is_dashing = False
self.speed = self.base_speed
for k in list(self.powerup_timers):
self.powerup_timers[k] -= 1
if self.powerup_timers[k] <= 0:
del self.powerup_timers[k]
self.magnet = "magnet" in self.powerup_timers
self.shield = "shield" in self.powerup_timers
if self.color_flash > 0:
self.color_flash -= 1
@property
def rect(self):
if self.is_sliding:
return pygame.Rect(self.x, self.y + self.height // 2,
self.width, self.height // 2)
return pygame.Rect(self.x, int(self.y), self.width, self.height)
def draw(self, surf):
color = (255, 100, 100) if self.color_flash > 0 else (
DASH_COLOR if self.is_dashing else (
SLIDE_COLOR if self.is_sliding else PLAYER_COLOR))
pygame.draw.rect(surf, color, self.rect)
class Coin:
def __init__(self, x):
self.x = x
self.y = random.randint(320, 420)
self.radius = 8
def update(self, speed):
self.x -= speed
def draw(self, surf):
pygame.draw.circle(surf, COIN_COLOR, (int(self.x), self.y), self.radius)
class PowerUp:
def __init__(self, x, kind):
self.x = x
self.y = 300
self.kind = kind
self.radius = 12
def draw(self, surf):
colors = {
"magnet": (0, 255, 255),
"shield": (255, 100, 100),
"double": (180, 100, 255)
}
pygame.draw.circle(surf, colors[self.kind], (int(self.x), self.y), self.radius)
class Shop:
def __init__(self):
self.open = False
def toggle(self):
self.open = not self.open
def draw(self, surf):
if not self.open:
return
pygame.draw.rect(surf, (30, 30, 45), (400, 120, 400, 260))
texts = [
"Magnet - 50 coins",
"Shield - 80 coins",
"Double Jump+ - 100 coins"
]
y = 150
for t in texts:
surf.blit(UI_FONT.render(t, True, (255, 255, 255)), (440, y))
y += 60
class Star:
def __init__(self):
self.x = random.randint(0, WIDTH)
self.y = random.randint(0, HEIGHT)
self.size = random.uniform(0.5, 2.5)
self.speed = random.uniform(0.2, 1.2)
self.brightness = random.randint(120, 255)
def update(self):
self.x -= self.speed
if self.x < 0:
self.x = WIDTH
self.y = random.randint(0, HEIGHT)
def draw(self, surf):
color = (self.brightness, self.brightness, self.brightness)
pygame.draw.circle(surf, color, (int(self.x), int(self.y)), int(self.size))
class GlowParticle:
def __init__(self):
self.x = random.randint(0, WIDTH)
self.y = random.randint(0, HEIGHT)
self.vx = random.uniform(-0.3, 0.3)
self.vy = random.uniform(-0.1, 0.1)
self.radius = random.uniform(1, 3)
self.color = random.choice([NEON_BLUE, NEON_PINK, NEON_GREEN])
def update(self):
self.x += self.vx
self.y += self.vy
if self.x < 0 or self.x > WIDTH:
self.vx *= -1
def draw(self, surf):
pygame.draw.circle(surf, (*self.color, 50),
(int(self.x), int(self.y)), int(self.radius))
class Particle:
def __init__(self, x, y, color):
self.x = x
self.y = y
self.vx = random.uniform(-2, 2)
self.vy = random.uniform(-2, 2)
self.life = 20
self.color = color
def update(self):
self.x += self.vx
self.y += self.vy
self.life -= 1
def alive(self):
return self.life > 0
def draw(self, surf):
pygame.draw.circle(surf, self.color, (int(self.x), int(self.y)), 3)
player = Player()
coins = []
powerups = []
particles = []
obstacles = []
shop = Shop()
stars = [Star() for _ in range(120)]
glows = [GlowParticle() for _ in range(40)]
spawn_timer = 0
score = 0
level = 1
LEVEL_TARGET = 300
def draw_hud(surf, player, score, level):
panel = pygame.Surface((340, 140), pygame.SRCALPHA)
panel.fill(UI_BG)
surf.blit(panel, (20, 20))
surf.blit(UI_FONT.render(f"LEVEL {level}", True, NEON_PINK), (35, 30))
surf.blit(UI_FONT.render(f"SCORE {score}", True, NEON_BLUE), (35, 65))
surf.blit(UI_FONT.render(f"COINS {player.coins}", True, COIN_COLOR), (35, 100))
def draw_controls(surf):
hints = [
"[SPACE] Jump",
"[SHIFT] Dash",
"[CTRL] Slide",
"[P] Pause",
"[G] Shop",
"[R] Restart"
]
y = HEIGHT - 160
for h in hints:
surf.blit(SMALL_FONT.render(h, True, (160, 160, 160)), (WIDTH - 220, y))
y += 24
def draw_menu(surf):
surf.fill(BG)
title = TITLE_FONT.render("NEON PARKOUR", True, NEON_BLUE)
surf.blit(title, (WIDTH // 2 - title.get_width() // 2, 160))
surf.blit(UI_FONT.render("[ENTER] START", True, NEON_GREEN), (WIDTH // 2 - 120, 300))
surf.blit(UI_FONT.render("[ESC] QUIT", True, NEON_PINK), (WIDTH // 2 - 110, 350))
def draw_pause(surf):
overlay = pygame.Surface((WIDTH, HEIGHT), pygame.SRCALPHA)
overlay.fill((0, 0, 0, 160))
surf.blit(overlay, (0, 0))
text = TITLE_FONT.render("PAUSED", True, NEON_PINK)
surf.blit(text, (WIDTH // 2 - text.get_width() // 2, 200))
tips = [
"SPACE Jump",
"SHIFT Dash",
"CTRL Slide",
"G Shop",
"R Restart",
"P Resume"
]
y = 300
for t in tips:
surf.blit(UI_FONT.render(t, True, NEON_GREEN), (WIDTH // 2 - 100, y))
y += 40
def draw_gameover(surf, score, coins):
overlay = pygame.Surface((WIDTH, HEIGHT), pygame.SRCALPHA)
overlay.fill((0, 0, 0, 200))
surf.blit(overlay, (0, 0))
surf.blit(TITLE_FONT.render("GAME OVER", True, (255, 80, 80)),
(WIDTH // 2 - 210, 180))
surf.blit(UI_FONT.render(f"Score: {score}", True, NEON_BLUE),
(WIDTH // 2 - 90, 280))
surf.blit(UI_FONT.render(f"Coins: {coins}", True, COIN_COLOR),
(WIDTH // 2 - 90, 330))
surf.blit(UI_FONT.render("Press [R] to Restart", True, NEON_GREEN),
(WIDTH // 2 - 160, 460))
running = True
while running:
clock.tick(FPS)
screen.fill(BG)
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == pygame.KEYDOWN:
if game_state == STATE_MENU:
if event.key == pygame.K_RETURN:
game_state = STATE_PLAYING
elif game_state == STATE_PLAYING:
if event.key == pygame.K_SPACE:
player.jump()
if event.key == pygame.K_LSHIFT:
player.dash()
if event.key == pygame.K_LCTRL:
player.slide()
if event.key == pygame.K_p:
game_state = STATE_PAUSED
if event.key == pygame.K_g:
shop.toggle()
elif game_state == STATE_PAUSED:
if event.key == pygame.K_p:
game_state = STATE_PLAYING
elif game_state == STATE_GAMEOVER:
if event.key == pygame.K_r:
player = Player()
coins.clear()
powerups.clear()
obstacles.clear()
particles.clear()
score = 0
level = 1
game_state = STATE_PLAYING
if game_state == STATE_PLAYING:
player.update()
player.speed = player.base_speed + level
for star in stars:
star.update()
for glow in glows:
glow.update()
spawn_timer += 1
if spawn_timer > max(40, 90 - level * 5):
obstacles.append(pygame.Rect(WIDTH, 420,
random.randint(30, 50),
random.randint(40, 70)))
coins.append(Coin(WIDTH + random.randint(0, 100)))
if random.random() < 0.05:
powerups.append(PowerUp(WIDTH + 100,
random.choice(["magnet", "shield", "double"])))
spawn_timer = 0
for c in coins:
c.update(player.speed)
if player.magnet:
dx = player.rect.centerx - c.x
dy = player.rect.centery - c.y
d = math.hypot(dx, dy)
if d:
c.x += dx / d * 4
for p in powerups:
p.x -= player.speed
for o in obstacles:
o.x -= player.speed
for c in coins[:]:
if c.x < player.rect.right and c.x > player.rect.left:
coins.remove(c)
player.coins += 1
score += 10
particles.append(Particle(c.x, c.y, COIN_COLOR))
for pu in powerups[:]:
if pu.x < player.rect.right and pu.x > player.rect.left:
player.apply_powerup(pu.kind)
powerups.remove(pu)
for o in obstacles[:]:
if o.colliderect(player.rect):
if player.shield:
obstacles.remove(o)
player.shield = False
else:
player.color_flash = 10
game_state = STATE_GAMEOVER
for p in particles[:]:
p.update()
if not p.alive():
particles.remove(p)
if score >= level * LEVEL_TARGET:
level += 1
if game_state == STATE_MENU:
draw_menu(screen)
elif game_state == STATE_PLAYING:
for star in stars:
star.draw(screen)
for glow in glows:
glow.draw(screen)
player.draw(screen)
for o in obstacles:
pygame.draw.rect(screen, (255, 80, 80), o)
for c in coins:
c.draw(screen)
for pu in powerups:
pu.draw(screen)
for p in particles:
p.draw(screen)
pygame.draw.line(screen, (80, 80, 80), (0, 470), (WIDTH, 470), 4)
pygame.draw.line(screen, NEON_BLUE, (0, 470), (WIDTH, 470), 2)
pygame.draw.line(screen, NEON_PINK, (0, 472), (WIDTH, 472), 1)
draw_hud(screen, player, score, level)
draw_controls(screen)
shop.draw(screen)
elif game_state == STATE_PAUSED:
draw_pause(screen)
elif game_state == STATE_GAMEOVER:
draw_gameover(screen, score, player.coins)
pygame.display.flip()
pygame.quit()
运行结果:
到这里,基本的单机“跑酷游戏”就已经可以通过运行代码玩了
当然,最最原始的版本其实是下面这个,是第一次直接给我的,特别简陋,而且问题很多。在第一轮对话中,已经多提一些指令,升级过一些基本的要素,如背景、按键提示、一段跳变成二段跳(可调整)等
(3)接下来就是把自己的一些新想法不断喂给大模型,根据其提出的更改建议,并结合自身实践,对最开始的代码框架进行修改、调整、补充,以丰富程序功能。
具体包括,例如:
增加不同难度的关卡和自主选关,以及失败后可以点击鼠标返回而不是直接弹出来,重新运行代码或者重新进。这一过程中还涉及相关界面的各种元素的调整改进。
对于游戏内容进行优化,例如,大模型后面突然给的代码,运行后显示不出来字体,呈现的是一堆方框而不是汉字,那就要重新调整,比如我选择计算机C盘自带的字体;再比如修改一些存在的问题,初版代码运行后,玩家碰到障碍物竟然加分,这需要调整,以及什么时候算做失败,达到什么门槛可以进入下一关,都可以调整。还有游戏本身的内容,如障碍物的位置、移动速度、出现频率,玩家碰到的加分金币的轨迹,其他加分方式等也可以不断补充。
(4)不断发现代码的问题和瑕疵,比如shop功能不能用(初版代码给的纯摆设),比如在修改代码过程中出现菜单页面显示“返回主菜单”,比如金币不能累计且还存在一些问题,等等,以及完善页面灵活切换、鼠标或键盘灵活点击、页面文字排版布局、内容优化等。总之,继续不断修改,直至形成最终版代码
下面,举一些例子,例如:
- 引入账号与存档系统,金币coins和个人道具保留,但这里设定关卡进度再打开游戏会重置
- 游戏状态更多样完善,在MENU / PLAY / PAUSE / GAMEOVER的基础上新增 LOGIN / LEVEL_CLEAR / END_CHOICE
- 初版只有level += 1 spawn_timer -= level * 5,无明确关卡概念,改动后体现“游戏难度渐进设计”,增强挑战性,并在最后可选无限闯关。
- 障碍物重构(多态),支持高 / 矮 / 宽 /空中障碍等
- 金币种类由1 种增加至3 种(small / normal / big),对应不同价值,位置随机
- 商店系统升级,初版,无法购买,无实际消耗,无库存,大模型给的代码纯摆设;新版:商店 UI,金币消费,库存,快捷键使用(1 / 2 / 3)并产生对应效果功能
- 交互与UI体验提升(鼠标、键盘相对应的按键或按钮);
- 背景与特效增强(发光粒子、心跳树等);对一些页面排版布局(文字,颜色等)、过关分数门槛、怎么加分数、过关金币奖励、game over触发条件、玩家速度、难度、道具价格等参数进行调整修改,这些参数在代码中均可人为改动
2.4 实验最终结果
-
代码运行视频:
备注:为了录视频,将过关分数门槛和道具价格,以及触发coin的奖励等游戏参数,调得比较夸张,
另外,登录状态下,调整为英文输入法 输入灵敏,中文状态小写字母不好输入
-
最终代码以及模块划分
import pygame
import math
import random
import json
import os
import time
ACCOUNT_FILE = "accounts.json"
def load_accounts():
if not os.path.exists(ACCOUNT_FILE):
return {}
with open(ACCOUNT_FILE, "r", encoding="utf-8") as f:
return json.load(f)
def save_accounts(data):
with open(ACCOUNT_FILE, "w", encoding="utf-8") as f:
json.dump(data, f, ensure_ascii=False, indent=2)
accounts = load_accounts()
current_user = None
total_coins = 0
SAVE_FILE = "save.json"
def load_progress():
if not os.path.exists(SAVE_FILE):
return {
"0": {"unlocked": True},
"1": {"unlocked": False},
"2": {"unlocked": False}
}
with open(SAVE_FILE, "r", encoding="utf-8") as f:
return json.load(f)
def save_progress(data):
with open(SAVE_FILE, "w", encoding="utf-8") as f:
json.dump(data, f, ensure_ascii=False, indent=2)
# 基础设置
WIDTH, HEIGHT = 1200, 600
FPS = 60
pygame.init()
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("NEON PARKOUR · LEVEL EDITION")
clock = pygame.time.Clock()
# 字体
TITLE_FONT = pygame.font.Font("C:/Windows/Fonts/msyh.ttc", 72)
UI_FONT = pygame.font.Font("C:/Windows/Fonts/msyh.ttc", 32)
SMALL_FONT = pygame.font.Font("C:/Windows/Fonts/msyh.ttc", 24)
# 颜色
BG = (8, 8, 18)
NEON_BLUE = (0, 220, 255)
NEON_PINK = (255, 80, 180)
NEON_GREEN = (0, 255, 170)
COIN_COLOR = (255, 215, 0)
UI_BG = (20, 20, 35, 180)
PLAYER_COLOR = (0, 200, 255)
SLIDE_COLOR = (0, 255, 200)
DASH_COLOR = (255, 255, 0)
#-------------------------------------------------------------------
COIN_TYPES = {
"small": {"radius": 6, "value": 1},
"normal": {"radius": 8, "value": 2},
"big": {"radius": 12, "value": 5}
}
OBSTACLE_TYPES = {
"block": {},
"tall": {"height": 100},
"short": {"height": 30},
"wide": {"width": 80},
"air": {"y": 320}, # 空中障碍
"spike": {"shape": "spike"}
}
# 不同的游戏状态
STATE_LOGIN = 0
STATE_MENU = 1
STATE_PLAYING = 2
STATE_PAUSED = 3
STATE_GAMEOVER = 4
STATE_LEVEL_CLEAR = 5
STATE_END_CHOICE = 6
game_state = STATE_LOGIN
accounts = load_accounts()
input_user = ""
input_pass = ""
input_focus = "user"
login_step = "user" # "user"输账号,"pass"输密码
account_box = pygame.Rect(520, 220, 300, 36)
password_box = pygame.Rect(520, 280, 300, 36)
login_error = ""
current_user = None
total_coins = 0
# 玩家
class Player:
def __init__(self):
self.reset_stats()
self.coins = 0
def reset_stats(self):
self.x = 140
self.base_y = 420
self.y = self.base_y
self.width = 30
self.height = 50
self.velocity_y = 0
self.gravity = 0.8
self.jump_power = -14
self.jumps_left = 2
self.is_sliding = False
self.slide_timer = 0
self.is_dashing = False
self.dash_timer = 0
self.base_speed = 6
self.speed = self.base_speed
self.magnet = False
self.shield = False
self.powerup_timers = {}
self.max_hp = 3
self.hp = self.max_hp
self.invincible_timer = 0
self.color_flash = 0
def jump(self):
max_jumps = 3
if "double" in self.powerup_timers:
max_jumps = 5
if self.jumps_left > 0 and self.jumps_left <= max_jumps:
self.velocity_y = self.jump_power
self.jumps_left -= 1
def dash(self):
if not self.is_dashing:
self.is_dashing = True
self.dash_timer = 20
self.speed = self.base_speed * 2
def slide(self):
if not self.is_sliding and self.on_ground():
self.is_sliding = True
self.slide_timer = 15
def on_ground(self):
return self.y + self.height >= 470
def update(self):
self.velocity_y += self.gravity
self.y += self.velocity_y
if self.on_ground():
self.y = 470 - self.height
self.velocity_y = 0
self.jumps_left = 2
if self.is_sliding:
self.slide_timer -= 1
if self.slide_timer <= 0:
self.is_sliding = False
if self.is_dashing:
self.dash_timer -= 1
if self.dash_timer <= 0:
self.is_dashing = False
self.speed = self.base_speed
for k in list(self.powerup_timers):
self.powerup_timers[k] -= 1
if self.powerup_timers[k] <= 0:
del self.powerup_timers[k]
self.magnet = "magnet" in self.powerup_timers
self.shield = "shield" in self.powerup_timers
if self.color_flash > 0:
self.color_flash -= 1
if self.invincible_timer > 0:
self.invincible_timer -= 1
if hasattr(self, "magnet_timer"):
self.magnet_timer -= 1
if self.magnet_timer <= 0:
self.magnet = False
if hasattr(self, "shield_timer"):
self.shield_timer -= 1
if self.shield_timer <= 0:
self.shield = False
def hurt(self):
if self.invincible_timer > 0:
return False
self.hp -= 1
self.invincible_timer = 30
self.color_flash = 10
return self.hp <= 0
@property
def rect(self):
if self.is_sliding:
return pygame.Rect(self.x, self.y + self.height // 2,
self.width, self.height // 2)
return pygame.Rect(self.x, int(self.y), self.width, self.height)
def draw(self, surf):
color = (255, 100, 100) if self.color_flash > 0 else (
DASH_COLOR if self.is_dashing else (
SLIDE_COLOR if self.is_sliding else PLAYER_COLOR))
# 身
pygame.draw.rect(surf, color, self.rect)
# 头
head_radius = 10
head_x = self.rect.centerx
head_y = self.rect.top - head_radius
pygame.draw.circle(surf, color, (head_x, head_y), head_radius)
# 眼
eye_offset = 3
pygame.draw.circle(surf, (255, 255, 255), (head_x - eye_offset, head_y - 2), 3)
pygame.draw.circle(surf, (255, 255, 255), (head_x + eye_offset, head_y - 2), 3)
def activate_powerup(self, name, duration=300):
self.powerup_timers[name] = duration
class Obstacle:
def __init__(self, x, kind="block"):
self.kind = kind
self.x = x
cfg = OBSTACLE_TYPES.get(kind, {})
self.y = cfg.get("y", 410)
self.width = cfg.get("width", random.randint(30, 50))
self.height = cfg.get("height", random.randint(40, 70))
self.shape = cfg.get("shape", "rect")
self.attack_timer = 0
def rect(self):
return pygame.Rect(int(self.x), self.y, self.width, self.height)
def update(self, speed):
self.x -= speed
def draw(self, surf):
if self.shape == "spike":
# 尖刺
points = [
(self.x, self.y + self.height),
(self.x + self.width // 2, self.y),
(self.x + self.width, self.y + self.height)
]
pygame.draw.polygon(surf, (255, 60, 60), points)
else:
pygame.draw.rect(surf, (255, 80, 80), self.rect())
#金币
class Coin:
def __init__(self, x, y=None, kind=None):
self.x = x
self.kind = kind or random.choice(list(COIN_TYPES.keys()))
cfg = COIN_TYPES[self.kind]
self.radius = cfg["radius"]
self.value = cfg["value"]
if y is not None:
self.y = y
else:
self.y = random.randint(300, 420)
def update(self, speed):
self.x -= speed
def draw(self, surf):
pygame.draw.circle(
surf,
COIN_COLOR,
(int(self.x), int(self.y)),
self.radius
)
# 道具 (初版那个摆设删了,啥用没有)
# 商店
class Shop:
def __init__(self):
self.open = False
self.items = [
{"name": "magnet", "display": "磁铁(按1使用)", "price": 1},
{"name": "shield", "display": "护盾(按2使用)", "price": 1},
{"name": "double", "display": "多次跳(按3使用)", "price": 1},
]
def toggle(self):
self.open = not self.open
def draw(self, surf, player):
if not self.open:
return
overlay = pygame.Surface((WIDTH, HEIGHT), pygame.SRCALPHA)
overlay.fill((0, 0, 0, 180))
surf.blit(overlay, (0, 0))
# 面板
panel_w, panel_h = 540, 400
panel_x = WIDTH // 2 - panel_w // 2
panel_y = HEIGHT // 2 - panel_h // 2
pygame.draw.rect(surf, (30, 30, 45), (panel_x, panel_y, panel_w, panel_h), border_radius=16)
pygame.draw.rect(surf, NEON_BLUE, (panel_x, panel_y, panel_w, panel_h), 2, border_radius=16)
# 标题
title = UI_FONT.render("商 店", True, NEON_PINK)
surf.blit(title, (panel_x + panel_w // 2 - title.get_width() // 2, panel_y + 20))
#字体(重调大小)
item_font = pygame.font.Font("C:/Windows/Fonts/msyh.ttc", 26) # 道具名
info_font = pygame.font.Font("C:/Windows/Fonts/msyh.ttc", 20) # 价格 / 库存
# 商品列表
y = panel_y + 85
item_height = 68
padding_x = 40
for item in self.items:
rect = pygame.Rect(
panel_x + padding_x,
y,
panel_w - padding_x * 2,
item_height
)
affordable = player.coins >= item["price"]
color = (255, 255, 255) if affordable else (110, 110, 110)
pygame.draw.rect(surf, (40, 40, 60), rect, border_radius=12)
pygame.draw.rect(surf, NEON_BLUE, rect, 2, border_radius=12)
# 道具名
name_surf = item_font.render(item["display"], True, color)
surf.blit(name_surf, (rect.x + 22, rect.y + 10))
# 价格
price_text = f"价格:{item['price']} 金币"
price_surf = info_font.render(price_text, True, (200, 200, 200))
surf.blit(price_surf, (rect.x + 22, rect.y + 38))
# 玩家现在有多少
count = accounts[current_user]["inventory"].get(item["name"], 0)
count_text = info_font.render(f"拥有:{count}", True, NEON_GREEN)
surf.blit(count_text, (rect.right - 120, rect.y + 22))
y += item_height + 12
# 关闭按钮
close_rect = pygame.Rect(panel_x + panel_w - 40, panel_y + 14, 26, 26)
pygame.draw.rect(surf, (255, 80, 80), close_rect, border_radius=6)
close_txt = SMALL_FONT.render("X", True, (255, 255, 255))
surf.blit(close_txt, close_txt.get_rect(center=close_rect.center))
# 背景-----------------------------------------------------
class Star:
def __init__(self):
self.x = random.randint(0, WIDTH)
self.y = random.randint(0, HEIGHT)
self.size = random.uniform(0.5, 2.5)
self.speed = random.uniform(0.2, 1.2)
self.brightness = random.randint(120, 255)
def update(self):
self.x -= self.speed
if self.x < 0:
self.x = WIDTH
self.y = random.randint(0, HEIGHT)
def draw(self, surf):
color = (self.brightness, self.brightness, self.brightness)
pygame.draw.circle(surf, color, (int(self.x), int(self.y)), int(self.size))
class HeartTree:
def __init__(self):
self.start_time = time.time()
def beat(self):
t = time.time() - self.start_time
# 模拟节拍
return 1 + 0.06 * math.sin(t * 4)
def clamp_color(self, r, g, b, a=None):
r = max(0, min(255, r))
g = max(0, min(255, g))
b = max(0, min(255, b))
if a is not None:
a = max(0, min(255, a))
return (r, g, b, a)
return (r, g, b)
def draw_branch(self, surf, x, y, length, angle, depth):
if depth == 0:
return
x2 = x + length * math.cos(angle)
y2 = y + length * math.sin(angle)
width = max(1, depth)
glow = width + 2
#
pygame.draw.line(
surf,
self.clamp_color(255, 80, 120, 60),
(int(x), int(y)),
(int(x2), int(y2)),
glow
)
pygame.draw.line(
surf,
self.clamp_color(255, 80 + depth * 15, 120),
(int(x), int(y)),
(int(x2), int(y2)),
width
)
self.draw_branch(surf, x2, y2, length * 0.75, angle - 0.45, depth - 1)
self.draw_branch(surf, x2, y2, length * 0.75, angle + 0.45, depth - 1)
def draw(self, surf):
scale = self.beat()
cx, cy = WIDTH // 2, HEIGHT - 80
length = 120 * scale
self.draw_branch(surf, cx, cy, length, -math.pi / 2, 9)
for i in range(0, 360, 2):
t = math.radians(i)
x = 16 * math.sin(t) ** 3
y = -(13 * math.cos(t) - 5 * math.cos(2 * t)
- 2 * math.cos(3 * t) - math.cos(4 * t))
pygame.draw.circle(
surf,
self.clamp_color(255, 50, 100, 200),
(int(cx + x * 6), int(cy - 140 + y * 6)),
1
)
class Petal:
def __init__(self):
self.reset()
def reset(self):
self.x = random.randint(WIDTH // 2 - 120, WIDTH // 2 + 120)
self.y = random.randint(60, 200)
self.vy = random.uniform(0.3, 0.8)
self.vx = random.uniform(-0.2, 0.2)
self.size = random.uniform(2, 4)
self.alpha = random.randint(160, 230)
self.color = random.choice([
(255, 180, 220),
(255, 140, 200),
(220, 180, 255)
])
def update(self):
self.x += self.vx
self.y += self.vy
if self.y > HEIGHT:
self.reset()
def draw(self, surf):
s = pygame.Surface((10, 10), pygame.SRCALPHA)
pygame.draw.circle(
s,
(*self.color, self.alpha),
(5, 5),
int(self.size)
)
surf.blit(s, (int(self.x), int(self.y)))
class GlowParticle:
def __init__(self):
self.x = random.randint(0, WIDTH)
self.y = random.randint(0, HEIGHT)
self.vx = random.uniform(-0.3, 0.3)
self.vy = random.uniform(-0.1, 0.1)
self.radius = random.uniform(1, 3)
self.color = random.choice([NEON_BLUE, NEON_PINK, NEON_GREEN])
def update(self):
self.x += self.vx
self.y += self.vy
if self.x < 0 or self.x > WIDTH:
self.vx *= -1
def draw(self, surf):
pygame.draw.circle(surf, (*self.color, 50),
(int(self.x), int(self.y)), int(self.radius))
# 粒子
class Particle:
def __init__(self, x, y, color):
self.x = x
self.y = y
self.vx = random.uniform(-2, 2)
self.vy = random.uniform(-2, 2)
self.life = 20
self.color = color
def update(self):
self.x += self.vx
self.y += self.vy
self.life -= 1
def alive(self):
return self.life > 0
def draw(self, surf):
pygame.draw.circle(surf, self.color, (int(self.x), int(self.y)), 3)
#=================
class Button:
def __init__(self, x, y, w, h, text):
self.rect = pygame.Rect(x, y, w, h)
self.text = text
self.scale = 1.0
self.target_scale = 1.0
self.color = NEON_PINK
self.hovered = False
def update(self, mouse_pos):
self.hovered = self.rect.collidepoint(mouse_pos)
if self.hovered:
self.target_scale = 1.12
else:
self.target_scale = 1.0
self.scale += (self.target_scale - self.scale) * 0.15
def draw(self, surf):
rect = self.rect.copy()
rect.width = int(rect.width * self.scale)
rect.height = int(rect.height * self.scale)
rect.center = self.rect.center
# hover 时颜色变亮
draw_color = self.color
if self.hovered:
draw_color = tuple(min(255, c + 40) for c in self.color) # 提亮
if self.hovered:
glow = pygame.Surface((rect.width + 20, rect.height + 20), pygame.SRCALPHA)
pygame.draw.rect(glow, (*self.color, 60),
glow.get_rect(), border_radius=16)
surf.blit(glow, glow.get_rect(center=rect.center))
pygame.draw.rect(surf, (25, 25, 40), rect, border_radius=14)
pygame.draw.rect(surf, self.color, rect, 2, border_radius=14)
txt = UI_FONT.render(self.text, True, (255, 255, 255))
surf.blit(txt, txt.get_rect(center=rect.center))
# 账号系统
ACCOUNT_FILE = "accounts.json"
def load_accounts():
if not os.path.exists(ACCOUNT_FILE):
return {}
with open(ACCOUNT_FILE, "r", encoding="utf-8") as f:
return json.load(f)
def save_accounts(data):
with open(ACCOUNT_FILE, "w", encoding="utf-8") as f:
json.dump(data, f, ensure_ascii=False, indent=2)
accounts = load_accounts()
current_user = None
total_coins = 0
# 给所有账号补 inventory
for acc in accounts.values():
if "inventory" not in acc:
acc["inventory"] = {
"magnet": 0,
"shield": 0,
"double": 0
}
# 保证 total_coins 一定存在
for acc in accounts.values():
if "total_coins" not in acc:
acc["total_coins"] = 0
save_accounts(accounts)
# 登录输入
input_user = ""
input_pass = ""
login_error = ""
# 对象
player = Player()
coins = []
powerups = []
particles = []
obstacles = []
shop = Shop()
heart_tree = HeartTree()
petals = [Petal() for _ in range(60)]
stars = [Star() for _ in range(120)]
glows = [GlowParticle() for _ in range(40)]
exit_button = Button(WIDTH // 2, 0, 240, 60, "退出游戏")
back_button = Button(WIDTH // 2 - 120, 520, 240, 60, "返回主菜单")
resume_button = Button(WIDTH // 2 - 120, 460, 240, 60, "继续游戏")
continue_button = Button(WIDTH // 2 - 120, 370, 240, 60, "继续挑战")
end_menu_button = Button(WIDTH // 2 - 140, 340, 260, 60, "返回主菜单")
spawn_timer = 0
score = 0
level = 1
LEVEL_TARGET = 200
LEVELS = {
0: {
"name": "新手关",
"speed": 6,
"types": ["block"],
"spawn_rate": 90
},
1: {
"name": "进阶关",
"speed": 8,
"types": ["block", "short", "wide"],
"spawn_rate": 70
},
2: {
"name": "高手关",
"speed": 10,
"types": ["block", "tall", "air", "spike"],
"spawn_rate": 50
}
}
progress = load_progress()
selected_level = 0
current_level = 0
hovered_level = None
# UI
def draw_hud(surf, player, score, level):
panel = pygame.Surface((340, 140), pygame.SRCALPHA)
panel.fill(UI_BG)
surf.blit(panel, (20, 20))
surf.blit(UI_FONT.render(f"LEVEL {level}", True, NEON_PINK), (35, 30))
surf.blit(UI_FONT.render(f"Score {score}", True, NEON_BLUE), (35, 65))
surf.blit(UI_FONT.render(f"Coins {player.coins}", True, COIN_COLOR), (35, 100))
draw_inventory(screen)
def draw_controls(surf):
hints = [
"[按空格键]宝贝,跳!",
"[Shift]加速Dash",
"[Ctrl] Slide",
"[P] 暂停Pause",
"[G] 商店Shop",
"[R] 重开Restart"
]
y = HEIGHT - 160
for h in hints:
surf.blit(SMALL_FONT.render(h, True, (160, 160, 160)), (WIDTH - 220, y))
y += 24
def is_unlocked(lv_id):
return progress.get(str(lv_id), {}).get("unlocked", False)
def draw_login(surf):
surf.fill(BG)
title = TITLE_FONT.render("登录 / 注册", True, NEON_PINK)
surf.blit(title, (WIDTH // 2 - title.get_width() // 2, 120))
# 账号
surf.blit(UI_FONT.render("账号:", True, NEON_BLUE), (400, 220))
cursor = "_" if int(time.time() * 2) % 2 == 0 else " "
if input_focus == "user":
surf.blit(UI_FONT.render(input_user + cursor, True, (255, 255, 255)), (520, 220))
else:
surf.blit(UI_FONT.render(input_user, True, (180, 180, 180)), (520, 220))
# 密码
surf.blit(UI_FONT.render("密码:", True, NEON_BLUE), (400, 280))
if input_focus == "pass":
surf.blit(UI_FONT.render("*" * len(input_pass) + cursor, True, (255, 255, 255)), (520, 280))
else:
surf.blit(UI_FONT.render("*" * len(input_pass), True, (180, 180, 180)), (520, 280))
if login_error:
err = SMALL_FONT.render(login_error, True, (255, 80, 80))
surf.blit(err, (WIDTH // 2 - err.get_width() // 2, 350))
step_text = "请输入账号" if login_step == "user" else "请输入密码"
surf.blit(SMALL_FONT.render(step_text, True, NEON_GREEN), (WIDTH // 2 - 80, 380))
tip = SMALL_FONT.render("Enter 登录 / 不存在则自动注册", True, (160, 160, 160))
surf.blit(tip, (WIDTH // 2 - tip.get_width() // 2, 420))
def draw_level_select(surf):
global hovered_level
overlay = pygame.Surface((WIDTH, HEIGHT), pygame.SRCALPHA)
overlay.fill((8, 8, 18, 180))
surf.blit(overlay, (0, 0))
title = TITLE_FONT.render(" 欢迎你,我的宝贝 (坏笑)", True, NEON_PINK)
sub = SMALL_FONT.render("单机跑酷闯关游戏(Enter或鼠标点击均可进入)", True, NEON_BLUE)
breath = 1 + 0.008 * math.sin(time.time() * 3)
tw = int(title.get_width() * breath)
th = int(title.get_height() * breath)
title_scaled = pygame.transform.smoothscale(title, (tw, th))
surf.blit(title_scaled, (WIDTH // 2 - tw // 2, 30)) # 稍微往下一点
surf.blit(sub, (WIDTH // 2 - sub.get_width() // 2, 120))
y = 200
item_spacing = 80
for i in LEVELS:
unlocked = is_unlocked(i)
selected = (i == selected_level)
if not unlocked:
color = (100, 60, 60)
text = f"{i + 1}. {LEVELS[i]['name']} (LOCKED)"
else:
color = (255, 255, 255) if selected else (180, 180, 180)
text = f"{i + 1}. {LEVELS[i]['name']}"
txt = UI_FONT.render(text, True, color)
text_x = WIDTH // 2 - txt.get_width() // 2
if selected and unlocked:
box_w = txt.get_width() + 44
box_h = 52
box_x = WIDTH // 2 - box_w // 2
box_y = y - 6
glow = pygame.Surface((box_w + 24, box_h + 24), pygame.SRCALPHA)
pygame.draw.rect(glow, (*NEON_BLUE, 70), glow.get_rect(), border_radius=12)
surf.blit(glow, (box_x - 12, box_y - 12))
pygame.draw.rect(surf, (20, 20, 40, 180), (box_x, box_y, box_w, box_h), border_radius=10)
pygame.draw.rect(surf, NEON_BLUE, (box_x, box_y, box_w, box_h), 2, border_radius=10)
surf.blit(txt, (text_x, y))
y += item_spacing
exit_button.rect.centerx = WIDTH // 2
exit_button.rect.top = y + 40
btn_rect = exit_button.rect
draw_color = exit_button.color
if exit_button.hovered:
draw_color = tuple(min(255, c + 40) for c in exit_button.color)
if exit_button.hovered:
glow = pygame.Surface((btn_rect.width + 20, btn_rect.height + 20), pygame.SRCALPHA)
pygame.draw.rect(glow, (*exit_button.color, 60), glow.get_rect(), border_radius=16)
surf.blit(glow, glow.get_rect(center=btn_rect.center))
pygame.draw.rect(surf, (25, 25, 40), btn_rect, border_radius=14)
pygame.draw.rect(surf, draw_color, btn_rect, 2, border_radius=14)
txt_surf = UI_FONT.render(exit_button.text, True, (255, 255, 255))
surf.blit(txt_surf, txt_surf.get_rect(center=btn_rect.center))
if game_state == STATE_MENU:
tip_text = "ESC 退出游戏"
else:
tip_text = "ESC 返回主菜单"
tip = SMALL_FONT.render(tip_text, True, (140, 140, 160))
tip.set_alpha(180)
surf.blit(tip, (WIDTH // 2 - tip.get_width() // 2, HEIGHT - 50)) # 固定在底部
def draw_pause(surf, back_button):
overlay = pygame.Surface((WIDTH, HEIGHT), pygame.SRCALPHA)
overlay.fill((0, 0, 0, 160))
surf.blit(overlay, (0, 0))
text = TITLE_FONT.render("PAUSED", True, NEON_PINK)
surf.blit(text, (WIDTH // 2 - text.get_width() // 2, 40))
hint_title = UI_FONT.render("给我的宝宝的小提示:", True, NEON_BLUE)
alpha = 180 + 40 * math.sin(time.time() * 3)
hint_title.set_alpha(int(alpha))
surf.blit(hint_title, (WIDTH // 2 - hint_title.get_width() // 2, 130))
# =========================
tips = [
("ESC", "返回主菜单"),
("空格键", "跳跃!"),
("Shift", "加速 Dash"),
("Ctrl", "滑铲"),
("G","商店可以买东西哦")
]
start_y = 200
line_height = 48
for key, desc in tips:
key_text = UI_FONT.render(key, True, NEON_GREEN)
desc_text = UI_FONT.render(desc, True, (220, 220, 220))
# 右对齐排列
surf.blit(key_text, (WIDTH // 2 - 120, start_y))
surf.blit(desc_text, (WIDTH // 2 + 20, start_y))
start_y += line_height
back_button.rect.centerx = WIDTH // 2
back_button.rect.top = start_y + 30
back_button.draw(surf)
resume_button.rect.centerx = WIDTH // 2
resume_button.rect.top = start_y + 100
resume_button.draw(surf)
shop.draw(surf, player)
def draw_gameover(surf, score, coins, back_button):
overlay = pygame.Surface((WIDTH, HEIGHT), pygame.SRCALPHA)
overlay.fill((0, 0, 0, 200))
surf.blit(overlay, (0, 0))
surf.blit(TITLE_FONT.render("GAME OVER", True, (255, 80, 80)),
(WIDTH // 2 - 210, 180))
surf.blit(UI_FONT.render(f"Score: {score}", True, NEON_BLUE),
(WIDTH // 2 - 90, 280))
surf.blit(UI_FONT.render(f"Coins: {coins}", True, COIN_COLOR),
(WIDTH // 2 - 90, 330))
back_button.draw(surf)
def draw_level_clear(surf, level):
overlay = pygame.Surface((WIDTH, HEIGHT), pygame.SRCALPHA)
overlay.fill((0, 0, 0, 180))
surf.blit(overlay, (0, 0))
# 文字动画
global level_clear_scale
level_clear_scale += (1.2 - level_clear_scale) * 0.08
scale = int(64 * level_clear_scale) # 稍小一点
center_x = WIDTH // 2
start_y = 160 # 整体下移
# 标题
title_font = pygame.font.Font("C:/Windows/Fonts/msyh.ttc", scale)
title = title_font.render("关卡通关!", True, NEON_GREEN)
surf.blit(title, (center_x - title.get_width() // 2, start_y))
# 解锁提示
sub_font = pygame.font.Font("C:/Windows/Fonts/msyh.ttc", 28)
if level >= 2: # 第 3 关及以后
sub = sub_font.render("还要继续挑战吗?", True, NEON_BLUE)
else:
sub = sub_font.render(f"已解锁第 {level + 2} 关", True, NEON_BLUE)
surf.blit(sub, (center_x - sub.get_width() // 2, start_y + 110))
# 过一关给的金币奖励
reward_font = pygame.font.Font("C:/Windows/Fonts/msyh.ttc", 26)
reward_text = reward_font.render(
f"获得金币:{level_clear_reward}", True, COIN_COLOR
)
surf.blit(reward_text, (center_x - reward_text.get_width() // 2, start_y + 195))
# ===== 粒子 =====
for p in clear_particles:
pygame.draw.circle(surf, p["color"], (int(p["x"]), int(p["y"])), 4)
# 继续按钮 =====
continue_button.rect.centerx = center_x
continue_button.rect.top = start_y + 265
if level >= 2:
continue_button.text = "继续无限闯关"
else:
continue_button.text = "继续挑战"
continue_button.draw(surf)
def draw_end_choice(surf):
overlay = pygame.Surface((WIDTH, HEIGHT), pygame.SRCALPHA)
overlay.fill((0, 0, 0, 200))
surf.blit(overlay, (0, 0))
# 标题
title = TITLE_FONT.render("恭喜通关!", True, NEON_GREEN)
surf.blit(title, (WIDTH // 2 - title.get_width() // 2, 160))
# 提示
hint = UI_FONT.render("接下来你想无限闯关吗?", True, NEON_BLUE)
surf.blit(hint, (WIDTH // 2 - hint.get_width() // 2, 240))
def spawn_clear_particles():
global clear_particles
clear_particles = []
for _ in range(120):
clear_particles.append({
"x": WIDTH // 2,
"y": HEIGHT // 2,
"vx": random.uniform(-6, 6),
"vy": random.uniform(-6, 6),
"color": random.choice([NEON_GREEN, NEON_BLUE, NEON_PINK])
})
def draw_inventory(surf):
if not current_user:
return
inv = accounts[current_user].get("inventory")
if not inv:
return
x = WIDTH - 200
y = 20
for name, count in inv.items():
text = f"{name}: {count}"
surf.blit(SMALL_FONT.render(text, True, NEON_GREEN), (x, y))
y += 26
# 主循环
level_clear_scale = 1.0
level_clear_timer = 120
level_clear_reward = 0
running = True
while running:
clock.tick(FPS)
screen.fill(BG)
mouse_pos = pygame.mouse.get_pos()
for event in pygame.event.get():
# 退出 ===
if event.type == pygame.QUIT:
running = False
# 键盘 ===
if event.type == pygame.KEYDOWN:
# -------- 登录 --------
if game_state == STATE_LOGIN:
if event.key == pygame.K_TAB:
input_focus = "pass" if input_focus == "user" else "user"
elif event.key == pygame.K_BACKSPACE:
if input_focus == "user":
input_user = input_user[:-1]
else:
input_pass = input_pass[:-1]
elif event.key == pygame.K_RETURN:
if login_step == "user":
if input_user.strip() == "":
login_error = "账号不能为空"
else:
login_step = "pass"
input_focus = "pass"
login_error = ""
else:
if input_user not in accounts:
accounts[input_user] = {
"password": input_pass,
"total_coins": 0,
"inventory": {
"magnet": 0,
"shield": 0,
"double": 0
}
}
save_accounts(accounts)
if accounts[input_user]["password"] != input_pass:
login_error = "密码错误"
else:
current_user = input_user
player.coins = accounts[current_user]["total_coins"]
game_state = STATE_MENU
login_error = ""
else:
if event.unicode:
if input_focus == "user":
input_user += event.unicode
else:
input_pass += event.unicode
# -------- 菜单 --------
elif game_state == STATE_MENU:
if event.key == pygame.K_UP:
selected_level = max(0, selected_level - 1)
if event.key == pygame.K_DOWN:
selected_level = min(len(LEVELS) - 1, selected_level + 1)
if event.key == pygame.K_RETURN and is_unlocked(str(selected_level)):
current_level = selected_level
player.reset_stats()
coins.clear()
powerups.clear()
obstacles.clear()
particles.clear()
score = 0
spawn_timer = 0
game_state = STATE_PLAYING
if event.key == pygame.K_ESCAPE:
running = False
# -------- 游戏中 --------
elif game_state == STATE_PLAYING:
if event.key == pygame.K_SPACE:
player.jump()
if event.key == pygame.K_LSHIFT:
player.dash()
if event.key == pygame.K_LCTRL:
player.slide()
if event.key == pygame.K_p:
game_state = STATE_PAUSED
if event.key == pygame.K_g:
shop.toggle()
if event.key == pygame.K_ESCAPE:
game_state = STATE_PAUSED
# --------使用道具 --------
# 磁铁
if event.key == pygame.K_1:
if accounts[current_user]["inventory"]["magnet"] > 0:
accounts[current_user]["inventory"]["magnet"] -= 1
player.activate_powerup("magnet", 300)
# 护盾
if event.key == pygame.K_2:
if accounts[current_user]["inventory"]["shield"] > 0:
accounts[current_user]["inventory"]["shield"] -= 1
player.activate_powerup("shield", 300)
# 多次跳
if event.key == pygame.K_3:
if accounts[current_user]["inventory"]["double"] > 0:
accounts[current_user]["inventory"]["double"] -= 1
player.activate_powerup("double", 300)
# -------- 暂停 --------
elif game_state == STATE_PAUSED:
if event.key == pygame.K_p:
game_state = STATE_PLAYING
if event.key == pygame.K_g:
shop.toggle()
# -------- Game over --------
elif game_state == STATE_GAMEOVER:
if current_user:
accounts[current_user]["total_coins"] = player.coins
save_accounts(accounts)
if event.key == pygame.K_r:
player.reset_stats()
coins.clear()
powerups.clear()
obstacles.clear()
particles.clear()
score = 0
spawn_timer = 0
game_state = STATE_MENU
# 鼠标 -------------------------
if event.type == pygame.MOUSEBUTTONDOWN:
mx, my = event.pos
if game_state == STATE_LOGIN:
if account_box.collidepoint(mx, my):
login_step = "user"
input_focus = "user"
elif password_box.collidepoint(mx, my):
login_step = "pass"
input_focus = "pass"
if shop.open:
panel_w, panel_h = 540, 400
panel_x = WIDTH // 2 - panel_w // 2
panel_y = HEIGHT // 2 - panel_h // 2
y = panel_y + 85
item_height = 68
padding_x = 40
for item in shop.items:
rect = pygame.Rect(
panel_x + padding_x,
y,
panel_w - padding_x * 2,
item_height
)
if rect.collidepoint(mx, my):
if player.coins >= item["price"]:
player.coins -= item["price"]
accounts[current_user]["inventory"][item["name"]] += 1
save_accounts(accounts)
y += item_height + 12
# 关闭按钮
close_rect = pygame.Rect(panel_x + panel_w - 40, panel_y + 14, 26, 26)
if close_rect.collidepoint(mx, my):
shop.open = False
if game_state == STATE_MENU:
if exit_button.rect.collidepoint((mx, my)):
running = False
y = 220
for i in LEVELS:
rect = pygame.Rect(WIDTH // 2 - 120, y, 240, 50)
if rect.collidepoint(mx, my) and is_unlocked(i):
selected_level = i
current_level = i
player.reset_stats()
coins.clear()
powerups.clear()
obstacles.clear()
particles.clear()
score = 0
spawn_timer = 0
game_state = STATE_PLAYING
y += 70
if game_state == STATE_GAMEOVER:
if back_button.rect.collidepoint(event.pos):
player.reset_stats()
coins.clear()
powerups.clear()
obstacles.clear()
particles.clear()
score = 0
spawn_timer = 0
game_state = STATE_MENU
if game_state == STATE_LEVEL_CLEAR:
if continue_button.rect.collidepoint(mx, my):
current_level += 1
player.reset_stats()
coins.clear()
obstacles.clear()
particles.clear()
clear_particles.clear()
score = 0
spawn_timer = 0
game_state = STATE_PLAYING
if game_state == STATE_PAUSED:
if back_button.rect.collidepoint(event.pos):
game_state = STATE_MENU
if resume_button.rect.collidepoint(event.pos):
game_state = STATE_PLAYING
if game_state == STATE_END_CHOICE:
if end_menu_button.rect.collidepoint(mx, my):
game_state = STATE_MENU
player.reset_stats()
coins.clear()
obstacles.clear()
particles.clear()
score = 0
#更新
if game_state == STATE_MENU:
exit_button.update(mouse_pos)
if game_state == STATE_PAUSED:
back_button.update(mouse_pos)
elif game_state == STATE_GAMEOVER:
back_button.update(mouse_pos)
if game_state == STATE_LEVEL_CLEAR:
level_clear_timer -= 1
for p in clear_particles:
p["x"] += p["vx"]
p["y"] += p["vy"]
if game_state == STATE_PLAYING:
player.update()
player.speed = player.base_speed + current_level
for star in stars:
star.update()
for glow in glows:
glow.update()
level_cfg = LEVELS[min(current_level, 2)]
spawn_timer += 1
if spawn_timer > level_cfg["spawn_rate"]:
# 障碍物
kind = random.choice(level_cfg["types"])
obstacles.append(Obstacle(WIDTH + 80, kind))
# 金币类型
coin_kind = random.choice(list(COIN_TYPES.keys()))
if random.random() < 0.2:
# 空中金币
coins.append(Coin(WIDTH + random.randint(60, 160), 300, coin_kind))
else:
# 地面金币
coins.append(Coin(WIDTH + random.randint(60, 160), None, coin_kind))
spawn_timer = 0
for c in coins[:]:
# 磁铁吸附
if player.magnet:
dx = player.rect.centerx - c.x
dy = player.rect.centery - c.y
dist = math.hypot(dx, dy)
if dist != 0:
c.x += dx / dist * 6
coin_rect = pygame.Rect(
int(c.x) - c.radius,
c.y - c.radius,
c.radius * 2,
c.radius * 2
)
if coin_rect.colliderect(player.rect):
coins.remove(c)
player.coins += c.value
score += 20 * c.value
particles.append(Particle(c.x, c.y, COIN_COLOR))
c.update(player.speed)
for o in obstacles:
o.update(player.speed)
for o in obstacles[:]:
if o.rect().colliderect(player.rect):
if player.shield:
obstacles.remove(o)
player.shield = False
elif player.hurt():
game_state = STATE_GAMEOVER
for p in particles[:]:
p.update()
if not p.alive():
particles.remove(p)
if score >= (current_level + 1) * LEVEL_TARGET:
if current_level == 2: # 高手关
game_state = STATE_END_CHOICE
level_clear_scale = 1.0
spawn_clear_particles()
else:
next_level = current_level + 1
if str(next_level) not in progress:
progress[str(next_level)] = {"unlocked": True}
save_progress(progress)
if str(next_level) not in progress:
progress[str(next_level)] = {"unlocked": True}
save_progress(progress)
reward = (current_level + 1) * 50
player.coins += reward
level_clear_reward = reward
game_state = STATE_LEVEL_CLEAR
level_clear_timer = 300
level_clear_scale = 1.0
spawn_clear_particles()
# 绘制
if game_state == STATE_LOGIN:
draw_login(screen)
if game_state == STATE_MENU:
screen.fill(BG)
for p in petals:
p.update()
p.draw(screen)
heart_tree.draw(screen)
draw_level_select(screen)
exit_button.draw(screen)
elif game_state == STATE_PLAYING:
for star in stars:
star.draw(screen)
for glow in glows:
glow.draw(screen)
player.draw(screen)
#护盾特效
if player.shield:
pygame.draw.rect(
screen,
(255, 80, 80),
player.rect.inflate(12, 12),
3
)
# 磁铁特效
if player.magnet:
pygame.draw.circle(
screen,
(0, 255, 255, 80),
player.rect.center,
130,
2
)
for o in obstacles:
pygame.draw.rect(screen, (255, 80, 80), o.rect())
for c in coins:
c.draw(screen)
for p in particles:
p.draw(screen)
pygame.draw.line(screen, (80, 80, 80), (0, 470), (WIDTH, 470), 4)
pygame.draw.line(screen, NEON_BLUE, (0, 470), (WIDTH, 470), 2)
pygame.draw.line(screen, NEON_PINK, (0, 472), (WIDTH, 472), 1)
draw_hud(screen, player, score, current_level + 1)
draw_controls(screen)
shop.draw(screen, player)
elif game_state == STATE_PAUSED:
draw_pause(screen, back_button)
elif game_state == STATE_END_CHOICE:
draw_end_choice(screen)
end_menu_button.draw(screen)
elif game_state == STATE_GAMEOVER:
draw_gameover(screen, score, player.coins, back_button)
elif game_state == STATE_LEVEL_CLEAR:
draw_level_clear(screen, current_level)
pygame.display.flip()
pygame.quit()
3. 实验过程中遇到的问题和解决过程
问题1:在 Windows 中文输入法下,登录时键盘输入,小写字母经常不触发或触发延迟;另外,代码报错
- 解决方案:特别注意中英文状态,就像双引号、括号,中文状态输入会报错,得切换英文状态
问题2: 输完代码运行好几次报错,尤其是涉及长代码
- 解决方案:注意代码缩进细节和代码放的位置!
问题3:shop界面和道具,最开始就是纯摆设
- 解决方案:借助大模型的帮助,不断改代码,同时要在界面上有交互提示以及道具要真正体现功能
问题4:字体问题导致报错或者运行显示一堆方框(界面看上去反正是方框)
- 解决方案:找系统C盘自己的字体

其他(感悟、思考等)
(一)关于这次综合实践的一些感想
这次综合实践,有很多切身感受。所以在全课总结之前,先写一下这次实验的一些感悟。
最主要的就是大模型运用的问题。
前三次实验总有一个实验环节,利用大模型生成实验相关代码,并与自写代码比较优缺。而我在前三次实验过程中,都是把自写代码给AI,让它一次性根据要求生成代码,我觉得这一过程反而是前三次实验最省时的环节,因为大模型几秒钟出结果,我直接拿过来运行然后截图即可。这次综合实践同样借助了大模型,但是和前三次实验截然不同。前三次实验每次都有自写代码作为依托,且我只需要对话一次就得到我所需要的。这次综合实践没有了现成的依托,需要把详细的目的指令告诉AI,指令要尽量细致、全面。例如,我第一次只是简单告诉它,帮我生成一款python小游戏代码,结果生成的根本不是我想要的。此外,只有大模型的话,想要一次性就生成超长代码,还是比较困难的,大模型最多给一个框架。
在与大模型对话过程中也出现了一些问题。大模型生成的代码运行可能存在问题、对话不到位可能导致代码覆盖之前的功能,越改越乱、还有其他我在上面“2.3实验过程”提到的那些问题。我想说,AI 遇强则强,一定要把想法细致明确的告诉他,同时还是要具备一些基本知识,不然想依赖大模型都不是很顺利。
此外就是在初版代码框架基础上,不断补充、修改、完善的过程,包括将我自己的想法告诉大模型,包括自己调整一些参数,这个过程同样收获很多。
(二)全课总结、感想体会、意见建议
1.全课总结与感想
学到了啥
-
ch1.第一节课对python初步认识了解
a.知道python 是什么?跨平台、解释型语言、动态数据类型、高级程序语言。 与C语言区别:C语言是编译型语言,python解释型语言(老师举例:蛋炒饭、盖浇饭)
b.python3.x 支持中文字符串
c.python开发环境:自带IDLE、第三方工具 如pycharm、VScode,并完成python开发环境的搭建
d.了解python的一些应用场景:Web 开发、大数据处理、人工智能、爬虫、游戏开发等
e.博客园、Gitee相关技能,包括pycharm上面怎么接入自己的Gitee -
ch2.接着学习了python一些基本语法
a. python与C语言、Java的注释的区别,代码缩进(!!!),编码规范,注意与C语言的区别
b. 标识符、变量、常量
c. 基本数据类型:数字类型、字符串类型、布尔类型、数据类型转换(这部分类比C语言)
e. 运算符:算术运算符、赋值运算符、比较运算符、逻辑运算符、位运算符(单目运算符 ~ 、+ 、- 取反、正号和负号;算术运算符 * 、/ 、% 、// 乘、除、求余;位运算符 << 、>> 左移、右移 ,& 按位与, ^ 按位异或, | 按位或;比较运算符 < 、<= 、> 、>= 、!= 、==), 注意优先级!
f. python的输入输出,input、print,跟着老师在课上敲基础代码 -
ch3.流程控制语句
顺序、分支、循环结构
学习各种语句,选择语句、循环语句、空语句
了解条件表达式,如三目操作符 条件 ? 表达式1 : 表达式2 -
通过学习ch1-ch3的知识,编写猜数字、剪刀石头布小游戏,基于猜数字游戏学会重要的断点调试技能(例如,通过调试提前知道系统随机生成的数字),学会打包.exe
-
ch4~ch5.序列、字符串
a.什么是序列 ,列表 + 元组 + 集合 +字典
b.列表list1 = [1,2,3,4,5] 元组tuple1=(1,2,3,4,5)字典dictionary1 = dict(zip(list1 , tuple1))={1: 1, 2: 2, 3: 3, 4: 4, 5:5} 集合set1 ={1,2,3,4,5} = {1,1,1,1,2,3,4,5}
c.学习序列的特性。索引,序列中每个元素的编号;切片,切片操作是访问序列中元素的另外一种方法,可以访问一定范围内的元素。
e.序列相加,用+把两个序列合并,相同的元素不会被去掉;序列乘法,乘以n相当于n个序列相加;集合不能相加,不能相乘;
f.学习各数据结构对应的创建、删除操作,对里面元素的增、删、查、改操作,推导式,四者的区别等。
g.学习关于字符串的相关知识、操作和处理方法,在字符串的基础上学习了正则表达式,以及一些查找、替换等操作的方法,正则表达式的字符,模式匹配等等 -
ch6~7.函数、面向对象、模块
a.区别函数的实参、形参,了解函数传参功能
b.对象:某一个具体的事物、实体或事例;类:具有相同属性和行为的一些对象的统称或集合;类的实例化:把(类)变成(对象)的过程。
c. 包和模块:模块就是一个.py文件,包就是一个装着多个.py文件的文件夹
d. 面向对象三要素:封装、继承、多态
e. 类的方法和函数的区别:
def print_name(student):#普通函数:独立存在,想传啥参数自己定
print(student.name)
class Student:#类的方法:属于Student类,第一个参数是self
def __init__(self, name):
self.name = name
def print_name(self): # 自带self,不用额外传student
print(self.name)
stu = Student("张三") #调用区别:
print_name(stu) # 函数要手动传对象
stu.print_name() # 方法直接用对象调,self自动传
f. 学习了解__init()__是什么:Python当中类的构造方法,初始化方法;有啥用:给新创建的对象初始化属性。
例如:
class Student:
def __init__(self, name, score): # 初始化姓名和成绩
self.name = name
self.score = score
创建对象时自动执行 __init__
stu = Student("张三", 100)
print(stu.name) # 张三
-
基于前面的知识,结合后面的文件操作,编写图书管理系统程序、进行设计计算器实验
-
ch8.异常处理调试、文件操作、数据库
a.处理异常的四种方式:try-except;try-except-else;try-except-finally; raise
b.学习了解冯诺依曼的体系架构;理解了内存与外存的区别
c.学习了文件的操作,如open、write、seek、close等等,实现文本及二进制文件的系列操作
d.了解结构化数据和非结构化数据;MySQL;SQLite;
f.数据库操作4个基本操作命令:增删改查 -
ch9.爬虫、socket通信等对我来说已经比较高阶一点的知识
a.了解啥是个爬虫;还有反爬虫;不要随便乱爬虫!
b.初步了解了爬虫的基本原理与工作流程(爬取 + 解析)。常见的爬取类库,例如:requests;httpx;。常见的解析模块,例如,BeautifulSoup; Selenium
c.反爬虫要使用一个Headers; robots.txt 告诉爬虫,哪些页面能爬,哪些不能爬。
···
-
还有就是socket实验,改IP,双人加密通信,并尝试文件读写。
-
ch last.其他
撰写实验报告、Gitee技能
大模型的合理运用
还有在实验2和实验3中学习使用华为云服务器
英语打卡
感想
这一学期的Python课程结束了,发觉时间过得真快啊。王老师上课氛围很活跃,讲解到位,也比较幽默,和同学们互动得特别好,解答耐心。上完这一学期的课,我对于编程方面的知识又增加了,对于python的基本知识也有了系统了解(尽管后期确实有些跟不上了),而且在课程中也掌握了很多其他方面的东西。比如,之前没有接触过的Gitee,博客园,还有用的不是很熟的华为云服务器。在学习过程中也克服了很多困难,比如刚开始,Gitee始终接不上,实验三那些改IP、加密解密不太会等,通过请教老师、学长或是借助大模型,这些过程我认为是更加珍贵的。
总之,感恩相遇blackwall!
2.意见和建议
(1)课前签到改为课中不定时签到,增加签退(坏笑)
(2)我感觉老师讲的有点快,尤其到了后面的实验三,还有爬虫等等,有点吸收不了,可能是我编程基础一般,再加上课时有限的问题。我觉得学习通的那些各种视频资料可以选取精华当成平时任务,布置成任务点,或者大家有额外学习或实践,都可以加分,(主要鼓励大家根据自己水平去学,跟上课程)
(3)考虑增加小组合作类型的作业或者实践
(4)可以考虑在期末增加一个综合项目展示环节(不强制答辩,可自愿报名),让大家有机会展示自己或小组的作品,增强成就感和学习动力

浙公网安备 33010602011771号