Python中如何同时运行多循环?(基于Zelle graphics库)
解决Python跑酷游戏多任务并行问题(Threading应用)
问题描述
我正在修Python入门课程,期末项目是制作一款类似谷歌无网恐龙游戏的无限跑酷游戏。需求如下:
- 让障碍物持续移动/生成,同时玩家可以跳跃躲避障碍物,需要两个循环同时运行
- 已添加的退出按钮及后续新增代码要在循环运行时正常工作
查资料得知需要用threading,但不理解原理也不会用,附上当前杂乱的代码。
Threading 核心原理
线程是Python中实现多任务并行的方式,简单来说就是让程序同时执行多个独立的代码块:
- 每个线程拥有自己的执行流程,但共享程序的内存空间(比如全局变量、窗口对象)
- 对于你的游戏,我们可以用一个线程专门处理障碍物的移动和生成,另一个主线程处理用户输入(跳跃、点击退出按钮),这样两个任务不会互相阻塞(比如不会因为等待按键输入而导致障碍物停止移动)
修改后的完整代码
from graphics import * import time import threading ############# # Variables # ############# gecko = "gecko.gif" savannah = "savannah.gif" keeled = "keeled.gif" lacerta = "lacerta.gif" watermtr = "watermonitor.gif" komodo = "komodo.gif" lizard = komodo win = GraphWin("Game", 1500, 750) # 全局变量:共享障碍物列表,方便线程和主线程访问 obstacles = [] # 控制线程与主循环运行的标志 running = True class Button(): # 按钮类:封装矩形与文本元素 def __init__(self, p1, p2, text): self.rectangle = Rectangle(p1, p2) self.text = Text( Point((p1.getX() + p2.getX())/2, (p1.getY() + p2.getY())/2), text) def draw(self, graphwin): self.rectangle.draw(graphwin) self.text.draw(graphwin) def clickedInside(button, mouse): # 判断鼠标是否点击在按钮范围内 if mouse is None: return False return (button.rectangle.getP1().getX() <= mouse.getX() <= button.rectangle.getP2().getX() and button.rectangle.getP1().getY() <= mouse.getY() <= button.rectangle.getP2().getY()) quitButton = Button(Point(1425, 10), Point(1485, 35), "Quit") def character(): # 初始化角色的位置、碰撞盒与背景 global characterXY, hitBoxSize, background if lizard == gecko: background = "geckobackground.gif" characterXY = Point(715, 375) hitBoxSize = Rectangle(Point(670, 435), Point(830, 350)) elif lizard == savannah: background = "savannahbackground.gif" characterXY = Point(710, 375) hitBoxSize = Rectangle(Point(655, 435), Point(845, 385)) elif lizard == keeled: background = "keeledbackground.gif" characterXY = Point(685, 425) hitBoxSize = Rectangle(Point(670, 435), Point(830, 370)) elif lizard == lacerta: background = "lacertabackground.gif" characterXY = Point(700, 410) hitBoxSize = Rectangle(Point(690, 435), Point(810, 365)) elif lizard == watermtr: background = "watermonitorbackground.gif" characterXY = Point(670, 410) hitBoxSize = Rectangle(Point(690, 435), Point(810, 385)) elif lizard == komodo: background = "komodobackground.gif" characterXY = Point(685, 398) hitBoxSize = Rectangle(Point(690, 435), Point(810, 375)) character() def obstacle_thread(): # 障碍物线程:负责生成与移动障碍物 global obstacles, running while running: # 生成新障碍物并添加到列表 new_obstacle = Image(Point(1525, 415), "cactus.gif") new_obstacle.draw(win) obstacles.append(new_obstacle) # 移动障碍物直到移出屏幕 for _ in range(1550): if not running: break new_obstacle.move(-1, 0) time.sleep(0.001) # 清理移出屏幕的障碍物 new_obstacle.undraw() obstacles.remove(new_obstacle) # 控制障碍物生成间隔,可根据难度调整 time.sleep(1.5) def main(): global running, obstacles hitBox = hitBoxSize player = Image(characterXY, lizard) ground = Line(Point(0, 437), Point(1500, 437)) ground.setWidth(3) gameBackground = Image(Point(750, 375), background) # 绘制所有游戏元素 gameBackground.draw(win) ground.draw(win) player.draw(win) quitButton.draw(win) # 启动障碍物线程:设置为守护线程,主程序退出时自动终止 obstacle_t = threading.Thread(target=obstacle_thread, daemon=True) obstacle_t.start() # 主循环:处理用户输入与退出逻辑 while running: # 非阻塞检查鼠标点击 mouse = win.checkMouse() if clickedInside(quitButton, mouse): running = False win.close() break # 非阻塞检查按键输入 key = win.checkKey() if key in ["space", "w"]: # 跳跃动画:向上移动 for _ in range(15): player.move(0, -6) hitBox.move(0, -6) time.sleep(0.005) # 下落动画:向下移动 for _ in range(15): player.move(0, 6) hitBox.move(0, 6) time.sleep(0.005) # 可选:碰撞检测逻辑(可根据hitBox与障碍物位置完善) # for obs in obstacles: # # 判断碰撞盒与障碍物是否重叠 # pass # 控制主循环帧率,降低CPU占用 time.sleep(0.01) def chooseLizard(): pickWin = GraphWin("Choose your lizard", 1000, 550) character = Image(Point(500, 275), keeled) character.draw(pickWin) pickWin.getMouse() # chooseLizard() if __name__ == "__main__": main()
关键修改点说明
- 线程初始化与启动:
- 定义
obstacle_thread函数作为线程执行逻辑,专注处理障碍物的生成与移动 - 使用
threading.Thread创建线程,设置daemon=True确保主程序退出时线程自动终止
- 定义
- 非阻塞输入处理:
- 替换原代码中阻塞式的
win.getMouse()和win.getKey()为win.checkMouse()和win.checkKey(),避免循环被卡住
- 替换原代码中阻塞式的
- 全局状态共享:
- 用
obstacles列表统一管理所有障碍物,方便线程与主线程访问 - 用
running布尔变量控制线程与主循环的停止,实现优雅退出
- 用
- 动画逻辑优化:
- 调整跳跃动画的
time.sleep参数,让跳跃动作更自然 - 添加主循环帧率控制,降低不必要的CPU占用
- 调整跳跃动画的
内容的提问来源于stack exchange,提问作者Otter Craft
相关产品推荐
相关产品推荐

