如何在Turtle图形库中终止ontimer已设置的函数?蛇游戏问题
问题描述
我正在开发一款食物可随机传送的蛇类游戏,使用Turtle库的ontimer结合函数递归,让mole(食物)在固定位置停留3秒后自动传送。但玩家控制的乌龟吃到mole时,触发传送后,之前ontimer已经设置好的回调函数仍会在到点后执行,导致传送被调用两次。我试过用参数阻止递归,但实际需求是:乌龟吃到mole后,传送计时器要重新从起点开始计时,而不是继续之前的剩余时间。
原主代码:
def mole_teleport(is_loop=True): rand_x = random.randint(-250, 250) rand_y = random.randint(-250, 250) mole.teleport(rand_x, rand_y) if is_loop == False: return else: screen.ontimer(mole_teleport, 3000) tim = Player() #user_turtle mole = Turtle("circle") mole_teleport() screen = Screen() screen.setup(600, 600) screen.listen() screen.onkey(tim.turn_left, "Left") screen.onkey(tim.turn_right, "Right") game_is_on = True while game_is_on: tim.move() if mole.distance(tim) < 20: mole_teleport(is_loop=False)
Player类代码:
from turtle import Turtle SPEED = 3 class Player(Turtle): def __init__(self) -> None: super().__init__() self.speed("fastest") self.shape("turtle") self.penup() def move(self): self.forward(SPEED) def turn_left(self): self.left(90) def turn_right(self): self.right(90)
解决方案
问题核心是:之前通过ontimer注册的3秒回调并没有被取消,即使你调用mole_teleport(is_loop=False)立即传送,那个已经在队列里的回调到时间还是会执行。要实现"吃到食物后重置计时器",需要先取消之前的计时器,再重新启动计时。
Turtle的ontimer会返回一个计时器ID,我们可以保存这个ID,在需要重置时用screen.cleartimer(timer_id)取消未触发的计时器,然后重新调用传送函数并设置新的计时器。
修改后的完整代码:
import random from turtle import Turtle, Screen def mole_teleport(): rand_x = random.randint(-250, 250) rand_y = random.randint(-250, 250) mole.teleport(rand_x, rand_y) # 保存计时器ID,用于后续取消操作 global timer_id timer_id = screen.ontimer(mole_teleport, 3000) class Player(Turtle): SPEED = 3 def __init__(self) -> None: super().__init__() self.speed("fastest") self.shape("turtle") self.penup() def move(self): self.forward(self.SPEED) def turn_left(self): self.left(90) def turn_right(self): self.right(90) # 初始化游戏元素 tim = Player() mole = Turtle("circle") screen = Screen() screen.setup(600, 600) # 绑定控制按键 screen.listen() screen.onkey(tim.turn_left, "Left") screen.onkey(tim.turn_right, "Right") # 初始化计时器ID并启动首次传送 timer_id = None mole_teleport() game_is_on = True while game_is_on: screen.update() # 优化动画流畅度,避免循环卡顿 tim.move() if mole.distance(tim) < 20: # 取消之前未触发的计时器 screen.cleartimer(timer_id) # 立即传送并重新启动3秒计时 mole_teleport()
关键修改点:
- 移除了
is_loop参数,简化逻辑:每次调用mole_teleport都会完成传送,并设置下一次3秒后的传送计时器,同时保存计时器ID - 吃到食物时,先调用
screen.cleartimer(timer_id)取消之前注册的计时器,再重新调用mole_teleport,确保计时器从当前时间重新开始计算3秒,不会出现重复触发 - 添加
screen.update()优化游戏循环的动画流畅度,避免Turtle原生循环的卡顿问题
内容的提问来源于stack exchange,提问作者tensai_bootleg
相关产品推荐
相关产品推荐

