乌龟程序在一段时间后变慢。尝试清除屏幕并删除但没有效果

时间:2019-09-25 15:37:52

标签: python-2.7 memory-management turtle-graphics

# set up the environment
wn = turtle.Screen()
wn.bgpic("space_invaders_background_new.gif")
turtle.fd(0)
turtle.speed(0)
turtle.bgcolor("black")
turtle.ht()
turtle.setundobuffer(None)
turtle.tracer(20,0)

# draw white boundaries around
class Game():   
    def draw_border(self):
        #Draw border
        self.pen = turtle.Turtle()
        self.pen.speed(0)
        self.pen.color("white")
        self.pen.pensize(0)
        self.pen.penup()
        self.pen.goto(-400, 400)
        self.pen.pendown()
        for side in range(4):
            self.pen.fd(800)
            self.pen.rt(90)
        self.pen.penup()
        self.pen.ht()

game = Game()
game.draw_border()

# player 1 and his actions

class Bullet(turtle.Turtle):
    def __init__(self):
        turtle.Turtle.__init__(self)
        self.hideturtle()
        self.shape("arrow")
        self.penup()
        self.speed(0)
        self.color("white")
        self.fd(0)
        self.goto(0, 0)

# player class
class Player(turtle.Turtle):
    def __init__(self, spriteshape, color, startx, starty):
        turtle.Turtle.__init__(self, shape = spriteshape)
        self.hideturtle()
        self.speed(0)
        self.penup()
        self.color(color)
        self.fd(0)
        self.goto(startx, starty)
        self.left(90)
        self.showturtle()

    def move(self):
        self.fd(10)
    def turn_left(self):
        self.lt(20)
    def turn_right(self):
        self.rt(20)
    def accelerate(self):
        self.move()

# Define Enemy1 class

class Enemy1(turtle.Turtle):

    a=[] # to store the bullets per enemy

    def __init__(self, spriteshape, color, startx, starty):
        turtle.Turtle.__init__(self, shape = "circle")
        self.hideturtle
        self.speed(0)
        self.penup()
        self.color(color)
        self.fd(0)
        self.setposition(startx, starty)

# function to invoked at firing frequency
    def enemy1_fire(self):
        x = self.xcor()
        y = self.ycor()
        bullet1 = Bullet()
        bullet1.setposition(x,y) # bullet will appear just above the player
        bullet1.setheading(270)
        bullet1.showturtle()
        self.a.append(bullet1)

num_Enemy1 = 1
enemies1 = []
for i in range(num_Enemy1): # design the enemies
    b = Enemy1("circle", "red", random.randint(-180,180), random.randint(-100,250))
    enemies1.append(b)

# Enemy1 class defined

player = Player("triangle", "white", 0, -390) # player declared

#key bindings
turtle.listen()
turtle.onkey(player.turn_left,"Left")
turtle.onkey(player.turn_right,"Right")
turtle.onkey(player.accelerate,"Up")
#turtle.onkey(player.brake,"Down")


# define the firing intervals
freq1 = 8

确定了游戏的成员后,在主游戏循环中定义移动和射击条件。我确保一旦它们离开屏幕后就清除它们,以免减慢速度,但是使用clear并没有任何改善。

# main game loop

while True:

    # enemy 1 loop

    for enemy1 in enemies1:
        enemy1.rt(10)
        enemy1.fd(10)
        freq1 = freq1 - 1
        if (freq1==0):
            enemy1.enemy1_fire()
            freq1 = 8


        for bullets in enemy1.a: # movement of each bullet defined
            y = bullets.ycor()
            y1 = y - 10
            bullets.sety(y1)

        for bullets in enemy1.a: # hide and delete bullets once they go off screen
            if (bullets.ycor()>390 or bullets.xcor()>390 or bullets.ycor()<-390 or bullets.xcor()<-390):
                bullets.hideturtle()
                bullets.clear() # should speed up the simulation
                del bullets # should speed up the simulation

delay = raw_input("Press enter to finish. > ")

2 个答案:

答案 0 :(得分:1)

项目符号处理逻辑中存在一些错误,这意味着您在每一帧上所做的工作都比您需要做的要多(并且某些工作可能无法达到您的预期)。

第一个问题是Enemy类的这一行:

a=[] # to store the bullets per enemy

此评论是错误的,并且极具误导性。您不是要在每个敌人中存储子弹,而是在类变量中只有一个列表,并且所有Enemy实例都共享它。如果您想为每个实例创建单独的列表,则需要将该行移至__init__方法中并输入:

self.a = []

每个敌人实例都引用同一列表的事实意味着,稍后当您使用嵌套循环遍历enemies1enemy1.a时,实际上您就击中了所有子弹游戏几次(每个敌人一次)。那可能不是您想要的。

如上所述,您可以将列表移到实例中。但是,另一种方法是保留类变量列表,而只循环遍历一次,而不是重复遍历。 Bullet对象实际上并不关心创建它们后会触发哪一个Enemy,因此即使只有一列子弹,也可以实现正确的行为。只需在项目符号上使for循环缩进,然后在Enemy.a上进行迭代即可。

代码的另一个主要问题是,您没有正确删除超出范围的项目符号。在您的代码中,您有del bullets,但是这只会从本地名称空间中删除变量bullets,而不会从您要迭代的列表中删除该名称所指的对象。因此,您逐渐积累了越来越多的项目符号,这可能会减慢您的代码速度。

不幸的是,在迭代列表时从列表中删除项目有点危险。如果您只是更正了删除逻辑以使用列表中的索引(例如,使用enumerate),则会发现您只是在检查部分子弹,而不是全部子弹。这是因为每次您删除一个项目符号时,所有后来的项目符号都会在列表中上移,因此您将跳过列表中的下一个项目符号(因为它现在位于删除的项目符号以前拥有的索引中)。尽管有一些可能的解决方法(例如,从列表的末尾开始迭代),但是最好的方法通常是为下一帧构建一个全新的列表,排除那些超出范围的列表。

new_bullet_list = []
for bullets in Enemy.a:
    if (bullets.ycor()>390 or bullets.xcor()>390 or bullets.ycor()<-390 or bullets.xcor()<-390):
        bullets.hideturtle()
        bullets.clear()
    else:
        new_bullet_list.append(bullets)
Enemy.a = new_bullet_list

作为旁注,您应该真正尝试改善变量名。不用a作为项目符号列表的名称了。像bulletsbullets_list之类的名称将更具描述性。使用bullets作为项目符号列表上的循环变量会产生误导,因为它是单个Bullet实例,而不是多个项目符号集合。 enemies1enemy1中的数字也没有任何用处(通常,变量名中的数字表明您应该使用列表,但是对于每个列表,这里只有一件事情要处理,数字是多余的。

答案 1 :(得分:1)

您正在将海龟(以子弹的名义)视为本地实体,当它们处理完毕时会收集垃圾。它们不是-它们是全球性的(由于内部有乌龟清单),永远不会消失。与其期望进行垃圾回收,不如跟踪并重用它们。这是采用此方法的代码重做:

from turtle import Screen, Turtle, mainloop
from random import randint

class Bullet(Turtle):
    def __init__(self):
        Turtle.__init__(self)
        self.hideturtle()
        self.setundobuffer(None)
        self.shape('arrow')
        self.speed('fastest')
        self.color('white')
        self.setheading(270)
        self.penup()

# player class
class Player(Turtle):
    def __init__(self, sprite_shape, color, startx, starty):
        Turtle.__init__(self, shape=sprite_shape)
        self.hideturtle()
        self.speed('fastest')
        self.color(color)
        self.penup()
        self.goto(startx, starty)
        self.left(90)
        self.showturtle()

        self.setundobuffer(None)

    def turn_left(self):
        self.left(20)

    def turn_right(self):
        self.right(20)

    def move(self):
        self.forward(10)

# Define Enemy class
class Enemy(Turtle):

    available_bullets = [] # to store the bullets

    def __init__(self, sprite_shape, color, startx, starty):
        Turtle.__init__(self, shape=sprite_shape)
        self.hideturtle()
        self.speed('fastest')
        self.color(color)
        self.penup()
        self.setposition(startx, starty)
        self.showturtle()

        self.bullets = []  # my outstanding fired bullets

    # function to invoked at firing frequency
    def enemy_fire(self):
        if Enemy.available_bullets:
            bullet = Enemy.available_bullets.pop()
        else:
            bullet = Bullet()

        bullet.setposition(self.position()) # bullet will appear just above the player
        bullet.showturtle()

        self.bullets.append(bullet)

# draw white boundaries
def draw_border():
    pen = Turtle()
    pen.hideturtle()
    pen.speed('fastest')
    pen.color('white')

    pen.penup()
    pen.goto(-400, 400)
    pen.pendown()

    for _ in range(4):
        pen.forward(800)
        pen.right(90)

# set up the environment
screen = Screen()
screen.setup(850, 850)
screen.bgcolor('black')

draw_border()

num_Enemy = 2
enemies = []
for _ in range(num_Enemy): # design the enemies
    enemy = Enemy('circle', 'red', randint(-180, 180), randint(-100, 250))
    enemies.append(enemy)

player = Player('triangle', 'white', 0, -390) # player declared

# key bindings
screen.onkey(player.turn_left, 'Left')
screen.onkey(player.turn_right, 'Right')
screen.onkey(player.move, 'Up')
screen.listen()

# define the firing intervals
frequency = 8

# main game loop
while True:

    for enemy in enemies:
        enemy.right(10)
        enemy.forward(10)

        if frequency == 0:
            enemy.enemy_fire()

        frequency = (frequency + 1) % 9

        dead_bullets = []

        for bullet in enemy.bullets: # movement of each bullet defined
            bullet.forward(10)

            if not -390 < bullet.ycor() < 390:
                bullet.hideturtle()
                dead_bullets.append(bullet)

        for bullet in dead_bullets:
            enemy.bullets.remove(bullet)
            Enemy.available_bullets.append(bullet)

mainloop()

您的下一个主要修复应该是摆脱while True:,后者在基于事件的乌龟环境中不起作用,而应使用ontimer()的{​​{1}}方法。