You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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()

关键修改点说明

  1. 线程初始化与启动:
    • 定义obstacle_thread函数作为线程执行逻辑,专注处理障碍物的生成与移动
    • 使用threading.Thread创建线程,设置daemon=True确保主程序退出时线程自动终止
  2. 非阻塞输入处理:
    • 替换原代码中阻塞式的win.getMouse()和win.getKey()为win.checkMouse()和win.checkKey(),避免循环被卡住
  3. 全局状态共享:
    • 用obstacles列表统一管理所有障碍物,方便线程与主线程访问
    • 用running布尔变量控制线程与主循环的停止,实现优雅退出
  4. 动画逻辑优化:
    • 调整跳跃动画的time.sleep参数,让跳跃动作更自然
    • 添加主循环帧率控制,降低不必要的CPU占用

内容的提问来源于stack exchange,提问作者Otter Craft

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 01:17:06