到目前为止,我的蛇游戏还不错.一个问题是,出于某种原因," turtle "对击键没有react ,我真的不知道为什么.我试了很多不同的东西,但都没用.主要问题是,我不完全确定主要问题在哪里.我可以肯定的是,问题很可能来self 的代码,但我似乎找不到它.如果你能帮我解决这个问题,那太好了.

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

推荐答案

这是一个很好的例子,说明了在调试时最小化代码的重要性.考虑以下代码:

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中有一个无限while循环.game_on永远不会被设置为False,因此control_snake永远不会被设置为False.

try 将关键侦听器before添加到无限渲染循环中,而不是之后.

control_snake移到start_game之前会引入一个新问题,即screen.exitonclick()control_snake的一部分,但如果在start_game之前调用control_snake,则screen.exitonclick()会阻止start_game运行.所以我们需要go 掉screen.exitonclick().

但有一种更好的方法可以触发重复事件,而不是while/sleep,即screen.ontimer.这使您可以将控制延迟回主循环,并阻止screen.exitonclick()调用.This post显示了一个示例.


退一步讲,这里有一些其他技巧,可以解决潜在的误解和bug的根源.

start_game中调用setup_screen有点奇怪.我会从mainsetup_screen来解耦这些.我可以想象这样一种情况:我们只想设置一次屏幕,但要多次重新启动游戏,例如,在蛇死后.

一般来说,在基本代码正常工作之前,我不太担心将事情分解成函数.不要仅仅因为听说函数长度超过5或6行就写摘要.这些函数首先应该有一个明确、单一的目的,并避免奇怪的依赖关系.

例如,control_snake实际上应该被称为add_snake_controls_then_block_until_exit或类似的名称,因为它不仅添加了snake控件(它实际上并不是"控制蛇",而是注册了这样做的控件),它还阻止了整个脚本,并运行turtle的内部更新循环,直到用户单击窗口.这听起来可能有点迂腐,但如果您将这个函数命名为确切地说明它的功能,那么这个错误就会更加明显,其附带的好处是代码通常更清晰.

您的游戏循环代码:

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

这有点让人困惑.通常的渲染顺序是:

  1. 更新职位
  2. 复卷机
  3. Hibernate /延迟控制,直到下一个更新周期

我建议更清楚

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

另一个技巧/经验法则是分批工作,经常运行代码.看起来您编写了大量代码,然后第一次运行它,不知道从哪里开始调试.例如,如果我正在编写一个蛇游戏,我不会担心尾部逻辑,直到我设置了头部并确定它可以工作.

如果你尽了最大努力,最终还是得到了很多代码和一个bug,那么系统地添加打印以查看控制的位置.如果你在control_snake中添加了一个print,你会发现它永远不会被调用,这几乎expose 了问题(以及它的解决方案).

另一种调试策略是删除代码,直到问题消失,然后带回最后一块代码,以查看问题到底是什么.

综上所述,你的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()

由于没有重新启动条件或伴随的逻辑,可能需要对其进行重构,以允许"游戏结束"屏幕并重置snake或类似的东西,但至少它是可靠的,并且没有太多不成熟的抽象需要推理.

Python相关问答推荐

使用numpy提取数据块

为什么我的Python代码在if-else声明中的行之前执行if-else声明中的行?

在Python中处理大量CSV文件中的数据

重新匹配{ }中包含的文本,其中文本可能包含{{var}

如何在Python中将returns.context. DeliverresContext与Deliverc函数一起使用?

未删除映射表的行

如何列举Pandigital Prime Set

如何在Python数据框架中加速序列的符号化

如何使用pytest来查看Python中是否存在class attribution属性?

将JSON对象转换为Dataframe

joblib:无法从父目录的另一个子文件夹加载转储模型

考虑到同一天和前2天的前2个数值,如何估算电力时间序列数据中的缺失值?

python panda ExcelWriter切换动态公式到数组公式

在numpy数组中寻找楼梯状 struct

在Django中重命名我的表后,旧表中的项目不会被移动或删除

没有内置pip模块的Python3.11--S在做什么?

浏览超过10k页获取数据,解析:欧洲搜索服务:从欧盟站点收集机会的微小刮刀&

Django抛出重复的键值违反唯一约束错误

按列表分组到新列中

以元组为索引的Numpy多维索引