我一直在制作蛇游戏,但是当我运行它的时候,它的表现就像它应该做的那样,但是没有显示的tkinter窗口。当我删除'while‘循环时,它会显示窗口,但是没有任何东西在里面,因为可视化在循环中。我使用pycharm pro 2021.3.2和python 3.9。这是我的代码:
from tkinter import *
import random as rm
import time
import keyboard as kb
# Creating the window and the canvas
root = Tk()
root.geometry("390x390")
root.resizable(False, False)
c = Canvas(root, width=390, height=390, bg="white")
c.pack()
#Creating lists of all possible x and y coordinates
board = []
for i in range(39):
for j in range(39):
board.append([i, j])
# Creating the Snake class
class Snake:
def __init__(self):
# Initializating:
self.head_cords = [20, 20] # A list that contains the x and the y coordinate of the Snake head
self.body_cords = [[17, 20], [18, 20], [19, 20]] # A list with smaller lists with coordinates of each Snake block
self.direction = "right" # The directions the Snake is facing
self.lenght = 4 # The Snake length (Not the game score, that is the number of eaten Apples, but the number of the Snake blocks + 1 for the head)
def move(self):
# Creating the movement function
# Movineg the body
self.body_cords.pop(0)
self.body_cords.append(self.head_cords)
# Moving the head according to the Snake direction
if self.direction == "up":
self.head_cords[1] = self.head_cords[1] - 1
if self.direction == "down":
self.head_cords[1] = self.head_cords[1] + 1
if self.direction == "left":
self.head_cords[0] = self.head_cords[0] - 1
if self.direction == "right":
self.head_cords[0] = self.head_cords[0] + 1
def check_dead(self):
# Creating the function to check if the Snake is dead basing on 2 conditions:
# 1: If the Snake hits the wall
if self.head_cords[0] > 39 or self.head_cords[0] < 1 or self.head_cords[1] > 39 or self.head_cords[1] < 1:
return True
# 2: If the Snake hits itself
elif self.head_cords in self.body_cords:
return True
else:
return False
def grow(self):
# Creating the growth function (it will be called if the Snake eats an Apple)
self.body_cords.insert(0, [self.body_cords[0][0], self.body_cords[0][1]])
Jordan = Snake() # Creating an instance of the Snake class
# Creating the Apple class
class Apple:
def __init__(self):
# Initializating the x and the y coordinates
# To place the Apple somewhere on the board, we should check which cells are already occupied by the Snake
snake_positions = []
for pos in Jordan.body_cords:
snake_positions.append(pos)
snake_positions.append(Jordan.head_cords)
# Then we choose which cells are not occupied by the Snake
possinle_positions = []
for pos in board:
if pos in snake_positions:
pass
else:
possinle_positions.append(pos)
# And choose one of them randomly
rm.choice(possinle_positions)
self.pos = rm.choice(possinle_positions)
def check_eaten(self):
# Creating the function to check if the Apple it eaten by the Snake
if Jordan.head_cords == self.pos:
return True
else:
return False
def __del__(self):
# Creating the Apple delete function
print("+1 Apple")
nessy = Apple() # Creating an instance of the Apple class
# Creating the visualization function
def visualizate():
# Clearing everything
c.create_rectangle(0, 0, 390, 390, fill="white")
# Visualizing the Snake
# Head
c.create_rectangle(Jordan.head_cords[0]*10-9, Jordan.head_cords[0]*10, Jordan.head_cords[1]*10-9, Jordan.head_cords[1]*10,
fill="yellow", outline="black", width=2)
# Body
for block in Jordan.body_cords:
c.create_rectangle(block[0]*10-9, block[0]*10, block[1]*10-9, block[1]*10, fill="yellow", outline="white", width=2)
# Visualizing the Apple
c.create_rectangle(nessy.pos[0]*10-9, nessy.pos[0]*10, nessy.pos[1]*10-9, nessy.pos[1]*10, fill="red", outline="black", width=1)
# Creating the contlors
# If you are from stackoverflow - ignore the controls. They do nothing now
def change_direction(id):
if id == 1:
Jordan.direction = "up"
if id == 2:
Jordan.direction = "down"
if id == 3:
Jordan.direction = "left"
if id == 4:
Jordan.direction = "right"
kb.add_hotkey("up", change_direction(1))
kb.add_hotkey("down", change_direction(2))
kb.add_hotkey("left", change_direction(3))
kb.add_hotkey("right", change_direction(4))
# The game cycle
while True:
visualizate() # Visualizing what we have
# Checking if the Snake is dead
if Jordan.check_dead():
time.sleep(5)
root.destroy() #Closing
print("You died")
break
# Checking if the Apple is eaten
if nessy.check_eaten():
del nessy
Jordan.grow()
nessy = Apple()
Jordan.move()
print("move")
time.sleep(1)
root.mainloop()
发布于 2022-08-16 10:56:34
root.mainloop()
是一个特殊的循环,它从系统中获取键/鼠标事件,将它们发送到小部件、更新小部件和(重新)在屏幕上绘制所有内容。但是您的while True
永远运行,所以它不能运行mainloop()
。您可能必须在分隔的thread
中运行它循环。或者在循环内部,您必须使用root.update()
来允许tkinter
执行一个循环(和更新窗口)。或者您应该使用root.after(milliseconds, function)
而不是while
、-loop和sleep()
来周期性地执行一个循环。
tkinter
可以bind()
键到函数,它不需要模块keyboard
(BTW:在我的Linux模块上,keyboard
需要作为root (keyboard
)运行)
如果要使用change_direction("up")
而不是change_direction(1)
,那么可以将函数简化为def change_direction(direction): jordan.direction = direction
。但就我而言,add_hotkey()
可能需要没有()
和没有参数的函数名称--也就是所谓的"callback"
--或者您必须使用lambda
来创建回调kb.add_hotkey("up", lambda:change_direction("up"))
您在create_rectangle
中使用了错误的值,它会创建大小不同的大矩形。您可以使用create_rectangle
创建白色矩形以删除先前的元素,但它不会从画布中删除先前的矩形,而是只将它们隐藏在白色矩形后面--在某个时刻,您可能有数百个隐藏的矩形--您应该使用canvas.delete('all')
。或者您应该保留对象的ID -- apple_id = c.create_rectange(...)
--然后将对象移动到新位置c.move(apple_id, ...)
最后一个问题:当你移动时,你把头加到身体上,但它是带有to元素的列表,当它附加到其他列表时,它不重复列表,但是它只发送引用,然后当你改变头部的位置,它在正文中也改变位置-它需要使用.copy()
来追加重复的元素self.body_cords.append(self.head_cords.copy())
编辑:i还将函数change_direction()
移到Snake.change_direction()
中
编辑: ==
提供True
或False
,因此在check_eaten()
中可以使用单行return (jordan.head_cords == self.pos)
而不使用if/else
编写更短的代码
这段代码适用于我
import tkinter as tk # PE8: `import *` is not preferred
import random
import time
# --- classes --- # PEP8: all classes directly after imports
# Creating the Snake class
class Snake:
def __init__(self):
# Initializating:
self.head_cords = [20, 20] # A list that contains the x and the y coordinate of the Snake head
self.body_cords = [[17, 20], [18, 20], [19, 20]] # A list with smaller lists with coordinates of each Snake block
self.direction = "right" # The directions the Snake is facing
self.lenght = 4 # The Snake length (Not the game score, that is the number of eaten Apples, but the number of the Snake blocks + 1 for the head)
def move(self):
# Creating the movement function
# Movineg the body
self.body_cords.pop(0)
self.body_cords.append(self.head_cords.copy())
# Moving the head according to the Snake direction
if self.direction == "up":
self.head_cords[1] -= 1 # shorter with `-=` / `+=`
if self.direction == "down":
self.head_cords[1] += 1 # shorter with `-=` / `+=`
if self.direction == "left":
self.head_cords[0] -= 1 # shorter with `-=` / `+=`
if self.direction == "right":
self.head_cords[0] += 1 # shorter with `-=` / `+=`
def check_dead(self):
# Creating the function to check if the Snake is dead basing on 2 conditions:
# 1: If the Snake hits the wall
if self.head_cords[0] > 39 or self.head_cords[0] < 1 or self.head_cords[1] > 39 or self.head_cords[1] < 1:
print('collide wall')
return True
# 2: If the Snake hits itself
elif self.head_cords in self.body_cords:
print('collide itself')
return True
else:
return False
def grow(self):
# Creating the growth function (it will be called if the Snake eats an Apple)
self.body_cords.insert(0, [self.body_cords[0][0], self.body_cords[0][1]])
def change_direction(self, direction):
self.direction = direction
# Creating the Apple class
class Apple:
def __init__(self):
# Initializating the x and the y coordinates
# To place the Apple somewhere on the board, we should check which cells are already occupied by the Snake
snake_positions = []
for pos in jordan.body_cords:
snake_positions.append(pos)
snake_positions.append(jordan.head_cords)
# Then we choose which cells are not occupied by the Snake
possinle_positions = []
for pos in board:
if pos not in snake_positions:
possinle_positions.append(pos)
# And choose one of them randomly
self.pos = random.choice(possinle_positions)
def check_eaten(self):
# Creating the function to check if the Apple it eaten by the Snake
#if jordan.head_cords == self.pos:
# return True
#else:
# return False
return (jordan.head_cords == self.pos) # `==` gives `True` or `False`
def __del__(self):
# Creating the Apple delete function
print("+1 Apple")
# --- functions --- # PEP8: all functions directly after classes
# Creating the visualization function
def visualizate():
# Clearing everything
#c.create_rectangle(0, 0, 390, 390, fill="white")
c.delete('all')
# Visualizing the Snake
# Head
x = jordan.head_cords[0]*10
y = jordan.head_cords[1]*10
c.create_rectangle(x-9, y-9, x, y, fill="green", outline="black", width=2)
# Body
for block in jordan.body_cords:
x = block[0]*10
y = block[1]*10
c.create_rectangle(x-9, y-9, x, y, fill="yellow", outline="white", width=2)
# Visualizing the Apple
x = nessy.pos[0]*10
y = nessy.pos[1]*10
c.create_rectangle(x-9, y-9, x, y, fill="red", outline="black", width=1)
# --- main ---
# Creating the window and the canvas
root = tk.Tk()
root.geometry("390x390")
root.resizable(False, False)
c = tk.Canvas(root, width=390, height=390, bg="white")
c.pack()
# Creating lists of all possible x and y coordinates
board = []
for i in range(39):
for j in range(39):
board.append([i, j])
jordan = Snake() # Creating an instance of the Snake class # PEP8: `CamelCaseNames` only for classes - it helps to recognize class in code
nessy = Apple() # Creating an instance of the Apple class
root.bind("<Up>", lambda event:jordan.change_direction("up"))
root.bind("<Down>", lambda event:jordan.change_direction("down"))
root.bind("<Left>", lambda event:jordan.change_direction("left"))
root.bind("<Right>", lambda event:jordan.change_direction("right"))
# The game cycle
while True:
visualizate() # Visualizing what we have
print('check snake')
# Checking if the Snake is dead
if jordan.check_dead():
print("You died")
root.update()
time.sleep(5)
root.destroy() # Closing
break
print('check apple')
# Checking if the Apple is eaten
if nessy.check_eaten():
del nessy
jordan.grow()
nessy = Apple()
print("move")
jordan.move()
root.update() # allow `mainloop()` to update window
time.sleep(0.5)
#root.mainloop() # no need if you use `root.update()`
https://stackoverflow.com/questions/73365612
复制相似问题