20254103 实验四《Python程序设计》实验报告
20254103 2025-2026-2 《Python程序设计》实验4报告
课程:《Python程序设计》
班级: 2541
姓名: 尉瀚文
学号:20254103
实验教师:王志强
实验日期:2026年5月26日
必修/选修: 专选课
一、实验内容
Python综合应用:爬虫、数据处理、可视化、机器学习、神经网络、游戏、网络安全等。
例如:编写从社交网络爬取数据,实现可视化舆情监控或者情感分析。
例如:利用公开数据集,开展图像分类、恶意软件检测等
例如:利用Python库,基于OCR技术实现自动化提取图片中数据,并填入excel中。
例如:爬取天气数据,实现自动化微信提醒
例如:利用爬虫,实现自动化下载网站视频、文件等。
例如:编写小游戏:坦克大战、贪吃蛇、扫雷等等
注:在Windows/Linux系统上使用VIM、PDB、IDLE、Pycharm等工具编程实现。
要求:
(1)程序能运行,功能丰富(至少5个功能)。(需求提交源代码,并建议录制程序运行的视频)
(2)综合实践报告,要体现实验分析、设计、实现过程、结果等信息,格式规范,逻辑清晰,结构合理。
(3)在实践报告中,需要对全课进行总结,并写课程感想体会、意见和建议等。
二、实验分析及设计
1、实验分析
在最初选择要做的综合实验时,作为一个资深玩家(doge),小游戏成为了我的“无脑之选”,但在具体选择游戏时我却犯了难。其实我一开始纠结过该选什么类型的游戏,也有了不切实际的遐想,但考虑到pygame安装时容易出现环境问题,而turtle作为Python自带的标准库,不用额外配置,直接就能运行,对我这种小白更友好。“接球大作战”属于经典的休闲小游戏,规则简单好上手,同时又有很大的功能扩展空间,可以加道具、加关卡、加技能......可玩性很强,也能切实锻炼我的代码逻辑,所以最终选了它作为我的综合实践对象。
2、实验设计
该游戏需要玩家通过键盘方向键或者鼠标来控制屏幕底部的挡板,接住从顶部随机掉落的球体来获得分数,同时要注意躲开红色炸弹球。游戏设置了简单、普通、困难三档难度,不同难度的初始生命值、球体下落速度、挡板长度和初始障碍物数量都不一样。
(1)得分规则:接到普通白球加10分,金色金币球加30分,连续接住球会触发连击倍数,得分翻倍;接到红色炸弹球会扣1条命,还会触发屏幕震动效果;每轮重置球体时按概率随机生成。
(2)胜负规则:得分到200分就算通关胜利;生命值扣光就游戏结束,点击屏幕可以重新开始。
(3)道具系统:会随机从顶部掉落共7种道具,有增益效果也有减益效果。增益道具有挡板变长、球速变慢、加一条命、奖励额外50分;减益道具有挡板变短、球速变快、额外扣除30分。道具效果都是限时的,会定时随机从顶部掉落,接住后触发对应时长的效果。
(4)关卡升级:每攒够50分自动升一级,背景颜色会变深,障碍物也会变多,球体下落的速度也会变快,难度慢慢往上增长。
(5)清屏技能:按空格键可以释放技能,把场上所有障碍物都清掉,还能让球短暂减速,技能有20秒冷却,左下角会显示冷却时间。
(6)多球分裂:每成功接住5个球,就会多分裂出来一个新球,场上最多同时有3个球,越玩越紧张,提升游戏的操作上限。
(7)连击得分:连续成功接球会提升连击倍数,得分按倍数翻倍计算;漏球或接到炸弹则重置连击,鼓励玩家保持稳定的操作精准度。
(8)动态障碍物:灰色障碍物可水平自主移动,碰到左右边界自动反弹;球体撞击障碍物会按碰撞轴反向弹开,增加游戏路径变数与操作难度。
(9)多操作方式:同时支持键盘方向键和鼠标移动控制挡板,玩家可自由选择习惯的操作方式;挡板具备边界限制,不会移出游戏窗口。
(10)接球闪光:接住球的瞬间挡板会闪变为黄色,快速恢复原色,提供明确的接球成功反馈。
(11)动态胜利动画:达成胜利条件后,结算文字颜色循环切换彩虹色,呈现庆祝动画。
三、实验过程及结果
(一)、实验过程
1、库导入模块
导入turtle作为游戏核心图形引擎,承担窗口创建、图形绘制、事件监听与动画控制全部功能;导入random标准库提供随机数能力,支撑随机位置、速度、道具、特殊球等随机性玩法,二者均为Python内置库,无需额外安装。

2、游戏窗口初始化模块
创建600×600固定尺寸的游戏主窗口,设置标题与黑色初始背景;核心优化是调用tracer(0)关闭自动重绘,改用手动帧刷新模式,从根源避免多元素同时移动时的画面闪烁,大幅提升游戏流畅度。

3、游戏核心状态变量定义
(1)定义得分、生命、连击倍率、等级、游戏状态、难度六个核心维度变量,通过game_state字符串枚举实现开始、进行中、失败、胜利四态状态机,管控全游戏的流程分支。

(2)效果计时器变量
采用帧计数机制管控所有时效类效果,分别对应挡板伸缩、小球变速、屏幕震动、技能冷却的剩余持续时长,每帧递减,归零后效果自动恢复,无需额外计时模块。

(3)游戏平衡基础参数
存放游戏核心平衡基准值,包含小球基础下落速度、全局速度系数、挡板基础长度、接球计数、最大球数,所有动态难度、道具效果均基于这些参数缩放,保证数值体系统一。

(4)实体管理列表
用三个列表分别存储障碍物、道具、小球三类动态实体,统一管理场景元素的增删、遍历更新与批量清理,是游戏实体调度的核心数据结构。

(5)视觉配置常量
预定义两套配色方案,彩虹色列表用于胜利界面的闪烁庆祝动画,关卡背景色列表对应不同等级的背景切换,通过视觉变化直观体现游戏进度。

4、玩家挡板实体初始化
创建玩家操控的挡板对象,基于基础方形横向拉伸为长条形状,抬笔避免移动留痕,初始固定在窗口底部中央,设置最快绘制速度保证操作响应无延迟。

5、小球创建函数与初始小球实例
(1)小球创建封装函数
将小球创建逻辑封装为可复用函数,支持自定义位置与特殊类型,为小球附加速度、类型属性;通过颜色和尺寸区分普通白球、金色高分球、红色炸弹球,同时为多球分裂、重置场景提供统一创建入口。

(2)初始小球实例化
游戏启动时生成第一个普通小球并加入管理列表,完成初始场景的核心实体搭建,让游戏开局即可进入玩法循环。

6、UI计分画笔初始化
创建专用的文字渲染画笔,与游戏实体画笔隔离,隐藏画笔本体,统一承担所有UI文字绘制工作,避免重复创建对象造成资源浪费。

7、UI界面绘制函数模块
(1)计分板更新函数
统一管理实时UI刷新,绘制前清空旧内容避免重叠,顶部居中展示等级、分数、生命、连击核心数据,左下角同步技能冷却状态,所有数据变动后调用即可保证UI与状态实时同步。

(2)开始菜单绘制函数
绘制游戏初始交互界面,顶部展示标题,中部通过固定坐标排布三个难度选项,配合后续点击检测实现“伪按钮”交互,底部补充操作说明与规则,降低新玩家学习成本。

(3)游戏失败界面函数
生命值归零时调用,用红色醒目字体展示失败标题,同时呈现最终得分与等级,底部提示点击重开,给玩家清晰的结果反馈与后续操作指引。

(4)胜利庆祝动画函数
通关后触发循环动画,通过索引取模逐帧切换文字颜色实现彩虹闪烁效果,同步展示成绩与重开提示,依托递归定时器实现无需额外线程的循环动画。

8、障碍物系统模块
(1) 障碍物生成函数
支持批量生成灰色横向移动障碍物,随机分布在屏幕中部,每个障碍物赋予随机水平速度与方向,生成后加入统一列表管理,等级提升时调用新增障碍,实现难度渐进增长。

(2)障碍物运动更新函数
每帧更新所有障碍物的水平位置,触碰窗口左右边界时反转速度,实现持续往返移动,持续干扰小球轨迹以提升接球难度。

(3)球-障碍物碰撞检测函数
实现小球与障碍物的物理碰撞反弹,通过横纵距离差判定碰撞,根据碰撞主方向反转对应速度轴模拟真实反弹,同时修正小球位置避免穿模卡顿,是游戏物理引擎的核心逻辑。

9、道具系统模块
(1)道具生成函数
实现定时随机掉落道具的机制,包含4种增益与3种减益道具,通过加权随机保证增益占比更高,不同道具用颜色区分识别,依托递归定时器实现4-8秒间隔的自动生成,无需主循环额外判断。
1 def spawn_powerup(): 2 if game_state != "playing": 3 return 4 # 正负道具混合 5 pu_types = [ 6 "paddle_grow", "ball_slow", "extra_life", "bonus_score", # 增益 7 "paddle_shrink", "ball_fast", "minus_score" # 减益 8 ] 9 weights = [3, 3, 1, 3, 2, 2, 2] 10 pu_type = random.choices(pu_types, weights=weights, k=1)[0] 11 12 pu = turtle.Turtle() 13 pu.speed(0) 14 pu.shape("circle") 15 pu.penup() 16 17 color_map = { 18 "paddle_grow": "blue", 19 "ball_slow": "cyan", 20 "extra_life": "pink", 21 "bonus_score": "gold", 22 "paddle_shrink": "purple", 23 "ball_fast": "orange", 24 "minus_score": "brown" 25 } 26 pu.color(color_map.get(pu_type, "white")) 27 pu.goto(random.randint(-250, 250), 280) 28 pu.type = pu_type 29 powerups.append(pu) 30 31 win.ontimer(spawn_powerup, random.randint(4000, 8000))
(2)道具更新与碰撞处理函数
管控道具全生命周期,包含匀速下落、边界清理、碰撞检测、效果触发、时效管理五个环节,接住道具后触发对应效果,时效类道具到期自动恢复默认参数,保证游戏数值平衡。
1 def update_powerups(): 2 global score, lives, paddle_grow_time, paddle_shrink_time 3 global ball_slow_time, ball_fast_time, slow_factor, base_dy 4 5 paddle_half_w = get_paddle_half_width() 6 7 for pu in powerups[:]: 8 pu.sety(pu.ycor() - 2.5) 9 10 if pu.ycor() < -280: 11 pu.hideturtle() 12 powerups.remove(pu) 13 continue 14 15 if (pu.ycor() - 10 <= paddle.ycor() + 10) and \ 16 (paddle.xcor() - paddle_half_w < pu.xcor() < paddle.xcor() + paddle_half_w): 17 18 if pu.type == "paddle_grow": 19 paddle.shapesize(stretch_wid=1, stretch_len=base_paddle_len * 1.5) 20 paddle_grow_time = 300 21 elif pu.type == "paddle_shrink": 22 paddle.shapesize(stretch_wid=1, stretch_len=base_paddle_len * 0.6) 23 paddle_shrink_time = 200 24 elif pu.type == "ball_slow": 25 slow_factor = 0.6 26 ball_slow_time = 180 27 elif pu.type == "ball_fast": 28 slow_factor = 1.5 29 ball_fast_time = 150 30 elif pu.type == "extra_life": 31 lives += 1 32 elif pu.type == "bonus_score": 33 score += 50 34 elif pu.type == "minus_score": 35 score = max(0, score - 30) 36 37 pu.hideturtle() 38 powerups.remove(pu) 39 update_scoreboard() 40 41 # 效果计时 42 if paddle_grow_time > 0: 43 paddle_grow_time -= 1 44 if paddle_grow_time == 0: 45 paddle.shapesize(stretch_wid=1, stretch_len=base_paddle_len) 46 47 if paddle_shrink_time > 0: 48 paddle_shrink_time -= 1 49 if paddle_shrink_time == 0: 50 paddle.shapesize(stretch_wid=1, stretch_len=base_paddle_len) 51 52 if ball_slow_time > 0: 53 ball_slow_time -= 1 54 if ball_slow_time == 0: 55 slow_factor = 1 56 57 if ball_fast_time > 0: 58 ball_fast_time -= 1 59 if ball_fast_time == 0: 60 slow_factor = 1
(3)挡板宽度计算辅助函数
统一计算挡板实际半宽像素值,作为所有碰撞检测与边界判断的尺寸依据,避免多处重复编写道具状态判断逻辑,减少代码冗余。

10、小球重置与特殊球机制
封装小球重置逻辑,统一处理触底、接球、飞出顶部后的状态重置,随机生成新位置与速度避免玩家背板;内置10%金币球、8%炸弹球的随机机制,丰富游戏的风险收益玩法,速度同步适配当前难度与道具效果。

11、视觉特效模块
(1)屏幕震动触发函数
对外提供统一的震动触发接口,设置5帧震动时长,在接球、接炸弹时调用,增强操作打击感与反馈强度。

(2)震动效果应用函数
每帧通过偏移底层画布坐标实现屏幕震动,无需移动所有游戏元素,性能开销极低,震动结束后自动复位画布,避免永久偏移。

12、技能与等级成长模块
(1)清屏技能函数
实现空格键触发的主动清屏技能,释放后清除全部障碍物并短暂减速所有小球,帮助玩家缓解高压局面;设置20秒冷却时间平衡技能强度,避免滥用破坏游戏平衡。

(2)等级升级检测函数
每50分自动升一级,升级同步切换背景色、新增障碍物、提升小球下落速度,实现难度平滑渐进增长,同时设置速度下限避免难度过高导致游戏无法操作。

13、游戏初始化与重置函数
是游戏状态流转的核心枢纽,负责开局与重开时的全量初始化:先清理上一局残留的所有实体,再重置全部数值与状态,根据选择的难度差异化配置参数,最后初始化场景元素并启动定时器,保证每局初始状态完全一致。
1 def start_game(): 2 global score, lives, combo, level, game_state, base_dy 3 global base_paddle_len, catch_count, skill_cd, slow_factor 4 global paddle_grow_time, paddle_shrink_time, ball_slow_time, ball_fast_time 5 6 # 清空对象 7 for obs in obstacles: 8 obs.hideturtle() 9 obstacles.clear() 10 for pu in powerups: 11 pu.hideturtle() 12 powerups.clear() 13 for b in balls[1:]: 14 b.hideturtle() 15 balls.clear() 16 17 # 重置状态 18 score = 0 19 lives = 3 20 combo = 1 21 level = 1 22 catch_count = 0 23 skill_cd = 0 24 slow_factor = 1 25 paddle_grow_time = 0 26 paddle_shrink_time = 0 27 ball_slow_time = 0 28 ball_fast_time = 0 29 win.bgcolor("black") 30 31 # 难度参数 32 if difficulty == "easy": 33 lives = 5 34 base_dy = -2 35 base_paddle_len = 8 36 obs_count = 2 37 elif difficulty == "hard": 38 lives = 2 39 base_dy = -4 40 base_paddle_len = 4 41 obs_count = 4 42 else: 43 lives = 3 44 base_dy = -3 45 base_paddle_len = 6 46 obs_count = 3 47 48 game_state = "playing" 49 paddle.shapesize(stretch_wid=1, stretch_len=base_paddle_len) 50 paddle.goto(0, -250) 51 52 # 初始化第一个球 53 b = create_ball() 54 balls.append(b) 55 56 create_obstacles(obs_count) 57 update_scoreboard() 58 win.ontimer(spawn_powerup, random.randint(3000, 5000)) 59 win.update()
14、玩家输入控制模块
(1)键盘右移控制函数
响应右方向键操作,控制挡板向右步进30像素,内置边界检测防止挡板移出窗口,非游戏状态下屏蔽操作避免逻辑混乱。

(2)键盘左移控制函数
响应左方向键操作,逻辑与右移对称,控制挡板向左步进30像素,保持操作手感统一,同样带边界限制防止移出窗口。

(3)鼠标跟随控制函数
实现鼠标实时跟随操控,将屏幕像素坐标转换为turtle画布坐标系,通过边界钳制保证挡板不超出窗口,操作比键盘更顺滑精准,适配不同玩家习惯。

(4)鼠标点击交互函数
分状态处理全局鼠标点击,开始菜单下通过坐标区间判断玩家选择的难度并启动游戏,结算界面下点击任意位置直接重开,无需额外控件即可完成全流程鼠标交互。

(5)全局事件绑定
将所有按键、鼠标事件与对应处理函数绑定,开启窗口事件监听,建立用户操作到游戏逻辑的完整映射,是交互系统生效的前提。

15、核心游戏主循环函数
是游戏的运行心脏,每30毫秒执行一次,按顺序完成状态校验、技能冷却、场景更新、小球物理、碰撞判定、胜负处理、多球分裂、渐进提速等全部逻辑,最后手动刷新画面并递归调用自身,形成稳定的游戏主循环。
1 # ---------- 核心游戏循环 ---------- 2 def update_game(): 3 global score, lives, combo, game_state, catch_count, skill_cd 4 5 if game_state == "start" or game_state != "playing": 6 win.ontimer(update_game, 50) 7 return 8 9 if skill_cd > 0: 10 skill_cd -= 1 11 if skill_cd % 30 == 0: 12 update_scoreboard() 13 14 apply_shake() 15 update_obstacles() 16 update_powerups() 17 level_up_check() 18 19 paddle_half_w = get_paddle_half_width() 20 21 for b in balls[:]: 22 b.setx(b.xcor() + b.dx) 23 b.sety(b.ycor() + b.dy) 24 25 # 左右墙反弹 26 if b.xcor() > 290 or b.xcor() < -290: 27 b.dx *= -1 28 29 # 障碍物碰撞 30 check_obstacle_collision(b) 31 32 # 触底处理 33 if b.ycor() < -280: 34 if len(balls) > 1: 35 b.hideturtle() 36 balls.remove(b) 37 continue 38 39 lives -= 1 40 combo = 1 41 update_scoreboard() 42 if lives <= 0: 43 game_state = "gameover" 44 show_game_over() 45 else: 46 reset_ball(b) 47 continue 48 49 # 挡板接球 50 if (b.ycor() - 10 <= paddle.ycor() + 10) and \ 51 (paddle.xcor() - paddle_half_w < b.xcor() < paddle.xcor() + paddle_half_w): 52 53 # 特殊球处理 54 if b.special == "bomb": 55 lives -= 1 56 screen_shake() 57 update_scoreboard() 58 if lives <= 0: 59 game_state = "gameover" 60 show_game_over() 61 break 62 reset_ball(b) 63 continue 64 elif b.special == "gold": 65 add_score = 30 * combo 66 else: 67 add_score = 10 * combo 68 69 combo += 1 70 score += add_score 71 catch_count += 1 72 screen_shake() 73 74 # 接球闪光效果 75 paddle.color("yellow") 76 win.ontimer(lambda: paddle.color("green"), 100) 77 78 update_scoreboard() 79 80 # 胜利判定 81 if score >= 200: 82 game_state = "win" 83 win_celebration() 84 break 85 86 # 每接5个球分裂一个新球 87 if catch_count % 5 == 0 and len(balls) < max_balls: 88 new_b = create_ball(b.xcor(), b.ycor()) 89 new_b.dx = -b.dx 90 new_b.dy = b.dy 91 balls.append(new_b) 92 93 reset_ball(b) 94 # 全局缓慢提速 95 global base_dy 96 base_dy = max(base_dy - 0.1, -7) 97 98 # 顶部保护 99 if b.ycor() > 300: 100 reset_ball(b) 101 102 win.update() 103 win.ontimer(update_game, 30)
16、程序启动入口
程序执行的入口逻辑,依次完成绘制初始菜单、启动游戏主循环、阻塞窗口保持运行三个步骤,保证程序启动后正常显示界面并等待用户交互,不会自动退出。

(二)、实验结果
1、程序源代码
点击查看代码
import turtle
import random
# ---------- 窗口设置 ----------
win = turtle.Screen()
win.title("接球大作战·强化版")
win.bgcolor("black")
win.setup(width=600, height=600)
win.tracer(0)
# ---------- 游戏核心变量 ----------
score = 0
lives = 3
combo = 1
level = 1
game_state = "start" # start / playing / gameover / win
difficulty = "normal"
# 效果计时器
paddle_grow_time = 0
paddle_shrink_time = 0
ball_slow_time = 0
ball_fast_time = 0
shake_time = 0
skill_cd = 0
# 基础参数
base_dy = -3
slow_factor = 1
base_paddle_len = 6
catch_count = 0
max_balls = 3
# 对象列表
obstacles = []
powerups = []
balls = []
# 庆祝动画
cele_colors = ["red", "yellow", "green", "blue", "purple", "orange", "cyan"]
cele_index = 0
# 关卡背景色
level_colors = ["black", "#1a1a2e", "#16213e", "#0f3460", "#533483", "#e94560"]
# ---------- 挡板 ----------
paddle = turtle.Turtle()
paddle.speed(0)
paddle.shape("square")
paddle.color("green")
paddle.shapesize(stretch_wid=1, stretch_len=base_paddle_len)
paddle.penup()
paddle.goto(0, -250)
# ---------- 初始球 ----------
def create_ball(x=0, y=250, is_special=None):
b = turtle.Turtle()
b.speed(0)
b.shape("circle")
b.penup()
b.goto(x, y)
b.dx = random.choice([-1, -0.5, 0.5, 1])
b.dy = base_dy * slow_factor
b.special = is_special # None普通 / gold金币 / bomb炸弹
if is_special == "gold":
b.color("gold")
elif is_special == "bomb":
b.color("red")
b.shapesize(stretch_wid=1.2, stretch_len=1.2)
else:
b.color("white")
return b
ball = create_ball()
balls.append(ball)
# ---------- 计分画笔 ----------
pen = turtle.Turtle()
pen.speed(0)
pen.color("white")
pen.penup()
pen.hideturtle()
# ---------- 辅助功能函数 ----------
def update_scoreboard():
pen.color("white")
pen.clear()
pen.goto(0, 260)
pen.write(f"Lv.{level} Score: {score} Lives: {lives} x{combo}",
align="center", font=("Courier", 18, "normal"))
pen.goto(-280, -280)
cd_text = f"技能[空格]: 就绪" if skill_cd <= 0 else f"技能冷却: {skill_cd // 30}s"
pen.write(cd_text, align="left", font=("Courier", 12, "normal"))
def draw_start_menu():
pen.color("white")
pen.clear()
pen.goto(0, 150)
pen.write("接球大作战·强化版", align="center", font=("Courier", 32, "bold"))
pen.goto(0, 80)
pen.write("选择难度开始游戏", align="center", font=("Courier", 18, "normal"))
pen.goto(-150, 20)
pen.write("【简单】", align="center", font=("Courier", 20, "normal"))
pen.goto(0, 20)
pen.write("【普通】", align="center", font=("Courier", 20, "normal"))
pen.goto(150, 20)
pen.write("【困难】", align="center", font=("Courier", 20, "normal"))
pen.goto(0, -40)
pen.write("←→方向键 / 鼠标移动挡板", align="center", font=("Courier", 14, "normal"))
pen.goto(0, -70)
pen.write("空格释放清屏技能 · 金色球高分 · 红色球扣命", align="center", font=("Courier", 12, "normal"))
def create_obstacles(count):
for _ in range(count):
obs = turtle.Turtle()
obs.speed(0)
obs.shape("square")
obs.color("gray")
obs.shapesize(stretch_wid=1, stretch_len=2)
obs.penup()
x = random.randint(-250, 250)
y = random.randint(-150, 150)
obs.goto(x, y)
obs.dx = random.choice([-0.8, -0.5, 0.5, 0.8]) # 移动速度
obstacles.append(obs)
def spawn_powerup():
if game_state != "playing":
return
# 正负道具混合
pu_types = [
"paddle_grow", "ball_slow", "extra_life", "bonus_score", # 增益
"paddle_shrink", "ball_fast", "minus_score" # 减益
]
weights = [3, 3, 1, 3, 2, 2, 2]
pu_type = random.choices(pu_types, weights=weights, k=1)[0]
pu = turtle.Turtle()
pu.speed(0)
pu.shape("circle")
pu.penup()
color_map = {
"paddle_grow": "blue",
"ball_slow": "cyan",
"extra_life": "pink",
"bonus_score": "gold",
"paddle_shrink": "purple",
"ball_fast": "orange",
"minus_score": "brown"
}
pu.color(color_map.get(pu_type, "white"))
pu.goto(random.randint(-250, 250), 280)
pu.type = pu_type
powerups.append(pu)
win.ontimer(spawn_powerup, random.randint(4000, 8000))
def update_powerups():
global score, lives, paddle_grow_time, paddle_shrink_time
global ball_slow_time, ball_fast_time, slow_factor, base_dy
paddle_half_w = get_paddle_half_width()
for pu in powerups[:]:
pu.sety(pu.ycor() - 2.5)
if pu.ycor() < -280:
pu.hideturtle()
powerups.remove(pu)
continue
if (pu.ycor() - 10 <= paddle.ycor() + 10) and \
(paddle.xcor() - paddle_half_w < pu.xcor() < paddle.xcor() + paddle_half_w):
if pu.type == "paddle_grow":
paddle.shapesize(stretch_wid=1, stretch_len=base_paddle_len * 1.5)
paddle_grow_time = 300
elif pu.type == "paddle_shrink":
paddle.shapesize(stretch_wid=1, stretch_len=base_paddle_len * 0.6)
paddle_shrink_time = 200
elif pu.type == "ball_slow":
slow_factor = 0.6
ball_slow_time = 180
elif pu.type == "ball_fast":
slow_factor = 1.5
ball_fast_time = 150
elif pu.type == "extra_life":
lives += 1
elif pu.type == "bonus_score":
score += 50
elif pu.type == "minus_score":
score = max(0, score - 30)
pu.hideturtle()
powerups.remove(pu)
update_scoreboard()
# 效果计时
if paddle_grow_time > 0:
paddle_grow_time -= 1
if paddle_grow_time == 0:
paddle.shapesize(stretch_wid=1, stretch_len=base_paddle_len)
if paddle_shrink_time > 0:
paddle_shrink_time -= 1
if paddle_shrink_time == 0:
paddle.shapesize(stretch_wid=1, stretch_len=base_paddle_len)
if ball_slow_time > 0:
ball_slow_time -= 1
if ball_slow_time == 0:
slow_factor = 1
if ball_fast_time > 0:
ball_fast_time -= 1
if ball_fast_time == 0:
slow_factor = 1
def get_paddle_half_width():
if paddle_grow_time > 0:
return base_paddle_len * 15
elif paddle_shrink_time > 0:
return base_paddle_len * 6
return base_paddle_len * 10
def update_obstacles():
for obs in obstacles:
obs.setx(obs.xcor() + obs.dx)
# 左右碰壁反弹
if obs.xcor() > 280 or obs.xcor() < -280:
obs.dx *= -1
def check_obstacle_collision(b):
obs_w, obs_h = 20, 10
ball_r = 10
for obs in obstacles:
dx = abs(b.xcor() - obs.xcor())
dy = abs(b.ycor() - obs.ycor())
if dx < obs_w + ball_r and dy < obs_h + ball_r:
if dx / (obs_w + ball_r) > dy / (obs_h + ball_r):
b.dx *= -1
else:
b.dy *= -1
if b.xcor() > obs.xcor():
b.setx(obs.xcor() + obs_w + ball_r)
else:
b.setx(obs.xcor() - obs_w - ball_r)
break
def reset_ball(b):
b.goto(random.randint(-200, 200), 250)
b.dx = random.choice([-1, -0.5, 0.5, 1])
b.dy = base_dy * slow_factor
# 10%概率生成特殊球
rand = random.random()
if rand < 0.1:
b.special = "gold"
b.color("gold")
b.shapesize(stretch_wid=1, stretch_len=1)
elif rand < 0.18:
b.special = "bomb"
b.color("red")
b.shapesize(stretch_wid=1.2, stretch_len=1.2)
else:
b.special = None
b.color("white")
b.shapesize(stretch_wid=1, stretch_len=1)
def screen_shake():
global shake_time
shake_time = 5
def apply_shake():
global shake_time
if shake_time > 0:
offset_x = random.randint(-3, 3)
offset_y = random.randint(-3, 3)
win.cv.canvasx(offset_x)
win.cv.canvasy(offset_y)
shake_time -= 1
else:
win.cv.canvasx(0)
win.cv.canvasy(0)
def use_skill():
"""空格释放清屏技能"""
global skill_cd
if game_state != "playing" or skill_cd > 0:
return
# 消除所有障碍物
for obs in obstacles:
obs.hideturtle()
obstacles.clear()
# 短暂减速所有球
for b in balls:
b.dy *= 0.5
skill_cd = 600 # 20秒冷却
update_scoreboard()
def level_up_check():
global level, base_dy
new_level = score // 50 + 1
if new_level > level:
level = new_level
# 背景变色
bg_color = level_colors[min(level - 1, len(level_colors) - 1)]
win.bgcolor(bg_color)
# 每升1关加1个障碍
create_obstacles(1)
# 略微提速
base_dy = max(base_dy - 0.3, -7)
update_scoreboard()
def win_celebration():
global cele_index
if game_state != "win":
return
pen.color(cele_colors[cele_index % len(cele_colors)])
pen.clear()
pen.goto(0, 50)
pen.write("BLACKWALL NB!", align="center", font=("Courier", 36, "bold"))
pen.goto(0, -20)
pen.write(f"最终得分: {score} 等级: Lv.{level}", align="center", font=("Courier", 20, "normal"))
pen.goto(0, -80)
pen.write("点击屏幕重新开始", align="center", font=("Courier", 16, "normal"))
cele_index += 1
win.ontimer(win_celebration, 200)
def show_game_over():
pen.color("red")
pen.clear()
pen.goto(0, 30)
pen.write("GAME OVER", align="center", font=("Courier", 36, "bold"))
pen.goto(0, -30)
pen.write(f"最终得分: {score} 等级: Lv.{level}", align="center", font=("Courier", 20, "normal"))
pen.goto(0, -90)
pen.write("点击屏幕重新开始", align="center", font=("Courier", 16, "normal"))
def start_game():
global score, lives, combo, level, game_state, base_dy
global base_paddle_len, catch_count, skill_cd, slow_factor
global paddle_grow_time, paddle_shrink_time, ball_slow_time, ball_fast_time
# 清空对象
for obs in obstacles:
obs.hideturtle()
obstacles.clear()
for pu in powerups:
pu.hideturtle()
powerups.clear()
for b in balls[1:]:
b.hideturtle()
balls.clear()
# 重置状态
score = 0
lives = 3
combo = 1
level = 1
catch_count = 0
skill_cd = 0
slow_factor = 1
paddle_grow_time = 0
paddle_shrink_time = 0
ball_slow_time = 0
ball_fast_time = 0
win.bgcolor("black")
# 难度参数
if difficulty == "easy":
lives = 5
base_dy = -2
base_paddle_len = 8
obs_count = 2
elif difficulty == "hard":
lives = 2
base_dy = -4
base_paddle_len = 4
obs_count = 4
else:
lives = 3
base_dy = -3
base_paddle_len = 6
obs_count = 3
game_state = "playing"
paddle.shapesize(stretch_wid=1, stretch_len=base_paddle_len)
paddle.goto(0, -250)
# 初始化第一个球
b = create_ball()
balls.append(b)
create_obstacles(obs_count)
update_scoreboard()
win.ontimer(spawn_powerup, random.randint(3000, 5000))
win.update()
# ---------- 输入控制 ----------
def paddle_right():
if game_state != "playing":
return
max_x = 300 - get_paddle_half_width()
if paddle.xcor() < max_x:
paddle.setx(paddle.xcor() + 30)
def paddle_left():
if game_state != "playing":
return
min_x = -300 + get_paddle_half_width()
if paddle.xcor() > min_x:
paddle.setx(paddle.xcor() - 30)
def paddle_mouse(x, y):
if game_state != "playing":
return
turtle_x = x - win.window_width() / 2
max_x = 300 - get_paddle_half_width()
new_x = max(-max_x, min(max_x, turtle_x))
paddle.setx(new_x)
def on_click(x, y):
global game_state, difficulty
if game_state == "start":
if -30 < y < 50:
if -190 < x < -110:
difficulty = "easy"
start_game()
elif -40 < x < 40:
difficulty = "normal"
start_game()
elif 110 < x < 190:
difficulty = "hard"
start_game()
elif game_state != "playing":
start_game()
win.listen()
win.onkeypress(paddle_right, "Right")
win.onkeypress(paddle_left, "Left")
win.onkeypress(use_skill, "space")
win.onclick(on_click)
win.getcanvas().bind("<Motion>", lambda e: paddle_mouse(e.x, e.y))
# ---------- 核心游戏循环 ----------
def update_game():
global score, lives, combo, game_state, catch_count, skill_cd
if game_state == "start" or game_state != "playing":
win.ontimer(update_game, 50)
return
if skill_cd > 0:
skill_cd -= 1
if skill_cd % 30 == 0:
update_scoreboard()
apply_shake()
update_obstacles()
update_powerups()
level_up_check()
paddle_half_w = get_paddle_half_width()
for b in balls[:]:
b.setx(b.xcor() + b.dx)
b.sety(b.ycor() + b.dy)
# 左右墙反弹
if b.xcor() > 290 or b.xcor() < -290:
b.dx *= -1
# 障碍物碰撞
check_obstacle_collision(b)
# 触底处理
if b.ycor() < -280:
if len(balls) > 1:
b.hideturtle()
balls.remove(b)
continue
lives -= 1
combo = 1
update_scoreboard()
if lives <= 0:
game_state = "gameover"
show_game_over()
else:
reset_ball(b)
continue
# 挡板接球
if (b.ycor() - 10 <= paddle.ycor() + 10) and \
(paddle.xcor() - paddle_half_w < b.xcor() < paddle.xcor() + paddle_half_w):
# 特殊球处理
if b.special == "bomb":
lives -= 1
screen_shake()
update_scoreboard()
if lives <= 0:
game_state = "gameover"
show_game_over()
break
reset_ball(b)
continue
elif b.special == "gold":
add_score = 30 * combo
else:
add_score = 10 * combo
combo += 1
score += add_score
catch_count += 1
screen_shake()
# 接球闪光效果
paddle.color("yellow")
win.ontimer(lambda: paddle.color("green"), 100)
update_scoreboard()
# 胜利判定
if score >= 200:
game_state = "win"
win_celebration()
break
# 每接5个球分裂一个新球
if catch_count % 5 == 0 and len(balls) < max_balls:
new_b = create_ball(b.xcor(), b.ycor())
new_b.dx = -b.dx
new_b.dy = b.dy
balls.append(new_b)
reset_ball(b)
# 全局缓慢提速
global base_dy
base_dy = max(base_dy - 0.1, -7)
# 顶部保护
if b.ycor() > 300:
reset_ball(b)
win.update()
win.ontimer(update_game, 30)
# ---------- 启动 ----------
draw_start_menu()
update_game()
turtle.done()
2、运行视频
3、视频链接
https://www.bilibili.com/video/BV1Ztj366EY4/?share_source=copy_web&vd_source=36b38531ac1f3769136a5b5cee3e3ac2四、实验过程中遇到的问题和解决过程
问题1:初次编写的程序开始游戏时多个小球迅速下落,球速过快无法加速,导致游戏立刻快速结束且无法重启,球坐标更新速度失控。
解决方案1:借助大模型的帮助,发现出现问题的原因是因为初次编写的程序使用了 while True 无限循环,且循环内没有等待/延时。在 turtle 中,while True 会以极快的速度(每秒数百万次)不断更新小球坐标,导致小球瞬间从顶部(y=250)落到触底位置(y<-280),看起来就是“一闪而过”然后立即游戏结束。解决方案是大模型给出用 turtle.ontimer() 实现定时回调,控制游戏帧率(通常 30~60 FPS),并让 GUI 事件正常处理;使用 win.ontimer(update_game, 30) 定时执行;每 30 毫秒移动一次,速度稳定;添加 win.onclick() 点击重启;修正 ball.ycor() - 10 <= paddle.ycor() + 10 并增加范围判断;限制 ball.dy 最低为 -6。
问题2:编写的程序小球下落速度太慢了
解决方案2:将最大速度上限拉高,从原 -7 提升至 -12,后期加速空间更大;每次接球提速 0.2,关卡升级提速 0.5。
五、课程内容总结
1. 初识Python:了解Python的发展历程与简洁易读、库资源丰富的核心语言特点,熟悉其在多领域的应用场景,完成了本地开发环境的搭建与基础配置。
2. Python基础语法:掌握变量定义规则与数字、字符串、布尔值、空值等基础数据类型,能熟练运用算术、比较、逻辑、赋值四类运算符,可使用input()、print()完成基础输入输出,严格遵循缩进、注释等代码书写规范。
3. 字符串处理:掌握字符串的创建、索引与切片操作,能灵活运用split()、join()、replace()、strip()等常用方法处理文本;掌握多种字符串格式化输出方式,了解正则表达式基础规则,可实现简单的内容匹配与校验。
4. 核心内置数据结构:掌握列表、元组、字典、集合四种数据结构的特性与适用场景,能熟练完成元素的增删改查操作,掌握切片、列表推导式等高效的数据处理方式。
5. 流程控制语句:掌握if-elif-else条件分支结构,以及for、while两种循环语句的用法,能配合break、continue、pass关键字调整循环执行逻辑,理解程序的执行顺序与判断规则。
6. 函数使用:掌握用def关键字定义、调用函数的方法,理解形参与实参的区别,能灵活运用位置参数、默认参数、关键字参数与可变参数设计函数;可通过return语句返回单个或多个结果,实现代码逻辑的封装与复用。
7. 文件及目录操作:了解冯·诺依曼体系架构,明确内存与外存的存储差异,能区分结构化与非结构化数据;掌握文件打开、读写、指针移动与关闭的核心方法,可使用with语句安全管理文件操作,能通过os模块完成目录创建、路径管理等操作。
8. 模块与标准库:掌握Python模块的多种导入方式与别名设置方法,熟悉os、sys、math、random、datetime等常用标准库的基础功能;能够自主创建自定义模块与包,规范管理项目的代码结构。
9. 面向对象程序设计:理解类与对象“抽象模板-具体实例”的对应关系,掌握类的实例化方式与构造方法的作用,明确类方法与普通函数的区别;掌握封装、继承、多态三大面向对象核心特性,了解模块与包的代码组织管理逻辑。
10. 异常处理与程序调试:理解异常处理对提升程序健壮性的作用,掌握try-except-finally异常捕获结构;能识别ValueError、TypeError、FileNotFoundError等常见异常类型,可借助断点调试快速定位、解决代码问题。
11. Python数据库操作:了解数据库的基础作用与应用场景,掌握利用Python实现数据库增、删、改、查的基础操作流程。
12. 爬虫基础:了解爬虫的基本概念与数据采集、分析的应用场景,明确爬虫开发的法律边界与合规要求;知晓爬虫的核心技术组成,了解基础的反反爬虫应对思路。
六、课程感想体会及意见建议
1、课程感悟体会
最开始接触Python的时候,真的是抗拒的,我总觉得编程python是计算机专业学生的事,对零基础的文科生自己来说门槛很高,看到代码就下意识觉得难。本人简直是个电脑白痴,对编程代码这些东西可谓是一窍不通。但是王老师带着我们梳理知识点,跟着课程一步步从基础语法学起,到写小练习,然后时不时的编一些小程序(虽然大部分都是copy老师写的),再到最后独立完成一个完整的小游戏,我真切感受到了编程的乐趣——它不是死记硬背的语法,而是把想法一步步落地实现的工具。
这次做接球游戏的过程让我收获特别多。从最基础的挡板移动、球体反弹,到一点点加上道具、障碍物、技能这些功能,中间踩了很多坑,有时候一个小的bug需要调试很久(哭)。但每次找到问题、看到功能正常运行的时候,那种成就感是别的事情比不了的。我也慢慢明白,写代码从来不是一步到位写出完美的程序,而是不断调试、不断优化的过程,耐心和细心比天赋重要得多。
这门课也打破了我对编程的刻板印象,它不只是枯燥的逻辑,也可以用来做有意思的小游戏、写实用的小工具。哪怕不是相关专业,学会一点Python,也能在很多地方帮到自己。特别感谢black wall老师的耐心讲解,把复杂的知识讲得通俗易懂,给了我很多试错的空间,让我这个编程新手也能跟上节奏,真正体会到了写代码的快乐。
2、课程意见建议
(1)建议增加随堂小练习的比重,讲完一个知识点就带着大家写一小段代码实操,既能及时巩固知识点,也能当场发现问题、解决问题,比单纯听课记忆更深刻。
(2)实验选题可以更丰富一些,除了游戏开发之外,也可以提供数据处理、实用小工具等不同方向的选题,让大家可以根据自己的兴趣和需求选择。
(3)课堂代码演示可以适当放慢节奏,关键步骤稍作停留,或者提前把演示代码发给大家,这样上课的时候可以跟着思路理解,不用只顾着抄代码跟不上讲解。
(4)本学期坚持英语打卡让我收获满满,在积累编程专业词汇、提升专业素养的同时,也高效巩固了四级英语知识,我十分认可这种学习方式,希望能够持续开展。
(5)老师可以在讲解一些python的复杂知识点时,适当放慢节奏,多引入生活实例或趣味案例,帮助我们理解。同时,希望可以向大家提供一些优质的网络教学视频(如b站),让在课上没有完全明白的同学自行查缺补漏。
我为black wall举大旗,看谁敢与他为敌!

浙公网安备 33010602011771号