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()
问题分析
原游戏循环的执行顺序存在逻辑错误:
- 每次循环先生成新的地鼠位置,再绘制显示,最后休眠2秒
- 按键判定时读取的
mole_x, mole_y是下一次要显示的位置,而非当前屏幕上的位置,导致判定滞后 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
相关产品推荐
相关产品推荐

