# 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. > ")
[项目符号处理逻辑中存在一些错误,这意味着您在每一帧上所做的工作都比您需要做的要多(并且某些工作可能无法达到您的预期目的。]
第一个问题在Enemy
类的这一行中:
a=[] # to store the bullets per enemy
此评论是错误的,并且极具误导性。您不是要存储子弹每个敌人,而是在类变量中只有一个列表,并且所有Enemy
实例都共享它。如果要为每个实例创建单独的列表,则需要将该行移到__init__
方法中并编写:
self.a = []
每个敌人实例都引用同一列表的事实意味着,稍后当您使用嵌套循环遍历enemies1
和enemy1.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
作为项目符号列表的名称。像bullets
或bullets_list
这样的名称将更具描述性。而且,将bullets
用作项目符号列表上的循环变量会产生误导,因为它是单个Bullet
实例,而不是多个项目符号集合。 enemies1
和enemy1
中的数字也没有任何用处(通常,变量名中的数字建议您使用列表,但是这里只需要处理一件事,数字是多余的) 。