Python Turtle模块没有回应要发出的击键

发布于 2025-02-11 12:55:24 字数 2036 浏览 1 评论 0 原文

到目前为止,我的蛇游戏还可以。一个问题是,由于某种原因,“乌龟”没有回应击键,我真的不知道为什么。我尝试了很多不同的东西,但这都是毫无用处的。主要问题是我不完全确定主要问题在哪里。我确定的是,问题很可能来自我的代码,但我似乎找不到。如果您可以协助我解决这个问题,那将是很棒的。

import time
from turtle import Screen, Turtle

STARTING_X_POSITIONS = [0, -20, -40]
MOVEMENT_DISTANCE = 20


class Snake:
    def __init__(self):
        self.segments = []
        self.create_snake()
        self.head = self.segments[0]

    def create_snake(self):
        for i in range(3):
            new_snake = Turtle('square')
            new_snake.color('RoyalBlue')
            new_snake.penup()
            new_snake.goto(STARTING_X_POSITIONS[i], 0)
            self.segments.append(new_snake)

    def move(self):
        # We Want The Loop To Start At Index (2) And Decrease Itself Till It Reaches Zero (Excluded)
        for snake_index in range(len(self.segments) - 1, 0, -1):
            x_pos = self.segments[snake_index - 1].xcor()
            y_pos = self.segments[snake_index - 1].ycor()
            self.segments[snake_index].goto(x_pos, y_pos)
        self.segments[0].forward(MOVEMENT_DISTANCE)

    def up(self):
        self.head.setheading(90)

    def down(self):
        self.head.setheading(270)

    def left(self):
        self.head.setheading(180)

    def right(self):
        self.head.setheading(0)


def setup_screen(screen):
    screen.bgcolor('black')
    screen.title('Snake Game')
    screen.setup(width=600, height=600)
    screen.tracer(0)


def start_game(screen, snake):
    setup_screen(screen)
    game_on = True
    while game_on:
        screen.update()
        time.sleep(0.1)
        snake.move()


def control_snake(screen, snake):
    screen.listen()
    screen.onkey(key='Up', fun=snake.up)
    screen.onkey(key='Down', fun=snake.down)
    screen.onkey(key='Left', fun=snake.left)
    screen.onkey(key='Right', fun=snake.right)
    screen.exitonclick()


def main():
    screen = Screen()
    snake = Snake()
    start_game(screen, snake)
    control_snake(screen, snake)


if __name__ == '__main__':
    main()

So far, my snake game is doing somewhat okay. One issue is that for some reason the "turtle" isn't responding to the keystrokes, and I really don't know why. I tried a lot of different stuff but it was all useless. The main problem is that I am not entirely sure where the main issue is. What I know for sure is that the problem is most likely from my code, but I can't seem to find it. If you could assist me in solving this issue that would great.

import time
from turtle import Screen, Turtle

STARTING_X_POSITIONS = [0, -20, -40]
MOVEMENT_DISTANCE = 20


class Snake:
    def __init__(self):
        self.segments = []
        self.create_snake()
        self.head = self.segments[0]

    def create_snake(self):
        for i in range(3):
            new_snake = Turtle('square')
            new_snake.color('RoyalBlue')
            new_snake.penup()
            new_snake.goto(STARTING_X_POSITIONS[i], 0)
            self.segments.append(new_snake)

    def move(self):
        # We Want The Loop To Start At Index (2) And Decrease Itself Till It Reaches Zero (Excluded)
        for snake_index in range(len(self.segments) - 1, 0, -1):
            x_pos = self.segments[snake_index - 1].xcor()
            y_pos = self.segments[snake_index - 1].ycor()
            self.segments[snake_index].goto(x_pos, y_pos)
        self.segments[0].forward(MOVEMENT_DISTANCE)

    def up(self):
        self.head.setheading(90)

    def down(self):
        self.head.setheading(270)

    def left(self):
        self.head.setheading(180)

    def right(self):
        self.head.setheading(0)


def setup_screen(screen):
    screen.bgcolor('black')
    screen.title('Snake Game')
    screen.setup(width=600, height=600)
    screen.tracer(0)


def start_game(screen, snake):
    setup_screen(screen)
    game_on = True
    while game_on:
        screen.update()
        time.sleep(0.1)
        snake.move()


def control_snake(screen, snake):
    screen.listen()
    screen.onkey(key='Up', fun=snake.up)
    screen.onkey(key='Down', fun=snake.down)
    screen.onkey(key='Left', fun=snake.left)
    screen.onkey(key='Right', fun=snake.right)
    screen.exitonclick()


def main():
    screen = Screen()
    snake = Snake()
    start_game(screen, snake)
    control_snake(screen, snake)


if __name__ == '__main__':
    main()

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

烙印 2025-02-18 12:55:24

这是调试时最小化代码的重要性的一个很好的例子。在此处考虑代码:

def start_game(screen, snake):
    game_on = True
    while game_on: # infinite loop
        screen.update()
        time.sleep(0.1)
        snake.move()

def control_snake(screen, snake):
    # add key listeners, the failing behavior

def main():
    # ...
    start_game(screen, snake)
    control_snake(screen, snake)

main 调用 start_game ,但是 start_game 在其中循环中具有无限 game_on 永远不会设置为 false ,因此 control_snake 永远不会达到。

尝试在之前添加关键的侦听器您进入无限渲染循环,而不是之后。

移动 control_snake start_game 引入了一个新问题,即 screen.exitonclick() control> control> control_snake 的一部分。 ,但是如果 control_snake start_game 之前调用,则 screet.exitonclick() blocks and blocks并预防 start_game 从运行中。因此,我们需要删除 screen.exitonClick()

但是,触发重复事件的方法比更好,而/ sleep screen.exitonclick()呼叫上阻止。 这篇文章显示了一个示例。


退后一步,这里还有一些其他提示,这些技巧解决了错误的误解和根本原因。

start_game 调用 setup_screen 有点奇怪。我会从 main 中调用 setup_screen 将其解除。我可以想象一个情况下,我们想设置一次屏幕,但是在蛇死后多次重新启动游戏。

通常,在您拥有基本代码工作之前,我不必担心将事情分解为功能。不要仅仅因为您听说过功能超过5或6行很差而写抽象。这些函数应该具有明确的奇异目的,并避免奇怪的依赖性。

例如, control_snake 应真正称为 add_snake_controls_then_block_until_exit 或类似的东西,因为它不仅添加了蛇控件(它并不是真正“控制蛇”注册执行此操作的控件,还可以阻止整个脚本并运行Turtle的内部更新循环,直到用户单击窗口。这听起来可能是ped的,但是如果您将此功能命名为准确说明其所做的事情,那么该错误将更加明显,并且通常会带来更清晰的代码的辅助益处。

您的游戏循环代码:

while game_on:
    screen.update()
    time.sleep(0.1)
    snake.move()

遵循有点混乱。通常的渲染顺序是:

  1. 更新位置
  2. rerender
  3. 睡眠/递送控制,直到下一个更新周期,

我建议更清楚的

while game_on:
    snake.move()    # update positions
    screen.update() # render the frame
    time.sleep(0.1) # defer/pause until the next tick

另一个提示/经验法则是在小块中工作,经常运行代码。看来您编写了大量代码,然后第一次运行它,不确定在哪里开始调试。如果我正在写蛇游戏,则我不必担心尾巴逻辑,直到我设置头并确定它有效。

如果您尽了最大的努力,请使用很多代码和错误,请系统地添加打印件,以查看达到控制权的位置。如果您在 control_snake 中添加了打印,您会发现它永远不会被调用,这几乎会散发出问题(因此是其解决方案)。

另一个调试策略是删除代码,直到问题消失,然后将最后一块返回,以确切查看问题是什么。

话虽如此,您的类似乎有目的且写得很好。

这是我的重写建议:

import turtle


class Snake:
    def __init__(self, grid_size, initial_x_positions):
        self.grid_size = grid_size
        self.create_snake(initial_x_positions)

    def create_snake(self, initial_x_positions):
        self.segments = []

        for x in initial_x_positions:
            segment = turtle.Turtle("square")
            segment.color("RoyalBlue")
            segment.penup()
            segment.goto(x, 0)
            self.segments.append(segment)

        self.head = self.segments[0]

    def move(self):
        for i in range(len(self.segments) - 1, 0, -1):
            x_pos = self.segments[i - 1].xcor()
            y_pos = self.segments[i - 1].ycor()
            self.segments[i].goto(x_pos, y_pos)

        self.head.forward(self.grid_size)

    def up(self):
        self.head.setheading(90)

    def down(self):
        self.head.setheading(270)

    def left(self):
        self.head.setheading(180)

    def right(self):
        self.head.setheading(0)


def create_screen():
    screen = turtle.Screen()
    screen.tracer(0)
    screen.bgcolor("black")
    screen.title("Snake Game")
    screen.setup(width=600, height=600)
    screen.listen()
    return screen


def main():
    initial_x_positions = 0, -20, -40
    frame_delay_ms = 80
    grid_size = 20

    screen = create_screen()
    snake = Snake(grid_size, initial_x_positions)
    screen.onkey(key="Up", fun=snake.up)
    screen.onkey(key="Down", fun=snake.down)
    screen.onkey(key="Left", fun=snake.left)
    screen.onkey(key="Right", fun=snake.right)

    def tick():
        snake.move()
        screen.update()
        turtle.ontimer(tick, frame_delay_ms)

    tick()
    screen.exitonclick()


if __name__ == "__main__":
    main()

由于没有重新启动条件或随附的逻辑,因此可能需要重新分配以允许“游戏上的游戏”屏幕并重置蛇或类似的东西,但至少没有稳固的必须提出推论的过早抽象。

This is a good example of the importance of minimizing the code when you debug. Consider the code here:

def start_game(screen, snake):
    game_on = True
    while game_on: # infinite loop
        screen.update()
        time.sleep(0.1)
        snake.move()

def control_snake(screen, snake):
    # add key listeners, the failing behavior

def main():
    # ...
    start_game(screen, snake)
    control_snake(screen, snake)

main calls start_game, but start_game has an infinite while loop in it. game_on is never set to False, and so control_snake will never be reached.

Try adding key listeners before you go into your infinite rendering loop, not after.

Moving control_snake ahead of start_game introduces a new problem, which is that screen.exitonclick() is part of control_snake, but if control_snake is called before start_game, then the screen.exitonclick() blocks and prevents start_game from running. So we need to remove screen.exitonclick().

But there's a better way to trigger repeated events than while/sleep, which is screen.ontimer. This lets you defer control back to your main loop and block on a screen.exitonclick() call. This post shows an example.


Taking a step back, here are a few other tips that address underlying misconceptions and root causes of your bugs.

It's a bit odd that setup_screen is called from start_game. I'd call setup_screen from main to decouple these. I can imagine a case where we want to set up the screen once, but restart the game multiple times, for example, after the snake dies.

In general, I'd worry less about breaking things out into functions until you have the basic code working. Don't write abstractions just because you've heard that functions longer than 5 or 6 lines are bad. The functions should have a clear, singular purpose foremost and avoid odd dependencies.

For example, control_snake should really be called add_snake_controls_then_block_until_exit or something like that, because not only does it add snake controls (it doesn't really "control the snake" exactly, it registers the controls that do so), it also blocks the whole script and runs turtle's internal update loop until the user clicks the window. This might sound pedantic, but if you'd named this function to state exactly what it does, the bug would be much more obvious, with the side benefit of clearer code in general.

Your game loop code:

while game_on:
    screen.update()
    time.sleep(0.1)
    snake.move()

is a bit confusing to follow. The usual rendering sequence is:

  1. update positions
  2. rerender
  3. sleep/defer control until the next update cycle

I suggest the clearer

while game_on:
    snake.move()    # update positions
    screen.update() # render the frame
    time.sleep(0.1) # defer/pause until the next tick

Another tip/rule of thumb is to work in small chunks, running your code often. It looks like you wrote a huge amount of code, then ran it for the first time and weren't sure where to begin debugging. If I were writing a snake game, I wouldn't worry about the tail logic until I've set up the head and established that it works, for example.

If you do wind up with a lot of code and a bug in spite of your best efforts, systematically add prints to see where control is reached. If you added a print in control_snake, you'd see it never gets called, which pretty much gives away the problem (and therefore its solution).

Another debugging strategy is to remove code until the problem goes away, then bring back the last chunk to see exactly what the problem was.

All that said, your Snake class seems purposeful and well-written.

Here's my rewrite suggestion:

import turtle


class Snake:
    def __init__(self, grid_size, initial_x_positions):
        self.grid_size = grid_size
        self.create_snake(initial_x_positions)

    def create_snake(self, initial_x_positions):
        self.segments = []

        for x in initial_x_positions:
            segment = turtle.Turtle("square")
            segment.color("RoyalBlue")
            segment.penup()
            segment.goto(x, 0)
            self.segments.append(segment)

        self.head = self.segments[0]

    def move(self):
        for i in range(len(self.segments) - 1, 0, -1):
            x_pos = self.segments[i - 1].xcor()
            y_pos = self.segments[i - 1].ycor()
            self.segments[i].goto(x_pos, y_pos)

        self.head.forward(self.grid_size)

    def up(self):
        self.head.setheading(90)

    def down(self):
        self.head.setheading(270)

    def left(self):
        self.head.setheading(180)

    def right(self):
        self.head.setheading(0)


def create_screen():
    screen = turtle.Screen()
    screen.tracer(0)
    screen.bgcolor("black")
    screen.title("Snake Game")
    screen.setup(width=600, height=600)
    screen.listen()
    return screen


def main():
    initial_x_positions = 0, -20, -40
    frame_delay_ms = 80
    grid_size = 20

    screen = create_screen()
    snake = Snake(grid_size, initial_x_positions)
    screen.onkey(key="Up", fun=snake.up)
    screen.onkey(key="Down", fun=snake.down)
    screen.onkey(key="Left", fun=snake.left)
    screen.onkey(key="Right", fun=snake.right)

    def tick():
        snake.move()
        screen.update()
        turtle.ontimer(tick, frame_delay_ms)

    tick()
    screen.exitonclick()


if __name__ == "__main__":
    main()

Since there's no restarting condition or accompanying logic, this will probably need to be refactored to allow for a "game over" screen and resetting the snake or something like that, but at least it's solid and there aren't a lot of premature abstractions to have to reason about.

晨曦慕雪 2025-02-18 12:55:24

我的工作如下

import turtle

STARTING_X_POSITIONS = [0, -20, -40]
MOVEMENT_DISTANCE = 20
frame_delay_ms = 80


class Snake:
    def __init__(self, screen):
        self.screen = screen
        self.control_snake()
        self.segments = []
        self.create_snake()
        self.head = self.segments[0]

    def create_snake(self):
        for i in range(3):
            new_snake = turtle.Turtle('square')
            new_snake.color('RoyalBlue')
            new_snake.penup()
            new_snake.goto(STARTING_X_POSITIONS[i], 0)
            self.segments.append(new_snake)

    def control_snake(self):
        self.screen.onkey(key='Up', fun=self.up)
        self.screen.onkey(key='Down', fun=self.down)
        self.screen.onkey(key='Left', fun=self.left)
        self.screen.onkey(key='Right', fun=self.right)
        self.screen.listen()

    def move(self):
        # We Want The Loop To Start At Index (2) And Decrease Itself Till It Reaches Zero (Excluded)
        for snake_index in range(len(self.segments) - 1, 0, -1):
            x_pos = self.segments[snake_index - 1].xcor()
            y_pos = self.segments[snake_index - 1].ycor()
            self.segments[snake_index].goto(x_pos, y_pos)
        self.segments[0].forward(MOVEMENT_DISTANCE)

    def up(self):
        self.head.setheading(90)

    def down(self):
        self.head.setheading(270)

    def left(self):
        self.head.setheading(180)

    def right(self):
        self.head.setheading(0)

class ScreenSetup:
    def __init__(self):
        self._screen = turtle.Screen()
        self.setup_screen()

    def setup_screen(self):
        self._screen.bgcolor('black')
        self._screen.title('Snake Game')
        self._screen.setup(width=600, height=600)
        self._screen.tracer(0)

    @property
    def screen(self):
        return self._screen


def run_snake(snake, screen):
    snake.move()
    screen.update()
    turtle.ontimer(lambda: run_snake(snake, screen), frame_delay_ms)


def main():
    screen = ScreenSetup().screen
    snake = Snake(screen)
    run_snake(snake, screen)
    screen.exitonclick()


if __name__ == '__main__':
    main()

I got it working as follows

import turtle

STARTING_X_POSITIONS = [0, -20, -40]
MOVEMENT_DISTANCE = 20
frame_delay_ms = 80


class Snake:
    def __init__(self, screen):
        self.screen = screen
        self.control_snake()
        self.segments = []
        self.create_snake()
        self.head = self.segments[0]

    def create_snake(self):
        for i in range(3):
            new_snake = turtle.Turtle('square')
            new_snake.color('RoyalBlue')
            new_snake.penup()
            new_snake.goto(STARTING_X_POSITIONS[i], 0)
            self.segments.append(new_snake)

    def control_snake(self):
        self.screen.onkey(key='Up', fun=self.up)
        self.screen.onkey(key='Down', fun=self.down)
        self.screen.onkey(key='Left', fun=self.left)
        self.screen.onkey(key='Right', fun=self.right)
        self.screen.listen()

    def move(self):
        # We Want The Loop To Start At Index (2) And Decrease Itself Till It Reaches Zero (Excluded)
        for snake_index in range(len(self.segments) - 1, 0, -1):
            x_pos = self.segments[snake_index - 1].xcor()
            y_pos = self.segments[snake_index - 1].ycor()
            self.segments[snake_index].goto(x_pos, y_pos)
        self.segments[0].forward(MOVEMENT_DISTANCE)

    def up(self):
        self.head.setheading(90)

    def down(self):
        self.head.setheading(270)

    def left(self):
        self.head.setheading(180)

    def right(self):
        self.head.setheading(0)

class ScreenSetup:
    def __init__(self):
        self._screen = turtle.Screen()
        self.setup_screen()

    def setup_screen(self):
        self._screen.bgcolor('black')
        self._screen.title('Snake Game')
        self._screen.setup(width=600, height=600)
        self._screen.tracer(0)

    @property
    def screen(self):
        return self._screen


def run_snake(snake, screen):
    snake.move()
    screen.update()
    turtle.ontimer(lambda: run_snake(snake, screen), frame_delay_ms)


def main():
    screen = ScreenSetup().screen
    snake = Snake(screen)
    run_snake(snake, screen)
    screen.exitonclick()


if __name__ == '__main__':
    main()

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文