将转换为PySDL2会使我的应用程序运行速度比在PyGame下运行得快吗?

时间:2017-04-25 14:51:29

标签: pygame pysdl2

我使用Pygame在Python中编写了一个小玩具。它生成的生物(一个带有方向线的圆圈,而不是一个图像)在屏幕上徘徊。我有兴趣让它更复杂,但我遇到了严重的性能问题。当屏幕上的生物数量超过20时,帧速率从60fps迅速下降到11fps,屏幕上有50fps。我已经通过多种不同的方式查看了我的(非常简单的)代码,甚至使用cProfile进行分析,但没有找到任何优化方法。

为了让长篇故事不那么长,我想我已经得出结论PyGame并不是因为我要求它做的事情。因此,我正在寻求转换为其他东西。 C ++是一个明显的答案,但由于这只是一个玩具,我宁可用Python编写代码,如果可能的话。特别是因为它已经写好了。

在查看C ++时,我发现Python有一个SDL(包装器?绑定?不确定术语):PySDL2。

感谢您坚持下去。现在的回报是:有没有理由相信将我的应用程序转换为使用PySDL2会让它变得更快?特别是考虑到PyGame显然在引擎盖下使用SDL(不知何故)。

编辑:根据要求:

import pygame
from pygame import gfxdraw
import pygame.locals
import os
import math
import random
import time

(INSERT CONTENTS OF VECTOR.PY FROM https://gist.github.com/mcleonard/5351452 HERE)


pygame.init()

#some global constants
BLUE = (0, 0, 255)
WHITE = (255,255,255)
diagnostic = False
SPAWN_TIME = 1 #number of seconds between creating new critters
FLOCK_LIMIT = 30 #number of critters at which the flock begins being culled
GUIDs = [0] #list of guaranteed unique IDs for identifying each critter

# Set the position of the OS window
position = (30, 30)
os.environ['SDL_VIDEO_WINDOW_POS'] = str(position[0]) + "," + str(position[1])

# Set the position, width and height of the screen [width, height]
size_x = 1000
size_y = 500
size = (size_x, size_y)
FRAMERATE = 60
SECS_FOR_DYING = 1
screen = pygame.display.set_mode(size)
screen.set_alpha(None)
pygame.display.set_caption("My Game")

# Used to manage how fast the screen updates
clock = pygame.time.Clock()


def random_float(lower, upper):
    num = random.randint(lower*1000, upper*1000)
    return num/1000


def new_GUID():
    num = GUIDs[-1]
    num = num + 1
    while num in GUIDs:
        num += 1
    GUIDs.append(num)
    return num


class HeatBlock:
    def __init__(self,_tlx,_tly,h,w):
        self.tlx = int(_tlx)
        self.tly = int(_tly)
        self.height = int(h)+1
        self.width = int(w)
        self.heat = 255.0
        self.registered = False

    def register_tresspasser(self):
        self.registered = True
        self.heat = max(self.heat - 1, 0)

    def cool_down(self):
        if not self.registered:
            self.heat = min(self.heat + 0.1, 255)
        self.registered = False

    def hb_draw_self(self):
        screen.fill((255,int(self.heat),int(self.heat)), [self.tlx, self.tly, self.width, self.height])


class HeatMap:
    def __init__(self, _h, _v):
        self.h_freq = _h #horizontal frequency
        self.h_rez = size_x/self.h_freq #horizontal resolution
        self.v_freq = _v #vertical frequency
        self.v_rez = size_y/self.v_freq #vertical resolution
        self.blocks = [] 

    def make_map(self):
        h_size = size_x/self.h_freq
        v_size = size_y/self.v_freq
        for h_count in range(0, self.h_freq):
            TLx = h_count * h_size #TopLeft corner, x
            col = []
            for v_count in range(0, self.v_freq):
                TLy = v_count * v_size #TopLeft corner, y
                col.append(HeatBlock(TLx,TLy,v_size,h_size))
            self.blocks.append(col)

    def hm_draw_self(self):
        for col in self.blocks:
            for block in col:
                block.cool_down()
                block.hb_draw_self()

    def register(self, x, y):
        #convert the given coordinates of the trespasser into a col/row block index
        col = max(int(math.floor(x / self.h_rez)),0)
        row = max(int(math.floor(y / self.v_rez)),0)
        self.blocks[col][row].register_tresspasser()


class Critter:
    def __init__(self):
        self.color = (random.randint(1, 200), random.randint(1, 200), random.randint(1, 200))
        self.linear_speed = random_float(20, 100)
        self.radius = int(round(10 * (100/self.linear_speed)))
        self.angular_speed = random_float(0.1, 2)
        self.x = int(random.randint(self.radius*2, size_x - (self.radius*2)))
        self.y = int(random.randint(self.radius*2, size_y - (self.radius*2)))
        self.orientation = Vector(0, 1).rotate(random.randint(-180, 180))
        self.sensor = Vector(0, 20)
        self.sensor_length = 20
        self.new_orientation = self.orientation
        self.draw_bounds = False
        self.GUID = new_GUID()
        self.condition = 0 #0 = alive, [1-fps] = dying, >fps = dead
        self.delete_me = False

    def c_draw_self(self):
        #if we're alive and not dying, draw our normal self
        if self.condition == 0:
            #diagnostic
            if self.draw_bounds:
                pygame.gfxdraw.rectangle(screen, [int(self.x), int(self.y), 1, 1], BLUE)
                temp = self.orientation * (self.linear_speed * 20)
                pygame.gfxdraw.line(screen, int(self.x), int(self.y), int(self.x + temp[0]), int(self.y + temp[1]), BLUE)
            #if there's a new orientation, match it gradually
            temp = self.new_orientation * self.linear_speed
            #draw my body
            pygame.gfxdraw.aacircle(screen, int(self.x), int(self.y), self.radius, self.color)
            #draw a line indicating my new direction
            pygame.gfxdraw.line(screen, int(self.x), int(self.y), int(self.x + temp[0]), int(self.y + temp[1]), BLUE)
            #draw my sensor (a line pointing forward)
            self.sensor = self.orientation.normalize() * self.sensor_length
            pygame.gfxdraw.line(screen, int(self.x), int(self.y), int(self.x + self.sensor[0]), int(self.y + self.sensor[1]), BLUE)
        #otherwise we're dying, draw our dying animation
        elif 1 <= self.condition <= FRAMERATE*SECS_FOR_DYING:
            #draw some lines in a spinningi circle
            for num in range(0,10):
                line = Vector(0, 1).rotate((num*(360/10))+(self.condition*23))
                line = line*self.radius
                pygame.gfxdraw.line(screen, int(self.x), int(self.y), int(self.x+line[0]), int(self.y+line[1]), self.color)

    def print_self(self):
        #diagnostic
        print("==============")
        print("radius:", self.radius)
        print("color:", self.color)
        print("linear_speed:", self.linear_speed)
        print("angular_speed:", self.angular_speed)
        print("x:", self.x)
        print("y:", int(self.y))
        print("orientation:", self.orientation)

    def avoid_others(self, _flock):
        for _critter in _flock:
            #if the critter isn't ME...
            if _critter.GUID is not self.GUID and _critter.condition == 0:
                #and it's touching me...
                if self.x - _critter.x <= self.radius + _critter.radius:
                    me = Vector(self.x, int(self.y))
                    other_guy = Vector(_critter.x, _critter.y)
                    distance = me - other_guy

                    #give me new orientation that's away from the other guy
                    if distance.norm() <= ((self.radius) + (_critter.radius)):
                        new_direction = me - other_guy
                        self.orientation = self.new_orientation = new_direction.normalize()

    def update_location(self, elapsed):
        boundary = '?'
        while boundary != 'X':
            boundary = self.out_of_bounds()
            if boundary == 'N':
                self.orientation = self.new_orientation = Vector(0, 1).rotate(random.randint(-20, 20))
                self.y = (self.radius) + 2
            elif boundary == 'S':
                self.orientation = self.new_orientation = Vector(0,-1).rotate(random.randint(-20, 20))
                self.y = (size_y - (self.radius)) - 2
            elif boundary == 'E':
                self.orientation = self.new_orientation = Vector(-1,0).rotate(random.randint(-20, 20))
                self.x = (size_x - (self.radius)) - 2
            elif boundary == 'W':
                self.orientation = self.new_orientation = Vector(1,0).rotate(random.randint(-20, 20))
                self.x = (self.radius) + 2
            point = Vector(self.x, self.y)
            self.x, self.y = (point + (self.orientation * (self.linear_speed*(elapsed/1000))))
            boundary = self.out_of_bounds()

    def update_orientation(self, elapsed):
        #randomly choose a new direction, from time to time
        if random.randint(0, 100) > 98:
            self.choose_new_orientation()
        difference = self.orientation.argument() - self.new_orientation.argument()
        self.orientation = self.orientation.rotate((difference * (self.angular_speed*(elapsed/1000))))

    def still_alive(self, elapsed):
        return_value = True #I am still alive
        if self.condition == 0:
            return_value = True
        elif self.condition <= FRAMERATE*SECS_FOR_DYING:
            self.condition = self.condition + (elapsed/17)
            return_value = True
        if self.condition > FRAMERATE*SECS_FOR_DYING:
            return_value = False

        return return_value

    def choose_new_orientation(self):
        if self.new_orientation:
            if (self.orientation.argument() - self.new_orientation.argument()) < 5:
                rotation = random.randint(-300, 300)
                self.new_orientation = self.orientation.rotate(rotation)

    def out_of_bounds(self):
        if self.x >= (size_x - (self.radius)):
            return 'E'
        elif self.y >= (size_y - (self.radius)):
            return 'S'
        elif self.x <= (0 + (self.radius)):
            return 'W'
        elif self.y <= (0 + (self.radius)):
            return 'N'
        else:
            return 'X'


# -------- Main Program Loop -----------
# generate critters
flock = [Critter()]
heatMap = HeatMap(60, 40)
heatMap.make_map()
last_spawn = time.clock()
run_time = time.perf_counter()
frame_count = 0
max_time = 0
ms_elapsed = 1
avg_fps = [1]
# Loop until the user clicks the close button.
done = False
while not done:
    # --- Main event loop only processes one event
    frame_count = frame_count + 1
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            done = True

    # --- Game logic should go here
    #check if it's time to make another critter
    if time.clock() - last_spawn > SPAWN_TIME:
        flock.append(Critter())
        last_spawn = time.clock()
    if len(flock) >= FLOCK_LIMIT:
        #if we're over the flock limit, cull the herd
        counter = FLOCK_LIMIT
        for critter in flock[0:len(flock)-FLOCK_LIMIT]:
            #this code allows a critter to be "dying" for a while, to play an animation
            if critter.condition == 0:
                critter.condition = 1
            elif not critter.still_alive(ms_elapsed):
                critter.delete_me = True
    counter = 0

    #delete all the critters that have finished dying
    while counter < len(flock):
        if flock[counter].delete_me:
            del flock[counter]
        else:
            counter = counter+1

    #----loop on all critters once, doing all functions for each critter
    for critter in flock:
        if critter.condition == 0:
            critter.avoid_others(flock)
            if critter.condition == 0:
                heatMap.register(critter.x, critter.y)
            critter.update_location(ms_elapsed)
            critter.update_orientation(ms_elapsed)
            if diagnostic:
                critter.print_self()

    #----alternately, loop for each function. Speed seems to be similar either way
    #for critter in flock:
    #    if critter.condition == 0:
    #        critter.update_location(ms_elapsed)
    #for critter in flock:
    #    if critter.condition == 0:
    #        critter.update_orientation(ms_elapsed)

    # --- Screen-clearing code goes here

    # Here, we clear the screen to white. Don't put other drawing commands
    screen.fill(WHITE)

    # --- Drawing code should go here
    #draw the heat_map
    heatMap.hm_draw_self()
    for critter in flock:
        critter.c_draw_self()

    #draw the framerate
    myfont = pygame.font.SysFont("monospace", 15)
    #average the framerate over 60 frames
    temp = sum(avg_fps)/float(len(avg_fps)) 
    text = str(round(((1/temp)*1000),0))+"FPS | "+str(len(flock))+" Critters"
    label = myfont.render(text, 1, (0, 0, 0))
    screen.blit(label, (5, 5))

    # --- Go ahead and update the screen with what we've drawn.
    pygame.display.update()

    # --- Limit to 60 frames per second
    #only run for 30 seconds
    if time.perf_counter()-run_time >= 30:
        done = True
    #limit to 60fps
    #add this frame's time to the list
    avg_fps.append(ms_elapsed)
    #remove any old frames
    while len(avg_fps) > 60:
        del avg_fps[0]
    ms_elapsed = clock.tick(FRAMERATE)
    #track longest frame
    if ms_elapsed > max_time:
        max_time = ms_elapsed

#print some stats once the program is finished
print("Count:", frame_count)
print("Max time since last flip:", str(max_time)+"ms")
print("Total Time:", str(int(time.perf_counter()-run_time))+"s")
print("Average time for a flip:", str(int(((time.perf_counter()-run_time)/frame_count)*1000))+"ms")
# Close the window and quit.
pygame.quit()

0 个答案:

没有答案