# 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. > ")
答案 0 :(得分:1)
项目符号处理逻辑中存在一些错误,这意味着您在每一帧上所做的工作都比您需要做的要多(并且某些工作可能无法达到您的预期)。
第一个问题是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
中的数字也没有任何用处(通常,变量名中的数字表明您应该使用列表,但是对于每个列表,这里只有一件事情要处理,数字是多余的。
答案 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}}方法。