Python 3.4 Tkinter - 滞后

Python 3.4 Tkinter - lagging

我正在为我在学校的 python class 做这个吃豆人游戏项目。一切都很顺利,但是......昨天,当我完成一些最终功能时,我的游戏开始变得非常糟糕。我想问题出在主循环中,但我找不到解决方案...这是一些代码:

from tkinter import *
import time
import threading
from PIL import Image, ImageTk
import random

points = []
matrix = []

class 游戏:

def __init__(self):
    self.tk = Tk()
    self.tk.title("Pac-man")
    self.canvas = Canvas(self.tk, width = 800, height = 700, bg = 'black')
    self.Pac = Pacman(self.canvas, 'yellow')
    self.Blinky = Ghost(self.canvas, 'red')
    self.score = 0
    self.scoreid = Label(self.tk,
                    text = "SCORE: {0}".format(self.score), bg = 'yellow', fg = 'black')
    self.scoreid.pack()
    for wall in outerWall: 
        self.drawWall(wall[0], wall[1], wall[2], wall[3])
        self.drawWall(800 - wall[0], wall[1], 800 - wall[2], wall[3])

    for wall in WierdWalls: 
        self.drawWall(wall[0], wall[1], wall[2], wall[3])
        self.drawWall(800 - wall[0], wall[1], 800 - wall[2], wall[3])

    for wall in BaseOfGhost:
        self.drawWall(wall[0], wall[1], wall[2], wall[3])
        self.drawWall(800 - wall[0], wall[1], 800 - wall[2], wall[3])
    self.canvas.create_line(350, 270, 450, 270, width = 5, fill = 'yellow')

    for wall in squareWalls: 
        self.drawSquareWall(wall[0], wall[1], wall[2], wall[3])
        self.drawSquareWall(800 - wall[0], wall[1], 800 - wall[2], wall[3])

    self.drawPoints()
    self.Pac.draw()
    self.canvas.pack()
    self.tk.update
    self.running = True

def drawWall(self, x0, y0, x1, y1, width = 3, color = 'blue'):
    self.canvas.create_line(x0, y0, x1, y1, width = width, fill = color)

def drawSquareWall(self, x0, y0, x1, y1, width = 2, color = 'black'):
    self.canvas.create_rectangle(x0, y0, x1, y1, fill = color)
    self.canvas.create_line(x0, y0, x1, y0, width = width, fill = 'blue')
    self.canvas.create_line(x1, y0, x1, y1, width = width, fill = 'blue')
    self.canvas.create_line(x1, y1, x0, y1, width = width, fill = 'blue')
    self.canvas.create_line(x0, y1, x0, y0, width = width, fill = 'blue')

def drawPoints(self):
    self.BuildHorizontalPath(40, 40, 13, 26)
    self.BuildVerticalPath(40, 40, 9, 16)
    self.BuildHorizontalPath(66, 168, 4, 26)
    self.BuildHorizontalPath(66, 104, 4, 26)
    self.BuildVerticalPath(170, 56, 8, 16)
    self.BuildVerticalPath(170, 192, 18, 24)
    self.BuildHorizontalPath(196, 104, 8, 26)
    self.BuildVerticalPath(352, 56, 3, 16)
    self.BuildHorizontalPath(40, 456, 5, 26)
    self.BuildHorizontalPath(40, 600, 5, 26)
    self.BuildVerticalPath(40, 472, 4, 16)
    self.BuildHorizontalPath(66, 520, 2, 26)
    self.BuildVerticalPath(92, 536, 4, 16 )
    self.BuildVerticalPath(40, 600, 4, 18)
    self.BuildHorizontalPath(66, 654, 12, 27)
    self.BuildHorizontalPath(200, 527, 7, 27)
    self.BuildHorizontalPath(200, 456, 7, 27)
    self.BuildVerticalPath (362, 472, 3, 18)
    self.BuildVerticalPath (274, 135, 3, 20)
    self.BuildHorizontalPath (292, 175, 4, 18)
    self.BuildVerticalPath (254, 550, 3, 20)
    self.BuildHorizontalPath (274, 591, 5, 22)
    self.BuildVerticalPath (363, 611, 2, 20)

def BuildHorizontalPath(self, val1, val2, number, increment = 25, delta = 8):
    x, y = val1, val2
    itemid1 = 0
    itemid2 = 0
    for i in range(number):
        deltax = x + delta
        deltay = y + delta
        itemid1 = self.canvas.create_oval(x, y, deltax, deltay, fill = 'white')
        itemid2 = self.canvas.create_oval(800-x, y, 800-deltax, deltay, fill = 'white')
        points.append([(deltax + x)/2, (deltay + y)/2, itemid1])
        points.append([800-(deltax + x)/2, (deltay+y)/2, itemid2])
        x += increment

def BuildVerticalPath(self, val1, val2, number, increment = 30, delta = 8):
    itemid1 = 0
    itemid2 = 0
    for i in range(number):
        x, y = val1, val2
        deltax = x + delta
        deltay = y + delta
        itemid1 = self.canvas.create_oval(x, y, deltax, deltay, fill = 'white')
        itemid2 = self.canvas.create_oval(800-x, y, 800-deltax, deltay, 
            fill = 'white')
        points.append([(deltax + x)/2, (deltay + y)/2, itemid1])
        points.append([800-(deltax + x)/2, (deltay + y)/2, itemid2])
        val2 += increment

def mainloop(self):
    while 1:
        if self.running:
            first_moment = round(time.time())
            if not self.Pac.hitwall():
                self.Pac.canvas.move(self.Pac.id, self.Pac.x, self.Pac.y)
            if self.Pac.feed():
                self.score = self.score + 10
                self.scoreid.config(text = "SCORE: {0}".format(self.score))
            if self.Pac.first_teleport():
                self.Pac.canvas.move(self.Pac.id, 760, 0)

            if self.Pac.second_teleport():
                self.Pac.canvas.move(self.Pac.id, -760, 0)

            if not self.Blinky.hitwall():
                self.Blinky.canvas.move(self.Blinky.id, self.Blinky.x, self.Blinky.y)
            if self.Blinky.hitwall():
                self.Blinky.change_direction()

            last_moment = round(time.time())
            self.tk.update_idletasks()
            self.tk.update()
            s = 0.01 - (last_moment - first_moment)
            if s >= 0:
                time.sleep(s)

class吃豆子:

def __init__(self, canvas, color):
    self.canvas = canvas
    self.id = canvas.create_oval(10, 10, 35, 35, fill = color)
    self.canvas.move(self.id, 400, 375)
    self.x = 0
    self.y = 0
    self.canvas.bind_all('<KeyPress-Left>', self.turn_left)
    self.canvas.bind_all('<KeyPress-Right>', self.turn_right)
    self.canvas.bind_all('<KeyPress-Up>', self.go_up)
    self.canvas.bind_all('<KeyPress-Down>', self.go_down)
    self.canvas.bind_all('<KeyRelease>', self.stop)
    #self.canvas.bind_all(self.hitwall, self.stop)

def feed(self):
    pos = self.canvas.coords(self.id)
    for point in points:
        if  point[0] >= pos[0] and point[0] <= pos[2]:
            if point[1] >= pos[1] and point[1] <= pos[3]:
                self.canvas.delete(point[2])
                points.remove(point)
                return True
        if  point[1] >= pos[1] and point[1] <= pos[3]:
            if point[0] >= pos[0] and point[0] <= pos[2]:
                self.canvas.delete(point[2])
                points.remove(point)
                return True
    return False

def stop(self, event):
    self.x = 0
    self.y = 0

def turn_left(self, event):
    self.x = -2

def turn_right(self, event):
    self.x = 2

def go_up(self, event):
    self.y = -2

def go_down(self, event):
    self.y = 2

def draw(self):
    self.canvas.move(self.id, self.x, self.y)

def hitwall(self):
    pos = self.canvas.coords(self.id)
    pos[0] += self.x
    pos[1] += self.y
    pos[2] += self.x
    pos[3] += self.y
    for wall in squareWalls:
        if pos[1] in range(wall[1], wall[3]):
            if pos[0] in range(wall[0], wall[2]):
                return True
            if pos[2] in range(wall[0], wall[2]):
                return True
        elif pos[3] in range(wall[1], wall[3]):
            if pos[0] in range(wall[0], wall[2]):
                return True
            if pos[2] in range(wall[0], wall[2]):
                return True
        if pos[1] in range(wall[1], wall[3]):
            if pos[0] >= 800 - wall[2] and pos[0]<= 800 - wall[0]:
                return True
            if pos[2] >= 800 - wall[2] and pos[2]<= 800 - wall[0]:
                return True
        elif pos[3] in range(wall[1], wall[3]):
            if pos[0] >= 800 - wall[2] and pos[0]<= 800 - wall[0]:
                return True
            if pos[2] >= 800 - wall[2] and pos[2]<= 800 - wall[0]:
                return True

    for wall in outerWall:
        if pos[1] > 90 and pos[3] < 680:

            if pos[0] <= wall[0] and pos[0] <= wall[2]:
                if pos[1] in range(wall[1], wall[3]):
                    return True

            if pos[2] <= wall[0] and pos[2] <=wall[2]:
                if pos[3] in range(wall[1], wall[3]):
                    return True

        if pos[3] > 680 or pos[1] < 20:
            return True

        if pos[1] > 20 and pos[1] < 90:
            if pos[0] < 20:
                return True
            if pos[2] > 780:
                return True
            if pos[2] > 385 and pos[2] < 415:
                return True
            if pos[0] > 385 and pos[0] < 415:
                return True

        if pos[1] > 90 and pos[3] < 680:

            if pos[0] >= 800 - wall[0] and pos[0] >= 800 - wall[2]:
                if pos[1] in range(wall[1], wall[3]):
                    return True

            if pos[2] >= 800 - wall[0] and pos[2] >= 800 - wall[2]:
                if pos[3] in range(wall[1], wall[3]):
                    return True

    for wall in list_of_walls:
        if pos[1] in range(wall[1], wall[3]):
            if pos[0] in range(wall[0], wall[2]):
                return True
            if pos[2] in range(wall[0], wall[2]):
                return True
        elif pos[3] in range(wall[1], wall[3]):
            if pos[0] in range(wall[0], wall[2]):
                return True
            if pos[2] in range(wall[0], wall[2]):
                return True
        if pos[1] in range(wall[1], wall[3]):
            if pos[0] >= 800 - wall[2] and pos[0]<= 800 - wall[0]:
                return True
            if pos[2] >= 800 - wall[2] and pos[2]<= 800 - wall[0]:
                return True
        elif pos[3] in range(wall[1], wall[3]):
            if pos[0] >= 800 - wall[2] and pos[0]<= 800 - wall[0]:
                return True
            if pos[2] >= 800 - wall[2] and pos[2]<= 800 - wall[0]:
                return True

    return False 

def first_teleport(self):
    pos = self.canvas.coords(self.id)
    if pos[1] > 290 and pos[1] < 350:
        if pos[0] < 0:
            return True
    return False

def second_teleport(self):
    pos = self.canvas.coords(self.id)
    if pos[3] > 290 and pos[3] < 350:
        if pos[2] > 800:
            return True
    return False

class 菜单():

def __init__(self):
    self.m = Tk()
    self.m.title("Pac-man")
    image1 = Image.open("/home/tano/The_Pac-man_game/pacman.jpg")
    photo = ImageTk.PhotoImage(image1)
    label1 = Label(self.m, image = photo )
    label1.image = photo
    label1.pack()
    play_button = Button(self.m,text="Play", command = self.Play).pack()
    exit_button = Button(self.m,text="Exit", command = self.Exit).pack()

def Play(self):
    self.m.destroy()
    g = Game()
    g.mainloop()

def Exit(self):
    self.m.destroy()

def mainloop(self):
    while 1:
        self.m.update_idletasks()
        self.m.update()
        time.sleep(0.01)

class幽灵(吃豆人):

def __init__(self, canvas, color):
    self.canvas = canvas
    self.id = canvas.create_oval(10, 10, 35, 35, fill = color)
    self.canvas.move(self.id, 350, 220)
    self.x = -2
    self.y = 0

def movement(self):
    if self.x > 0:
        self.turn_right
    if self.x < 0:
        self.turn_left
    if self.y > 0:
        self.go_down
    if self.y < 0:
        self.go_up

def change_direction(self):
    z = random.randrange(1, 4)
    if z == 1:
        self.x = 0
        self.y = 2
    elif z == 2:
        self.x = 2
        self.y = 0
    elif z == 3:
        self.x = 0
        self.y = -2
    elif z == 4:
        self.x = -2
        self.y = 0



outerWall = [ [5, 5, 5, 200], [5, 5, 400, 5], [20, 20, 20, 200],
[20, 20, 385, 20], [5, 200, 5, 215], [20, 200, 150, 200], [5, 215, 135, 215], [135, 215, 135, 275],[150, 200, 150, 290], [135, 215, 135, 275], [5, 290, 150, 290], [5, 275, 135, 275], [5, 350, 150, 350], [5, 365, 135, 365],
[150, 350, 150, 440] , [135, 365, 135, 425], [20, 440, 150, 440], [5, 425, 135, 425], [5, 425, 5, 695], [20, 440, 20, 545], [20, 575, 20, 680], [20, 545, 60, 545], [20, 575, 60, 575], [60, 545, 60, 575], [5, 695, 795, 695], [385, 20, 385, 90], [385, 90, 400, 90], [20, 680, 780, 680]]

squareWalls = [[60, 60, 150, 90], [200, 60, 340, 90], [60, 130, 150, 150], [200, 350, 240, 440], [200, 490, 340, 510]]

BaseOfGhost = [[300, 270, 300, 370], [310, 280, 310, 360], [300, 270, 350, 270], [300, 370, 400, 370], [310, 360, 400, 360], [310, 280, 350, 280],[350, 280, 350, 270] ]

WierdWalls = [[300, 140, 500, 140], [500, 140, 500, 160], [500, 160, 410, 160],[300, 140, 300, 160], [300, 160, 390, 160], [390, 160, 390, 220], [410, 160, 410, 220], [390, 220, 410 , 220], [560, 140, 560, 200], [600, 140, 600, 290], [560, 200, 480, 200], [480, 200, 480, 220], [480, 220, 560, 220], [560, 220, 560, 290], [560, 290, 600, 290], [560, 140, 600, 140], [150, 480, 150, 575], [150, 480, 60, 480], [60, 480, 60, 500], [60, 500, 130, 500], [130, 500, 130, 575], [130, 575, 150, 575], [300, 440, 390, 440 ], [300, 440, 300, 420], [300, 420, 500, 420], [390, 440, 390, 510], [390, 510, 410, 510],[300, 580, 390, 580 ], [300, 580, 300, 550], [300, 550, 500, 550], [390, 580, 390, 640], [390, 640, 410, 640], [60, 640, 340, 640], [60, 640, 60, 620], [60, 620, 200, 620], [200, 620, 200, 550], [200, 550, 220, 550,],[220, 550, 220, 620], [220, 620, 340, 620], [340, 620, 340, 640] ] 

list_of_walls = [[300,140,500,160],[390,160,410,220], [560,140,600,290],[560,200,480,220], [240,200,320,220],[240,140,200,290],[150,480,130,575], [650,480,670,575], [150,480,60,500], [650,480,740,500], [300,420,500,440], [390,440,410,510], [300,550,500,580], [390,580,410,640], [60,620,340,640], [460,620,740,640], [200,550,220,620], [580,550,600, 620], [300, 270, 400, 370]]

m = Menu()
m.mainloop()

我担心的是这个函数 hitwall() 是否太大,因为到目前为止我在这个主循环中使用了它两次,我还需要两倍... 如果有人帮我找到解决这个问题的方法,我会很高兴,因为这个项目对我来说非常重要。

在游戏初始化时,您可以进行一些预处理并在逻辑上将游戏场地划分为象限,然后为每个象限构建一个列表,其中仅包含完全或部分位于该区域的墙。

有了这样的列表,您可以通过只查看 Pacman 当前所在区域对应的墙来加快 hitwall() 的速度(并忽略其他区域列表中的那些).这将 显着 减少所需的比较次数——如果墙壁是随机分布的并且 Pacman 完全只在一个象限内,则减少多达 75%。

sleep 的调用导致您的程序滞后。可能还有其他问题,但这绝对是其中之一。你不应该有自己的 mainloop 方法,你应该使用内置的 mainloop 函数。

您可以通过从 mainloop 中删除 while 语句,删除睡眠语句以及对 updateupdate_idletasks 的调用,然后让它使用 [=18] =] 命令使自己每隔几毫秒 运行。

看起来像这样:

def animate(self):

    <all of the code for updating the display>

    self.after(30, self.animate)

完整版如下:

def animate(self):
    if self.running:
        if not self.Pac.hitwall():
            self.Pac.canvas.move(self.Pac.id, self.Pac.x, self.Pac.y)
        if self.Pac.feed():
            self.score = self.score + 10
            self.scoreid.config(text = "SCORE: {0}".format(self.score))
        if self.Pac.first_teleport():
            self.Pac.canvas.move(self.Pac.id, 760, 0)

        if self.Pac.second_teleport():
            self.Pac.canvas.move(self.Pac.id, -760, 0)

        if not self.Blinky.hitwall():
            self.Blinky.canvas.move(self.Blinky.id, self.Blinky.x, self.Blinky.y)
        if self.Blinky.hitwall():
            self.Blinky.change_direction()

        self.after(30, self.animate)

您可以在一些地方使用 else 来减少计算量。例如,如果 hitwall() returns True,可能不需要也调用 feed(),对吗?而且,如果吃豆人击中了第一个传送点,则无需检查它是否在第二个传送点中。通过正确使用 else,您可能可以将计算量减少三分之一或更多。

此外,您在每次迭代期间调用 self.Blinky.hitwall() 两次。你不应该只调用一次吗?