我用pygame
做了小行星的外观。我仍然需要实现冲突,但由于它的运行速度非常慢,“数量”超过10。请将我的代码撕成碎片,并解释为什么它是坏的,以便我可以改进。:)
import pygame
import pygame.locals
import numpy
import random
asteroids = []
paused = False
delta_angle = 0.01
amount = 5 # Anything above 5 makes it run much slower, above 10 makes it run really slow.
maxVelocity = 10
screen = (1600, 1200)
left_down, right_down = False, False
boost = False
pygame.init()
display = pygame.display.set_mode(screen, pygame.RESIZABLE)
background = pygame.Surface(screen)
clock = pygame.time.Clock()
class Boid:
def __init__(self):
self.position = numpy.array([0, 0])
self.velocity = numpy.array([0, 0])
self.acceleration = numpy.array([0, 0])
self.angle = 0
self.size = 1
def update(self):
self.position += self.velocity
self.velocity += self.acceleration
self.acceleration = 0
def rotation(angle):
rotation = numpy.array([[numpy.cos(angle), -numpy.sin(angle)], [numpy.sin(angle), numpy.cos(angle)]])
return rotation
def Ship(angle):
offset1 = numpy.array([[0.0, -15.0], [-10.0, 15.0], [0.0, 10.0], [10.0, 15.0], [-5.0, 12.5], [0.0, 20.0], [5.0, 12.5]])
ship = []
for i in range(len(offset1)):
offset1[i] = rotation(angle).dot(offset1[i])
ship.append(offset1[i])
return ship
def Asteroids(angle):
offset2 = numpy.array([[-10.0, 0.0], [-8.0, -5.0], [-5.0, -8.0], [0.0, -10.0], [6.0, -8.0], [9.0, -4.0], [4.0, -2.0], [10.0, 0.0], [7.0, 7.5], [2.5, 4.0], [0.0, 10.0], [-6.0, 8.0], [-9.0, 3.0], [-4.0, 0.0]])
asteroid = []
for i in range(len(offset2)):
offset2[i] = rotation(angle).dot(offset2[i])
asteroid.append(offset2[i])
return asteroid
def player_creator():
boid = Boid()
vec = numpy.random.random_sample(2) - numpy.array([0.5, 0.5])
boid.position = numpy.array([screen[0] / 2, screen[1] / 2])
boid.velocity = vec / numpy.linalg.norm(vec) * 5
boid.acceleration = numpy.array([0.0, 0.0])
if vec[0] < 0 and vec[1] > 0 or vec[0] < 0 and vec[1] < 0:
boid.angle = numpy.arctan((vec[1]) / (vec[0])) + numpy.pi
elif vec[0] > 0 and vec[1] < 0:
boid.angle = numpy.arctan((vec[1]) / (vec[0])) + 2 * numpy.pi
else:
boid.angle = numpy.arctan((vec[1]) / (vec[0]))
return boid
def drawing_player_creator():
vertices = player.position + player.velocity + numpy.array([Ship(angle)[0], Ship(angle)[1], Ship(angle)[2], Ship(angle)[3]])
pygame.draw.polygon(display, (0, 0, 0), (vertices), 0)
pygame.draw.polygon(display, (255, 255, 255), (vertices), 2)
player.update()
def drawing_booster_flames():
vertices = player.position + numpy.array([Ship(angle)[2], Ship(angle)[4], Ship(angle)[5], Ship(angle)[6]])
pygame.draw.polygon(display, (0, 0, 0), (vertices), 0)
pygame.draw.polygon(display, (255, 255, 255), (vertices), 2)
def asteroid_creator(amount):
for asteroid in range(amount):
asteroid = Boid()
vec = numpy.random.random_sample(2) - numpy.array([0.5, 0.5])
asteroid.position = numpy.random.random_sample(2) * screen
asteroid.velocity = vec / numpy.linalg.norm(vec)
asteroid.acceleration = numpy.array([0.0, 0.0])
asteroid.angle = numpy.random.random_sample(1)[0] * 2 * numpy.pi
asteroid.size = numpy.random.randint(2, 8)
asteroids.append(asteroid)
def drawing_asteroid_creator(amount):
for n in range(amount):
#pygame.draw.circle(display, (255 - (n / amount * 255), 255 - (n / amount * 255), 255 - (n / amount * 255)), (int(asteroids[n].position[0]), int(asteroids[n].position[1])), 21)
#pygame.draw.circle(display, (n / amount * 255, n / amount * 255, n / amount * 255), (int(asteroids[n].position[0]), int(asteroids[n].position[1])), 19)
asteroid_angle = asteroids[n].angle
vertices = asteroids[n].position + asteroids[n].size * numpy.array([(Asteroids(asteroid_angle)[0]), (Asteroids(asteroid_angle)[1]), (Asteroids(asteroid_angle)[2]), (Asteroids(asteroid_angle)[3]), (Asteroids(asteroid_angle)[4]), (Asteroids(asteroid_angle)[5]), (Asteroids(asteroid_angle)[6]), (Asteroids(asteroid_angle)[7]), (Asteroids(asteroid_angle)[8]), (Asteroids(asteroid_angle)[9]), (Asteroids(asteroid_angle)[10]), (Asteroids(asteroid_angle)[11]), (Asteroids(asteroid_angle)[12]), (Asteroids(asteroid_angle)[13])])
pygame.draw.polygon(display, (0, 0, 0), (vertices), 0)
pygame.draw.polygon(display, (255, 255, 255), (vertices), 2)
asteroids[n].update()
asteroid_creator(amount)
player = player_creator()
angle = player.angle + numpy.pi / 2
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
exit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_p:
if paused == False:
paused = True
elif paused == True:
paused = False
if event.key == pygame.K_LEFT:
left_down = True
if event.key == pygame.K_RIGHT:
right_down = True
if event.key == pygame.K_SPACE:
boost = True
if event.type == pygame.KEYUP:
if event.key == pygame.K_LEFT:
left_down = False
if event.key == pygame.K_RIGHT:
right_down = False
if event.key == pygame.K_SPACE:
boost = False
if event.type == pygame.VIDEORESIZE:
screen = (event.w, event.h)
display = pygame.display.set_mode((screen), pygame.RESIZABLE)
background = pygame.Surface(screen)
if paused == False:
display.blit(background, (0, 0))
drawing_asteroid_creator(amount)
drawing_player_creator()
if left_down == True:
angle -= delta_angle * 2 * numpy.pi
#player.velocity = rotation(-delta_angle * 2 * numpy.pi).dot(player.velocity)
if right_down == True:
angle += delta_angle * 2 * numpy.pi
#player.velocity = rotation(delta_angle * 2 * numpy.pi).dot(player.velocity)
if boost == True:
drawing_booster_flames()
player.velocity += rotation(-numpy.pi / 2).dot(numpy.array([numpy.cos(angle), numpy.sin(angle)]) / 10)
if maxVelocity < numpy.linalg.norm(player.velocity):
player.velocity = maxVelocity * player.velocity / numpy.linalg.norm(player.velocity)
if player.position[0] > screen[0] + 15:
player.position[0] = 0
if player.position[0] < 0 - 15:
player.position[0] = screen[0]
if player.position[1] > screen[1] + 15:
player.position[1] = 0 - 15
if player.position[1] < 0 - 15:
player.position[1] = screen[1] + 15
for n in range(amount):
if asteroids[n].position[0] > screen[0] + asteroids[n].size * 15:
asteroids[n].position[0] = 0 - asteroids[n].size * 15
if asteroids[n].position[0] < 0 - asteroids[n].size * 15:
asteroids[n].position[0] = screen[0] + asteroids[n].size * 15
if asteroids[n].position[1] > screen[1] + asteroids[n].size * 15:
asteroids[n].position[1] = 0 - asteroids[n].size * 15
if asteroids[n].position[1] < 0 - asteroids[n].size * 15:
asteroids[n].position[1] = screen[1] + asteroids[n].size * 15
clock.tick(120)
pygame.display.update()
发布于 2020-01-27 13:58:04
我将主要关注性能问题,最后只会提示几个设计问题。
对于这个用例,我想说的是远离numpy
。numpy
有一些相当高的开销,您倾向于以“大规模”规模更快的计算来摊销这些开销。这里可不是这样的。
您可以使用numpy
进行矢量加法和三角函数。在后一种情况下,您可以看到使用numpy是错误的方法。快速基准:
In [5]: import numpy as np
In [6]: %timeit np.cos(0.6)
630 ns ± 19.1 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
In [7]: import math
In [8]: %timeit math.cos(0.6)
70.7 ns ± 1.47 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
In [9]: from math import cos
In [10]: %timeit cos(0.6)
44.1 ns ± 0.51 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
numpy
比纯Python慢10倍,并在本地导入函数,这给您带来了额外的麻烦。
对于长度为2的向量,使用它的开销可能远高于计算的胜数。当您将三角函数应用于单个值(标量)时,情况就更糟了。
如果您仍然想继续使用numpy
,那么您很可能希望关注rotation
函数。
def rotation(angle):
rotation = numpy.array([[numpy.cos(angle), -numpy.sin(angle)], [numpy.sin(angle), numpy.cos(angle)]])
return rotation
在这里,您计算了两次所使用的三角函数。您只需计算每一次并重用它。
def rotation_reuse(angle):
c, s = numpy.cos(angle), numpy.sin(angle)
return numpy.array(((c,-s), (s, c)))
再次,对此更改进行基准测试。
In [17] %timeit rotation(0.6)
4.3 µs ± 252 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
In [18]: %timeit rotation_reuse(0.6)
2.68 µs ± 30.6 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
您可以看到,它基本上需要一半的时间,正如您所期望的那样,做一半的操作。
如果我们继续我们已经学到的
In [20]: def new_rotation(angle):
...: c, s = cos(angle), sin(angle)
...: return numpy.array(((c,-s), (s, c)))
...:
In [21]: %timeit new_rotation(0.6)
1.22 µs ± 18.7 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
另一个2倍的改进,同时保持一个numpy
数组作为返回值。这不会破坏您的其他操作的位置和速度。您必须检查执行手动循环是否比使用numpy
更快。
此外,在更“设计”级别上,Ship
和Asteroid
函数之间有很多重复的代码。在这里,您很可能应该尝试拥有一个类,然后创建实例,从而减少重复的数量。
而且,在Python中,人们倾向于用第一个字母大写写类定义,并且函数都是小写的。您有Ship
和Asteroid
作为函数。这与前一段联系起来,也许你已经意识到,这些东西是适当的实体,值得一个更实质性的建模。
此外,改进职能的教条。就像现在一样,随便的读者很难理解正在发生的事情。
https://codereview.stackexchange.com/questions/236198
复制相似问题