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

Python打地鼠游戏按键判定滞后问题求助

解决Python Turtle打地鼠游戏按键判定滞后问题

问题描述

基于Python Turtle库开发的打地鼠游戏,运行时地鼠会在格子间随机跳跃,但每次按对应数字键都提示“Miss”,只有提前预判下一个位置按键才能触发“Hit”,说明按键判定逻辑滞后到了下一次循环。

原代码

import turtle
import random
import time

t = turtle.Turtle()
t.hideturtle()

mole_x, mole_y = 0, 0

# Set up screen
wn = turtle.Screen()
wn.title("Whack-A-Mole")
wn.bgcolor("green")
wn.setup(width=600, height=600)
wn.tracer(0)


# Draws a square with top-left position (x,y) and side length size
def drawsq(x, y, size):
    t.penup()
    t.goto(x, y)
    t.pendown()
    for i in range(4):
        t.forward(size)
        t.right(90)
    t.penup()


# Draw a circle at center (x,y) with radius r
def drawcr(x, y, r):
    t.penup()
    t.goto(x, y - r)
    t.pendown()
    t.circle(r)

def molecoords():
    coords = [-150, 0, 150]
    x = random.choice(coords)
    y = random.choice(coords)
    return x, y

#Draws the mole
def draw_mole(x, y): 
    drawcr(x, y, 50)  # Body
    drawcr(x - 40, y - 40, 7)  # Left foot
    drawcr(x + 40, y - 40, 7)  # Right foot
    drawcr(x - 55, y + 15, 7)  # Left hand
    drawcr(x + 55, y + 15, 7)  # Right hand
    t.penup()  # Head
    t.goto(x - 45, y + 20)
    t.setheading(-50)
    t.pendown()
    t.circle(60, 100)
    t.setheading(0)
    drawcr(x - 10, y + 35, 2)  # Left eye
    drawcr(x + 10, y + 35, 2)  # Right eye
    drawgrid(x - 7, y + 20, 2, 1, 7)  # Teeth
    t.goto(x, y + 22)  # Nose
    t.fillcolor("black")  # Set the fill color
    t.begin_fill()  # Begin filling
    t.pendown()
    t.left(60)
    t.forward(5)
    t.left(120)
    t.forward(5)
    t.left(120)
    t.forward(5)
    t.end_fill()
    t.setheading(0)

# Draw a grid with x rows and y columns with squares of side length size starting at (tlx,tly)
def drawgrid(tlx, tly, x, y, size):
    for i in range(x):
        for j in range(y):
            drawsq(tlx + (i * size), tly - j * size, size)

def check_hit(key):
    target_positions = {
        '1': (-150, -150),
        '2': (0, -150),
        '3': (150, -150),
        '4': (-150, 0),
        '5': (0, 0),
        '6': (150, 0),
        '7': (-150, 150),
        '8': (0, 150),
        '9': (150, 150)
    }
    target_x, target_y = target_positions.get(key)
    if (mole_x, mole_y) == (target_x, target_y):
        print("Hit!")
    else:
        print("Miss!")

def on_key_press(key):
    check_hit(key)

def game_loop():
    global mole_x, mole_y
    start = time.time()
    duration = 30
    while time.time() - start < duration:
        t.clear()
        drawgrid(-225, 225, 3, 3, 150)
        mole_x, mole_y = molecoords()
        draw_mole(mole_x, mole_y)
        wn.update()
        time.sleep(2)

# Bind key press events
wn.listen()
wn.onkeypress(lambda: on_key_press('1'), '1')
wn.onkeypress(lambda: on_key_press('2'), '2')
wn.onkeypress(lambda: on_key_press('3'), '3')
wn.onkeypress(lambda: on_key_press('4'), '4')
wn.onkeypress(lambda: on_key_press('5'), '5')
wn.onkeypress(lambda: on_key_press('6'), '6')
wn.onkeypress(lambda: on_key_press('7'), '7')
wn.onkeypress(lambda: on_key_press('8'), '8')
wn.onkeypress(lambda: on_key_press('9'), '9')

game_loop()

def gameover():
    t.penup()
    wn.clear()
    wn.bgcolor("black")
    t.goto(0, 100)
    t.pencolor("White")
    t.write("Time's Up!", align="center", font=("Arial", 80, "bold"))

gameover()

turtle.done()

问题分析

原游戏循环的执行顺序存在逻辑错误:

  1. 每次循环先生成新的地鼠位置,再绘制显示,最后休眠2秒
  2. 按键判定时读取的mole_x, mole_y是下一次要显示的位置,而非当前屏幕上的位置,导致判定滞后
  3. time.sleep(2)会阻塞整个程序,期间按键事件无法及时处理,进一步加剧延迟

修改方案

1. 调整循环逻辑

先显示当前地鼠位置,等待2秒后再更新位置,确保按键判定匹配屏幕显示的内容。

2. 替换阻塞延时

用turtle.ontimer()替代time.sleep(),实现非阻塞式延时,保证按键事件能实时响应。

修改后的完整代码

import turtle
import random
import time

t = turtle.Turtle()
t.hideturtle()

mole_x, mole_y = 0, 0
start_time = time.time()
duration = 30

# Set up screen
wn = turtle.Screen()
wn.title("Whack-A-Mole")
wn.bgcolor("green")
wn.setup(width=600, height=600)
wn.tracer(0)


# Draws a square with top-left position (x,y) and side length size
def drawsq(x, y, size):
    t.penup()
    t.goto(x, y)
    t.pendown()
    for i in range(4):
        t.forward(size)
        t.right(90)
    t.penup()


# Draw a circle at center (x,y) with radius r
def drawcr(x, y, r):
    t.penup()
    t.goto(x, y - r)
    t.pendown()
    t.circle(r)

def molecoords():
    coords = [-150, 0, 150]
    x = random.choice(coords)
    y = random.choice(coords)
    return x, y

#Draws the mole
def draw_mole(x, y): 
    drawcr(x, y, 50)  # Body
    drawcr(x - 40, y - 40, 7)  # Left foot
    drawcr(x + 40, y - 40, 7)  # Right foot
    drawcr(x - 55, y + 15, 7)  # Left hand
    drawcr(x + 55, y + 15, 7)  # Right hand
    t.penup()  # Head
    t.goto(x - 45, y + 20)
    t.setheading(-50)
    t.pendown()
    t.circle(60, 100)
    t.setheading(0)
    drawcr(x - 10, y + 35, 2)  # Left eye
    drawcr(x + 10, y + 35, 2)  # Right eye
    drawgrid(x - 7, y + 20, 2, 1, 7)  # Teeth
    t.goto(x, y + 22)  # Nose
    t.fillcolor("black")  # Set the fill color
    t.begin_fill()  # Begin filling
    t.pendown()
    t.left(60)
    t.forward(5)
    t.left(120)
    t.forward(5)
    t.left(120)
    t.forward(5)
    t.end_fill()
    t.setheading(0)

# Draw a grid with x rows and y columns with squares of side length size starting at (tlx,tly)
def drawgrid(tlx, tly, x, y, size):
    for i in range(x):
        for j in range(y):
            drawsq(tlx + (i * size), tly - j * size, size)

def check_hit(key):
    target_positions = {
        '1': (-150, -150),
        '2': (0, -150),
        '3': (150, -150),
        '4': (-150, 0),
        '5': (0, 0),
        '6': (150, 0),
        '7': (-150, 150),
        '8': (0, 150),
        '9': (150, 150)
    }
    target_x, target_y = target_positions.get(key)
    if (mole_x, mole_y) == (target_x, target_y):
        print("Hit!")
    else:
        print("Miss!")

def on_key_press(key):
    check_hit(key)

def game_loop():
    global mole_x, mole_y, start_time
    # 检查游戏是否超时
    if time.time() - start_time >= duration:
        gameover()
        return
    # 清空画布并绘制网格
    t.clear()
    drawgrid(-225, 225, 3, 3, 150)
    # 绘制当前地鼠位置
    draw_mole(mole_x, mole_y)
    wn.update()
    # 2秒后更新地鼠位置并继续循环
    wn.ontimer(lambda: update_mole_position(), 2000)

def update_mole_position():
    global mole_x, mole_y
    # 生成新的地鼠位置
    mole_x, mole_y = molecoords()
    # 启动下一轮游戏循环
    game_loop()

def gameover():
    t.penup()
    wn.clear()
    wn.bgcolor("black")
    t.goto(0, 100)
    t.pencolor("White")
    t.write("Time's Up!", align="center", font=("Arial", 80, "bold"))

# Bind key press events
wn.listen()
wn.onkeypress(lambda: on_key_press('1'), '1')
wn.onkeypress(lambda: on_key_press('2'), '2')
wn.onkeypress(lambda: on_key_press('3'), '3')
wn.onkeypress(lambda: on_key_press('4'), '4')
wn.onkeypress(lambda: on_key_press('5'), '5')
wn.onkeypress(lambda: on_key_press('6'), '6')
wn.onkeypress(lambda: on_key_press('7'), '7')
wn.onkeypress(lambda: on_key_press('8'), '8')
wn.onkeypress(lambda: on_key_press('9'), '9')

# 初始化地鼠位置并启动游戏
mole_x, mole_y = molecoords()
game_loop()

turtle.done()

修改说明

  • 将原循环拆分为game_loop()和update_mole_position(),先显示当前地鼠位置,2秒后再更新位置,确保判定逻辑匹配屏幕内容
  • 用wn.ontimer()实现非阻塞延时,避免程序休眠期间无法处理按键事件
  • 初始化时先生成地鼠位置,避免第一次进入循环时显示空白

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 11:07:03