首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >小行星使用吡咯游戏

小行星使用吡咯游戏
EN

Code Review用户
提问于 2020-01-26 18:31:25
回答 1查看 381关注 0票数 2

我用pygame做了小行星的外观。我仍然需要实现冲突,但由于它的运行速度非常慢,“数量”超过10。请将我的代码撕成碎片,并解释为什么它是坏的,以便我可以改进。:)

代码语言:javascript
运行
复制
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()
EN

回答 1

Code Review用户

发布于 2020-01-27 13:58:04

我将主要关注性能问题,最后只会提示几个设计问题。

对于这个用例,我想说的是远离numpynumpy有一些相当高的开销,您倾向于以“大规模”规模更快的计算来摊销这些开销。这里可不是这样的。

您可以使用numpy进行矢量加法和三角函数。在后一种情况下,您可以看到使用numpy是错误的方法。快速基准:

代码语言:javascript
运行
复制
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函数。

代码语言:javascript
运行
复制
def rotation(angle):
    rotation = numpy.array([[numpy.cos(angle), -numpy.sin(angle)], [numpy.sin(angle), numpy.cos(angle)]])
    return rotation

在这里,您计算了两次所使用的三角函数。您只需计算每一次并重用它。

代码语言:javascript
运行
复制
def rotation_reuse(angle):
    c, s = numpy.cos(angle), numpy.sin(angle)
    return numpy.array(((c,-s), (s, c)))

再次,对此更改进行基准测试。

代码语言:javascript
运行
复制
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)

您可以看到,它基本上需要一半的时间,正如您所期望的那样,做一半的操作。

如果我们继续我们已经学到的

代码语言:javascript
运行
复制
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更快。

此外,在更“设计”级别上,ShipAsteroid函数之间有很多重复的代码。在这里,您很可能应该尝试拥有一个类,然后创建实例,从而减少重复的数量。

而且,在Python中,人们倾向于用第一个字母大写写类定义,并且函数都是小写的。您有ShipAsteroid作为函数。这与前一段联系起来,也许你已经意识到,这些东西是适当的实体,值得一个更实质性的建模。

此外,改进职能的教条。就像现在一样,随便的读者很难理解正在发生的事情。

票数 1
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/236198

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档