【问题标题】:Low frame rate, possibly because un-accelerated graphics低帧率,可能是因为未加速的图形
【发布时间】:2017-12-25 11:21:11
【问题描述】:

所以几个月前我开始使用 pygame 创建一个游戏,它的顶视图类似于星际争霸、帝国时代等……我编写了大约 1320 行代码来创建我的游戏基础;但是,我在传输图像时遇到了帧速率问题,我认为这是因为我无法在 pygame 中使用加速图形。我目前对图像进行 blitting 的方式是提前在表面上对所有图像进行 blit,然后我在表面下创建整个屏幕的 blit 图像。我应该使用更有效的方法吗?

所以我的假设是这将是一个巨大的混乱,我不想浪费你的家伙的时间。基本上每当我对屏幕大小的表面进行 blit 时,我的帧率都会下降约 20 帧,有没有办法在 pygame 中避免这种情况?

    ##PYGAME INITATE##
import pygame, os
os.environ['SDL_VIDEO_CENTERED'] = '1'
pygame.init()

_W,_H = pygame.display.Info().current_w, pygame.display.Info().current_h
flags = pygame.DOUBLEBUF | pygame.HWSURFACE
gameDisplay = pygame.display.set_mode((_W,_H),pygame.FULLSCREEN ) ## CREATES SCREEN YOU DISPLAY ON ##
gameDisplay.fill((0,0,0)) ## FILL COLOR OF SCREEN ##
pygame.display.set_caption("Dope Game")  ## SETS NAME ##
gameClock = pygame.time.Clock() ## CLOCK OF THE GAME ##
import math
import os
import random
import copy


SQRT = math.sqrt
PI = math.pi
cos = math.cos
sin = math.sin


## REPEATABLE FUNCTIONS ##

def loadScale(file,command,sizeX,sizeY):
    temp = pygame.image.load(file)
    tempInfo = temp.get_rect()
    tempInfo1,tempInfo2,tempInfo3,tempInfo4 = temp.get_rect()
    tempInfo3 = int(tempInfo3)
    tempInfo4 = int(tempInfo4)
    if (command == "ratio"):    
        tempInfo3 = tempInfo3*sizeX
        tempInfo4 = tempInfo4*sizeY
        temp = pygame.transform.scale(temp,(int(tempInfo3),int(tempInfo4) ) )

    elif (command == "size"):
        temp = pygame.transform.scale(temp, (sizeX,sizeY) )         

    return(temp)

## NON GAME RELATED CLASSES ##

class EnterFrame():
    def __init__(self,frameReset,function,parse,reset):
        self.frameReset = frameReset
        self.currentFrame = frameReset
        self.function = function
        self.parse = parse
        self.reset = reset
        if (self.reset != "onComplete"):
            self.reset = (reset-1)
        enterFrameTable.append(self)

    def step(self,enterFrameTable):
        if (self.currentFrame == 0):
            self.function(self.parse)
            if (self.reset != "onComplete"):
                if (self.reset > 0): 
                    self.currentFrame = self.frameReset
                    self.reset = self.reset-1
                else:
                    enterFrameTable.remove(self)
                    del self
            else:
                self.currentFrame = self.frameReset
        else:
            self.currentFrame = self.currentFrame-1

class PlayerCreation():
    def __init__(self): 
        self.x = _W
        self.y = _H
        self.view = [1600,1600]
        self.viewShift = []
        self.viewChangeSpeed = 25

    def moveView(self,key):
        add = EnterFrame(0,self.moveViewAction,key,"onComplete")
        self.viewShift.append([add,key])

    def moveViewAction(self,key):
        if (key == "up"):
            self.view[1] = self.view[1]-self.viewChangeSpeed
            Map.recenterView()
            if (self.view[1] < 0):
                self.view[1] = 0

        elif (key == "right"):
            self.view[0] = self.view[0]+self.viewChangeSpeed
            Map.recenterView()
            if (self.view[0] > Map.tileSize*4):
                self.view[0] = Map.tileSize*4


        elif (key == "down"):
            self.view[1] = self.view[1]+self.viewChangeSpeed
            Map.recenterView()
            if (self.view[1] > Map.tileSize*4):
                self.view[1] = Map.tileSize*4


        elif (key == "left"):
            self.view[0] = self.view[0]-self.viewChangeSpeed
            Map.recenterView()
            if (self.view[0] < 0):
                self.view[0] = 0

    def endMoveView(self,key):
        for i in range(len(self.viewShift)-1,-1,-1 ):
            if (self.viewShift[i][1] == key):
                enterFrameTable.remove(self.viewShift[i][0])
                del self.viewShift[i]               

class ImageCreation():
    def __init__(self,name,image,type,hitBox):
        self.name = name
        self.image = image
        self.type = type
        self.hitBox = hitBox
        self.rect = self.image.get_rect()
        if (self.hitBox != "none"):
            self.shiftX = hitBox[0][0]
            self.shiftY = hitBox[0][1]
            for i in range(1,len(hitBox) ):
                if (hitBox[i][0] < self.shiftX):
                    self.shiftX = hitBox[i][0]

                if (hitBox[i][1] < self.shiftY):
                    self.shiftY = hitBox[i][1]

        else:
            self.shiftX = self.rect[2]/2
            self.shiftY = self.rect[3]/2

        imageTable.append(self)

    def draw(self,x,y):
        image = self.image
        self.blit = gameDisplay.blit(image,(x,y) )

class MapCreation():
    def __init__(self):
        self.tileSize = 800
        self.size = self.tileSize*10
        self.tiles = []
        self.loadedTiles = []
        self.surface = pygame.Surface([self.tileSize*5,self.tileSize*5], pygame.SRCALPHA, 32) 
        self.centerTile = [5,5] 
        self.drawPoint = [(_W-self.tileSize)/2,(_H-self.tileSize)/2]
        self.amount = round(self.size/self.tileSize)

        backGround = loadScale("Grass.png","size",self.tileSize,self.tileSize)
        for i in range(0,self.amount):
            for u in range(0,self.amount):
                image = copy.copy(backGround)
                newTile = Tile(image,[u*self.tileSize,i*self.tileSize])
                self.tiles.append(newTile)



        info = imageFind("House.png")
        tile = self.tiles[55]
        imageObject(info,tile,[240,50])

        self.loadTiles("center")

    def recenterView(self):

        if (Player.view[0] > 3*self.tileSize):
            self.centerTile[0] = self.centerTile[0]+1
            Player.view[0] = Player.view[0]-self.tileSize
            self.loadTiles("right")
            print("right")

        elif (Player.view[0] < 1*self.tileSize):
            self.centerTile[0] = self.centerTile[0]-1
            Player.view[0] = Player.view[0]+self.tileSize
            self.loadTiles("center")
            print("center")


        if (Player.view[1] > 3*self.tileSize):
            self.centerTile[1] = self.centerTile[1]+1
            Player.view[1] = Player.view[1]-self.tileSize
            self.loadTiles("center")
            print("center")


        elif (Player.view[1] < 1*self.tileSize):
            self.centerTile[1] = self.centerTile[1]-1
            Player.view[1] = Player.view[1]+self.tileSize
            self.loadTiles("center")
            print("center")


    def loadTiles(self,load):
        tileIndex = self.centerTile[0]+self.centerTile[1]*self.amount
        if (load == "center"):
            self.loadedTiles = []
            for i in range(-2,3):
                for u in range(-2,3):
                    loadTile = tileIndex + i*self.amount + u
                    self.loadedTiles.append(self.tiles[loadTile])
                    self.tiles[loadTile].loaded = [u+2,i+2]

            self.surface = pygame.Surface([self.tileSize*5,self.tileSize*5], pygame.SRCALPHA, 32)       

            for i in range(0,len(self.loadedTiles) ):           
                sx = self.loadedTiles[i].loaded[0]*self.tileSize
                sy = self.loadedTiles[i].loaded[1]*self.tileSize
                self.surface.blit(self.loadedTiles[i].buttomLayer,(sx,sy) )


            for i in range(0,len(self.loadedTiles) ):           
                sx = self.loadedTiles[i].loaded[0]*(self.tileSize+2)
                sy = self.loadedTiles[i].loaded[1]*(self.tileSize+2)
                self.surface.blit(self.loadedTiles[i].middleLayer,(sx,sy) )

            for i in range(0,len(self.loadedTiles) ):           
                sx = self.loadedTiles[i].loaded[0]*(self.tileSize+2)
                sy = self.loadedTiles[i].loaded[1]*(self.tileSize+2)
                self.surface.blit(self.loadedTiles[i].topLayer,(sx,sy) )

        elif (load == "right"):
            self.loadedTiles = []
            for i in range(-2,3):
                for u in range(-2,3):
                    loadTile = tileIndex + i*self.amount + u
                    self.loadedTiles.append(self.tiles[loadTile])
                    self.tiles[loadTile].loaded = [u+2,i+2]

            ## OLD METHOD THAT WASNT WORKING ##
            ##subSurf = self.surface.subsurface(self.tileSize,0,self.tileSize*1,self.tileSize*5)
            ##self.surface = pygame.Surface([self.tileSize*5,self.tileSize*5], pygame.SRCALPHA, 32)
            ##self.surface.blit(subSurf,(0,0) )

            ## NEW METHOD ##
            self.surface.scroll(dx=-self.tileSize*1,dy=0)

    def draw(self):
        global Player       
        image = self.surface.subsurface(Player.view[0],Player.view[1],self.tileSize,self.tileSize)      
        image = pygame.transform.scale(image,(self.tileSize,self.tileSize) )
        gameDisplay.blit(image,((_W-self.tileSize)/2,(_H-self.tileSize)/2) )
        image = pygame.transform.scale(self.surface,(300,300) )
        gameDisplay.blit(image,(0,0 ) )

class Tile():
    def __init__(self,image,coords):
        transparentSurface = pygame.Surface([1000,1000], pygame.SRCALPHA, 32)
        self.x = coords[0]
        self.y = coords[1]
        self.loaded = False
        self.buttomLayer = image
        self.middleLayer = copy.copy(transparentSurface)
        self.topLayer = transparentSurface      

class imageObject():
    def __init__(self,info,tile,coords):
        self.info = info
        self.image = info.image
        self.rect = self.image.get_rect()

        self.x = coords[0]
        self.y = coords[1]

        self.hitBox = []

        if (self.info.hitBox != "none"):
            for i in range(0,len(self.info.hitBox) ):
                self.hitBox.append([self.info.hitBox[i][0]+self.x,self.info.hitBox[i][1]+self.y])
        #self.object = createObject(self.hitBox,True,"none")

        if (info.type == "background"):
            tile.buttomLayer.blit(self.image,(self.x,self.y) )
        if (info.type == "object"):
            tile.middleLayer.blit(self.image,(self.x,self.y) )
        if (info.type == "object alphas"):
            tile.topLayer.blit(self.image,(self.x,self.y) )


def imageFind(name):
    for i in range(0,len(imageTable) ):
        if (name == imageTable[i].name):
            return(imageTable[i])

    return("none") 

def imageLoad(types):
    if (types == "basic"):
        image = loadScale("House.png","ratio",1,1)
        basicHouse = ImageCreation("House.png",image,"object",[[190, 375], [350, 375], [350, 235], [190, 235]])

        image = loadScale("Grass.png","ratio",1,1)
        grass = ImageCreation("Grass.png",image,"background","none")

def enterFrameHandle(enterFrameTable): 
    for i in range(len(enterFrameTable)-1,-1,-1 ):
        enterFrameTable[i].step(enterFrameTable)

def EventHandle(event):
    global Player

    if (event.type == pygame.QUIT):
        endGame()

    elif(event.type == pygame.KEYDOWN):
        key = (pygame.key.name(event.key) )

        if (key == "escape"):
            endGame()       

        elif (key == "up" or key == "right" or key == "down" or key == "left"):
            Player.moveView(key)

    elif(event.type == pygame.KEYUP):
        key = (pygame.key.name(event.key) )
        if (key == "up" or key == "right" or key == "down" or key == "left"):
            Player.endMoveView(key)

def endGame():
    global QuitGame
    QuitGame = True

def mainLoop(): 

    ## GLOBALS ##
    global QuitGame
    QuitGame = False

    global Player
    Player = PlayerCreation()

    ## MAIN TABLES ##
    global enterFrameTable
    enterFrameTable = []

    global basicObjectTable
    basicObjectTable = []

    ## TEMP TABLES ##
    global imageTable
    imageTable = []


    ## START UP LOOPS ##

    imageLoad("basic")
    global Map
    Map = MapCreation()

    ## Temporary ##

    while (QuitGame == False):
        enterFrameHandle(enterFrameTable)
        for event in pygame.event.get():
            EventHandle(event)


        Map.draw()
        pygame.display.update() ## updates the screen ##

        gameDisplay.fill([0,0,0]) ## Clears screen for next frame ##
        gameClock.tick(64) ## The FPS ##
        fps = gameClock.get_fps()
        if (fps < 64 and fps != 0):
            fps = gameClock.get_fps()
            print("FPS HAS DROPPED TOO LOW DOWN TO",fps)


runGameNow = True

mainLoop()
pygame.quit()
quit()

路径查找器的问题是由于在查找最近点时在迭代线上拾取

【问题讨论】:

  • 你的问题有点含糊。您能否提供一些游戏代码用于minimal reproducible example
  • 我希望人们在更广泛的意义上使用 Pyglet。我要大胆猜测一下,你根本没有使用Pygame Groups 或正确使用?
  • 我认为倾倒一千多行代码不会给你任何有用的建议。您应该自己做一些功课,并将这种混乱减少到一个合理的最小示例,人们可以在不了解您代码的所有细节的情况下阅读和遵循。
  • 好吧,我最初试图完全避免删除任何代码,但它被要求,我试图将它尽可能地减少到〜250,这是尽可能简约而不可能消除问题,我做了尽可能多的研究,但得出的结论是,每当在 pygame 中对大表面进行 blitting 时,它会显着降低帧速率,是否有一种“预 blit”表面的好方法来避免这种情况?感谢您提供我仍在学习的任何信息,并且真的只是希望能指出正确的方向
  • 再一次,你对一组进行了 blit —— 而不是一个一个地浮出水面。如果这不起作用,那么您要么在渲染序列中对对象进行大量更新,要么实际上已经设法将 Pygame 的边界推到了它的绝对极限(请记住,它并不是真正为复杂的 3D 设计的游戏)。

标签: python pygame pyglet


【解决方案1】:

这个问题真的很简单。
您将线条数量视为疾病,原因必须是 Pygame。通常,或者至少在几乎任何情况下——如果你不是使用语言和库的神,问题可能不在于语言或库。因为你很可能没有接近推动两者可以为你提供的极限。
这个事实的一个致命的问题是,如果你看看你的眼睛在屏幕上看到的东西,你会很快注意到,每当你的背景即将循环播放时——那就是发生故障/丢帧的时候。 显然,这可能是图书馆做一些可疑的事情的原因。如果是图书馆做了实际的循环。

但在您的代码中,这是您自己完成的实现。
所以最好的选择是从那里开始。
只是为了对延迟发生的位置非常有信心——我们可以看看 Python 附带的分析器 (cProfiler)。

python -m cProfile -o sample_data.pyprof awesome_game.py
pyprof2calltree -i sample_data.pyprof -k

结果将显示以下内容:

从这里我们可以看到pygame.SurfaceenterFrameHandle 的处理时间很多。我敢打赌pygame.Surface 会在enterFrameHandle 的某个地方被调用。

所以最好的选择是从eventFrameHandle 链的末尾开始。
在这个细分中,这是loadTiles

而且,这里有很多警告标志。
这里至少有四个循环......而且循环很糟糕,因为它们需要处理时间。

我通过简单地添加调试信息:

def loadTiles
    if load == "center":
        print('Loading center')
    elif load == "right":
        print('Loading right')

显然,只要发生故障,center 就会被触发。
因此,这进一步缩小了范围。所以我在每个 for 循环周围添加了计时器。

  • 循环一:耗时 0.0 秒
  • 循环二:耗时 0.29 秒
  • 循环三:耗时 0.5 秒
  • 循环四:耗时 0.6 秒

总而言之,这些对您来说非常不利,因为它们直接影响渲染顺序。那么这是为什么呢?让我们进一步分解。
我们将从占用 0.3 秒的第一个循环开始:

for i in range(0,len(self.loadedTiles) ):           
    sx = self.loadedTiles[i].loaded[0]*self.tileSize
    sy = self.loadedTiles[i].loaded[1]*self.tileSize
    self.surface.blit(self.loadedTiles[i].buttomLayer,(sx,sy) )

所以len(self.loadedTiles) 将是25
这意味着这个循环必须在每个调用 loadTiles("center") 的渲染周期中迭代 25 次。

每个循环几乎都需要0.01 秒,每个完整循环最多需要 0.25 秒。这并不可怕,好吧。。如果您希望游戏达到 60 FPS,那么没有循环或函数调用可能需要超过 0.0166 秒总共

所以我们已经超出了我们想要的 FPS 目标。因此,如果我们想到达任何地方,我们必须将其缩短几毫秒。

循环本身并没有那么糟糕,我的意思是 25 次迭代,现代 PC 可以在比 time() 测量的更短的时间内完成。所以,这一切都指向self.surface.blit().. 从经验来看,这是肯定的。
这也与我们上面的图表在pygame.surface.blit 中花费了总 CPU 时间的 30% 的事实相关。所以这是我们的第一个小偷。

看看其余的循环,它们基本上是相同的,只是数学中的数字更大,计算时间稍长,因此可能是循环中的时间差。

那么,我们可以做些什么来缩小.blit 倍呢?
好吧,我们可以在每次循环迭代中停止调用 blit,只需移动精灵对象的位置,然后将它们一个一个地 blit。
但是老师,这几乎是一样的吗?
嗯,是的,我很快就会成为忍者龟,它是.. 这就是为什么,我们将把精灵移动到批次/组中​​,并渲染组。因此,我们可以改变位置(快速操作),并且因为我们将渲染移到循环之外,我们还可以将它们转换为组对象并渲染组。

首先,我们会将您的对象转换为pygame.sprite.Sprite,这些是包含碰撞检测、移动等功能的聪明小对象。

self.grass = pygame.sprite.Sprite()
self.grass.image = pygame.image.load("Grass.png")
self.grass.rect = pygame.Rect(0, 0, 60, 60)

然后你将它添加到渲染组中(一个批次):

self.main_group = pygame.sprite.Group(self.grass)

Bam,现在您可以执行以下操作,而不是更新 25 个精灵:

self.main_group.draw(gameDisplay)

然后砰,速度!

现在,您的代码不是为此而设计的。
所以这需要一段时间来修复和纠正这个设计缺陷。
因为这对我来说需要几个小时才能使您的原始代码尽可能接近,所以我忽略了这一点并重新设计了您的整个 MapCreation 并修改了您的 PlayerCreation 以及您如何移动地图(我从尝试重新定位每个时间,这是一个好主意,但实现 1PM 重写别人的代码远非复杂).. 所以.. 这是解决您问题的新方法:

import pygame, os
from time import time
from collections import OrderedDict
os.environ['SDL_VIDEO_CENTERED'] = '1'
pygame.init()

_W,_H = pygame.display.Info().current_w, pygame.display.Info().current_h
flags = pygame.DOUBLEBUF | pygame.HWSURFACE | pygame.RESIZABLE
gameDisplay = pygame.display.set_mode((500, 500))
gameDisplay.fill((0,0,0)) ## FILL COLOR OF SCREEN ##
pygame.display.set_caption("Dope Game")  ## SETS NAME ##
gameClock = pygame.time.Clock() ## CLOCK OF THE GAME ##
import math
import os
import random
import copy


SQRT = math.sqrt
PI = math.pi
cos = math.cos
sin = math.sin


## REPEATABLE FUNCTIONS ##

def loadScale(file,command,sizeX,sizeY):
    temp = pygame.image.load(file)
    tempInfo = temp.get_rect()
    tempInfo1,tempInfo2,tempInfo3,tempInfo4 = temp.get_rect()
    tempInfo3 = int(tempInfo3)
    tempInfo4 = int(tempInfo4)
    if (command == "ratio"):    
        tempInfo3 = tempInfo3*sizeX
        tempInfo4 = tempInfo4*sizeY
        temp = pygame.transform.scale(temp,(int(tempInfo3),int(tempInfo4) ) )

    elif (command == "size"):
        temp = pygame.transform.scale(temp, (sizeX,sizeY) )         

    return(temp)

## NON GAME RELATED CLASSES ##

class EnterFrame():
    def __init__(self,frameReset,function,parse,reset):
        self.frameReset = frameReset
        self.currentFrame = frameReset
        self.function = function
        self.parse = parse
        self.reset = reset
        if (self.reset != "onComplete"):
            self.reset = (reset-1)
        enterFrameTable.append(self)

    def step(self,enterFrameTable):
        if (self.currentFrame == 0):
            self.function(self.parse)
            if (self.reset != "onComplete"):
                if (self.reset > 0): 
                    self.currentFrame = self.frameReset
                    self.reset = self.reset-1
                else:
                    enterFrameTable.remove(self)
                    del self
            else:
                self.currentFrame = self.frameReset
        else:
            self.currentFrame = self.currentFrame-1

class PlayerCreation():
    def __init__(self): 
        self.x = _W
        self.y = _H
        self.view = [1600,1600]
        self.viewShift = []
        self.viewChangeSpeed = 25

    def moveView(self,key):
        add = EnterFrame(0,self.moveViewAction,key,"onComplete")
        self.viewShift.append([add,key])

    def moveViewAction(self,key):
        if (key == "up"):
            self.view[1] = self.view[1]-self.viewChangeSpeed
            Map.move_tile(0, 1) # Player moves up, so the tiles should move down ->  (0, 1) == (x, y)
            if (self.view[1] < 0):
                self.view[1] = 0

        elif (key == "right"):
            self.view[0] = self.view[0]+self.viewChangeSpeed
            Map.move_tile(-1, 0)
            if (self.view[0] > Map.tileSize*4):
                self.view[0] = Map.tileSize*4

        elif (key == "down"):
            self.view[1] = self.view[1]+self.viewChangeSpeed
            Map.move_tile(0, -1)
            if (self.view[1] > Map.tileSize*4):
                self.view[1] = Map.tileSize*4

        elif (key == "left"):
            self.view[0] = self.view[0]-self.viewChangeSpeed
            Map.move_tile(1, 0) 
            if (self.view[0] < 0):
                self.view[0] = 0

    def endMoveView(self,key):
        for i in range(len(self.viewShift)-1,-1,-1 ):
            if (self.viewShift[i][1] == key):
                enterFrameTable.remove(self.viewShift[i][0])
                del self.viewShift[i]               

class ImageCreation():
    def __init__(self,name,image,type,hitBox):
        self.name = name
        self.image = image
        self.type = type
        self.hitBox = hitBox
        self.rect = self.image.get_rect()
        if (self.hitBox != "none"):
            self.shiftX = hitBox[0][0]
            self.shiftY = hitBox[0][1]
            for i in range(1,len(hitBox) ):
                if (hitBox[i][0] < self.shiftX):
                    self.shiftX = hitBox[i][0]

                if (hitBox[i][1] < self.shiftY):
                    self.shiftY = hitBox[i][1]

        else:
            self.shiftX = self.rect[2]/2
            self.shiftY = self.rect[3]/2

        imageTable.append(self)

    def draw(self,x,y):
        image = self.image
        self.blit = gameDisplay.blit(image,(x,y) )

class MapCreation():
    def __init__(self):
        self.tileSize = 800
        self.size = self.tileSize*10
        self.tiles = []
        self.centerTile = [5,5] 
        self.amount = round(self.size/self.tileSize)

        self.sprites = OrderedDict()
        self.grass_image = pygame.image.load("Grass.png")
        self.grass_group = pygame.sprite.Group()

        for x in range(0, self.amount*60, 60):      ## 10*60, but we step 60 pixels, so in the end, this will be 10 steps.
            for y in range(0, self.amount*60, 60):  ## Which is the same as `for x in range(self.amount)` but we scale it up
                                                    ## to give us pixels instead of just the ammount.
                index = len(self.sprites) # -- Generate a index for this sprite. Used for access later (to update pos for instace)

                ## == Create the sprite, add a image to it and define a position and size.
                self.sprites[index] = pygame.sprite.Sprite()
                self.sprites[index].image = self.grass_image
                self.sprites[index].rect = pygame.Rect(x, y, 60, 60)

                ## == Then add the sprite to the grass group.
                self.grass_group.add(self.sprites[index])

    def move_tile(self, dx, dy):
        for index in self.sprites:
            x, y, width, height = self.sprites[index].rect
            ## == this is how you move the sprites:d
            self.sprites[index].rect = pygame.Rect(x+dx, y+dy, 60, 60)

    def draw(self):
        self.grass_group.update()
        self.grass_group.draw(gameDisplay)

class Tile():
    def __init__(self,image,coords):
        transparentSurface = pygame.Surface([1000,1000], pygame.SRCALPHA, 32)
        self.x = coords[0]
        self.y = coords[1]
        self.loaded = False
        self.buttomLayer = image
        self.middleLayer = copy.copy(transparentSurface)
        self.topLayer = transparentSurface      

class imageObject():
    def __init__(self,info,tile,coords):
        self.info = info
        self.image = info.image
        self.rect = self.image.get_rect()

        self.x = coords[0]
        self.y = coords[1]

        self.hitBox = []

        if (self.info.hitBox != "none"):
            for i in range(0,len(self.info.hitBox) ):
                self.hitBox.append([self.info.hitBox[i][0]+self.x,self.info.hitBox[i][1]+self.y])
        #self.object = createObject(self.hitBox,True,"none")

        if (info.type == "background"):
            tile.buttomLayer.blit(self.image,(self.x,self.y) )
        if (info.type == "object"):
            tile.middleLayer.blit(self.image,(self.x,self.y) )
        if (info.type == "object alphas"):
            tile.topLayer.blit(self.image,(self.x,self.y) )


def imageFind(name):
    for i in range(0,len(imageTable) ):
        if (name == imageTable[i].name):
            return(imageTable[i])

    return("none") 

def imageLoad(types):
    if (types == "basic"):
        image = loadScale("House.png","ratio",1,1)
        basicHouse = ImageCreation("House.png",image,"object",[[190, 375], [350, 375], [350, 235], [190, 235]])

        image = loadScale("Grass.png","ratio",1,1)
        grass = ImageCreation("Grass.png",image,"background","none")

def enterFrameHandle(enterFrameTable): 
    for i in range(len(enterFrameTable)-1,-1,-1 ):
        enterFrameTable[i].step(enterFrameTable)

def EventHandle(event):
    global Player

    if (event.type == pygame.QUIT):
        endGame()

    elif(event.type == pygame.KEYDOWN):
        key = (pygame.key.name(event.key) )

        if (key == "escape"):
            endGame()       

        elif (key == "up" or key == "right" or key == "down" or key == "left"):
            Player.moveView(key)

    elif(event.type == pygame.KEYUP):
        key = (pygame.key.name(event.key) )
        if (key == "up" or key == "right" or key == "down" or key == "left"):
            Player.endMoveView(key)

def endGame():
    global QuitGame
    QuitGame = True

def mainLoop(): 

    ## GLOBALS ##
    global QuitGame
    QuitGame = False

    global Player
    Player = PlayerCreation()

    ## MAIN TABLES ##
    global enterFrameTable
    enterFrameTable = []

    global basicObjectTable
    basicObjectTable = []

    ## TEMP TABLES ##
    global imageTable
    imageTable = []


    ## START UP LOOPS ##

    imageLoad("basic")
    global Map
    Map = MapCreation()

    ## Temporary ##

    while (QuitGame == False):
        enterFrameHandle(enterFrameTable)
        for event in pygame.event.get():
            EventHandle(event)


        pygame.display.update() ## updates the screen ##
        gameDisplay.fill([0,0,0]) ## Clears screen for next frame ##

        #Map.draw()
        Map.grass_group.update()
        Map.grass_group.draw(gameDisplay)

        gameClock.tick(64) ## The FPS ##
        fps = gameClock.get_fps()
        if (fps < 64 and fps != 0):
            fps = gameClock.get_fps()
            print("FPS HAS DROPPED TOO LOW DOWN TO",fps)

        pygame.display.flip()


runGameNow = True

mainLoop()
pygame.quit()
quit()

此代码很少在 FPS 中下拉。
当它发生时,它会下降到〜63 FPS,因为我移动了窗口(这是一件很费力的事情,因为所有 OpenGL 引用都需要补偿新窗口位置和在 Pygame 中触发的事件(调整事件大小,移动事件等很费力,但发生一次而不是每个渲染循环..所以它是可以接受的)。

结论

  1. 性能缓慢很少是库的错。
  2. 暂时停止以开发人员的身份思考,并观察屏幕,看看是否能找到故障所在的线索。在这种情况下,每次瓷砖围绕屏幕边缘旋转时都会发生明显的故障。这是一个线索!无需任何编程技能即可直观地看到此错误。
  3. 使用分析器并调试您的代码花费大部分时间的地方。
  4. 尝试看看其他人是如何解决游戏设计问题的。通过将所有内容放在类中,您走在正确的轨道上,但这主要有助于您的代码可读性 - 尝试查看其他人的类中的内容以及他们如何使用 Pygame来电。

总而言之,这是一段非常有趣且不错的代码。
它只是不是为了性能而设计的,之后很难编写代码。所以从头开始也许不是一个坏主意?但请保留您的旧代码库以供参考,并避免明显的陷阱:

  • 渲染序列中的循环
  • 逐个渲染对象,改为使用批处理
  • 只执行循环来更新位置/统计数据,而不是图形!

关于该主题的最后一点:显卡是巨大的动力源。他们每秒可以计算几百万次操作,如果不是更多的话。每次你blit屏幕上的东西时,显卡需要中断它的计算和内存分配来翻转缓冲区并更新你在屏幕上看到的内容。这工作很慢,因为它不是计算,而是操作任务(这通常包括等待队列和信号).. 因此,与其将数百个blit's 扔到显卡上,不如通过向其发送纹理(描述事物外观的数学数据)来对其进行数学运算..然后调用blit(在本例中为draw)一次,然后让显卡完成所有数学运算 - 执行一个大的逻辑运算 - 并预知.. x100 的速度已经。

【讨论】:

  • 我只想让您知道,我非常感谢如此彻底的回复。我总体上很困惑,你帮了很多忙。你是真正的英雄。
  • @mason 干杯 ​​bud,当你的一个答案有意义并且真正教/澄清东西时总是很好的 :) 祝你在游戏中好运!另外,欢迎来到 StackOverflow,如果我或任何其他答案解决了您的问题 - 请不要忘记将其标记为已解决,以便其他人可以轻松找到问题的正确答案 :)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-10-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多