【问题标题】:Best way to re factor my code to be more efficient?重构我的代码以提高效率的最佳方法?
【发布时间】:2014-05-03 22:50:27
【问题描述】:

我最近一直在制作一个简单的 2D RPG, 进展顺利。

我正在尝试做的事情: 1. 遍历一个文本文件,初始化类(mapcontrol.LoadMap())。 2. 调用 mapcontrol.Render() 将每个精灵类添加到主精灵数组(sprite_list)。 3.当某个事件发生时(到达屏幕末尾),将所有图块移动一个变量(cameraX/Y) 4.画瓷砖 5. 删除图块

问题在于,我必须每帧调用 mapcontrol.LoadMap() 方法,这会导致主要的 fps 问题(在我的 3.8 Ghz 双核上可能每两秒运行 1 帧)。

我的问题:优化此程序的最佳方法是什么?我知道很多程序效率低下,那么改进它的最佳方法是什么?

我愿意接受你最严厉的打击,因为我知道它即将到来。 在这里,如果伤害了你的眼睛,请见谅。

import pygame
import os
import sys
import time
import random
pygame.init()
class GameState(object):
    def __init__(self):
        self.state = 'start_screen'
    def state_eval(self):
        if self.state == 'start_screen':

            Start_Screen()
        if self.state == 'local_map':

            Local_Map()
        # if self.state == 'world_map':
            # World_Map()
        if self.state == 'in_progress':
            pass
gamestate = GameState()



def Local_Map():
    gamestate.state = 'in_progress'
    cameraX, cameraY = (0,0)
    width, height = 600, 400
    screen = pygame.display.set_mode((width, height))
    pygame.display.set_caption("Frigid Development")
    sprite_list = pygame.sprite.Group()
    clock = pygame.time.Clock()
    tileSize = 32


    class Player(pygame.sprite.Sprite):
        def __init__(self, cameraX, cameraY):
            pygame.sprite.Sprite.__init__(self)
            self.images_w = [pygame.image.load("C:\Users\Software Development\Desktop\Test00.png"), pygame.image.load("C:\Users\Software Development\Desktop\Test01.png"), pygame.image.load("C:\Users\Software Development\Desktop\Test02.png")]
            self.images_s = [pygame.image.load("C:\Users\Software Development\Desktop\Test03.png"), pygame.image.load("C:\Users\Software Development\Desktop\Test04.png"), pygame.image.load("C:\Users\Software Development\Desktop\Test05.png")]
            self.images_a = [pygame.image.load("C:\Users\Software Development\Desktop\Test06.png"), pygame.image.load("C:\Users\Software Development\Desktop\Test07.png"), pygame.image.load("C:\Users\Software Development\Desktop\Test08.png")]
            self.images_d = [pygame.image.load("C:\Users\Software Development\Desktop\Test09.png"), pygame.image.load("C:\Users\Software Development\Desktop\Test10.png"), pygame.image.load("C:\Users\Software Development\Desktop\Test11.png")]
            self.image_list = [self.images_w, self.images_s, self.images_a, self.images_d]
            self.list_index = 1
            self.index = 0
            self.ani_speed = 6
            self.image = self.image_list[self.list_index][self.index]
            self.rect = self.image_list[self.list_index][self.index].get_rect()
            self.rect.x, self.rect.y, = 200, 300
            self.mov = False
        def update(self):
            if self.list_index >= len(self.image_list):
                self.list_index = 0
            if self.index < 0:
                self.index = 2
            if self.index >= len(self.image_list[self.list_index]):
                self.index = 0
            self.image = self.image_list[self.list_index][self.index]
            self.rect.x, self.rect.y = (self.rect.x - cameraX), (self.rect.y - cameraY)
            screen.blit(self.image, (self.rect.x, self.rect.y))




    class Grass(pygame.sprite.Sprite):
            def __init__(self, tileX, tileY):
                pygame.sprite.Sprite.__init__(self)
                self.image = pygame.image.load("C:\Users\Software Development\Desktop\Tile00.png")
                self.rect = self.image.get_rect()
                self.tileX = tileX
                self.tileY = tileY
            def update(self):
                screen.blit(self.image, (self.tileX, self.tileY))

    class Rock(pygame.sprite.Sprite):
            def __init__(self, tileX, tileY):
                pygame.sprite.Sprite.__init__(self)
                self.image = pygame.image.load("C:\Users\Software Development\Desktop\Tile01.png")
                self.rect = self.image.get_rect()
                self.tileX = tileX
                self.tileY = tileY
            def update(self):
                screen.blit(self.image, (self.tileX, self.tileY))


    class Cube(pygame.sprite.Sprite):
            def __init__(self, tileX, tileY):
                pygame.sprite.Sprite.__init__(self)
                self.image = pygame.image.load("C:\Users\Software Development\Desktop\Tile02.png")
                self.rect = self.image.get_rect()
                self.tileX = tileX
                self.tileY = tileY
            def update(self):
                screen.blit(self.image, (self.tileX, self.tileY))


    class MapControl(object):
        def __init__(self, cameraX, cameraY):
            self.tile_dict = {0: pygame.image.load("C:\Users\Software Development\Desktop\Tile00.png"), 1: pygame.image.load("C:\Users\Software Development\Desktop\Tile01.png"), 2: pygame.image.load("C:\Users\Software Development\Desktop\Tile02.png")}
            self.tileX = 0
            self.tileY = 0
            self.length = 0
            self.count = 0
            self.file = open('C:\Users\Software Development\Desktop\Map00L0.txt', 'r')
            self.oix = True
            self.map_array = []

        def LoadMap(self, map, cameraX, cameraY):
            self.tileX = cameraX
            self.tileY = cameraY
            for x in map:
                for tile in x:
                    if tile == '0':
                        grass = Grass(self.tileX, self.tileY)
                        self.map_array.append(grass)
                        self.tileX = self.tileX+32
                    if tile == '1':
                        rock = Rock(self.tileX, self.tileY)
                        self.map_array.append(rock)
                        self.tileX = self.tileX+32
                    if tile == '\n':
                        self.tileX = cameraX
                        self.tileY += 32
                    if tile == '2':
                        cube = Cube(self.tileX, self.tileY)
                        self.map_array.append(cube)
                        self.tileX = self.tileX+32
                self.tileX = cameraX
                self.tileY = cameraY
        def CalcMapBorders(self, map):
            for ba in map:
                for line in self.file:
                    self.length += 1
                    self.count = len(list(line.strip('\n')))



        def Render(self):
            for draw in self.map_array:
                sprite_list.add(draw)

        def CleanUp(self):
            for rm in self.map_array:
                sprite_list.remove(rm)




    file = open('C:\Users\Software Development\Desktop\Map00L0.txt', 'r')   
    map00ly0 = list(file.read())    
    map = [map00ly0]

    def Loop(screen, map, cameraX, cameraY):
        cameraX, cameraY = 0,0
        player = Player(cameraX, cameraY)
        mapcontrol = MapControl(cameraX, cameraY)
        mapcontrol.LoadMap(map, cameraX, cameraY)
        while gamestate.state == 'in_progress':
            sprite_list.add(player) 
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    pygame.quit()


            key = pygame.key.get_pressed()

            if key[pygame.K_DOWN] and not(key[pygame.K_LEFT] or key[pygame.K_RIGHT]):
                time.sleep(0.0)
                player.rect.y += 2
                player.index += 1
                player.list_index = 1

            if key[pygame.K_UP] and not(key[pygame.K_LEFT] or key[pygame.K_RIGHT]):
                time.sleep(0.0)
                player.rect.y -= 2
                player.index += 1
                player.list_index = 0

            if key[pygame.K_RIGHT]:
                time.sleep(0.0)
                player.rect.x += 2
                player.index += 1
                player.list_index = 3

            if key[pygame.K_LEFT]:
                time.sleep(0.0)
                player.rect.x -= 2
                player.index += 1
                player.list_index = 2

            if not key[pygame.K_UP] and not key[pygame.K_DOWN] and not key[pygame.K_LEFT] and not key[pygame.K_RIGHT]:
                player.index = 0    

            screen.fill((0,0,0))

            mapcontrol.CalcMapBorders(map)

            mapcontrol.Render()

            sprite_list.update()

            if player.rect.y > height - 25:
                player.rect.y -= 2
                player.index += 1
                cameraY -= 3
                #mapcontrol.LoadMap(map, cameraX, cameraY)
            if player.rect.y < 0:
                player.rect.y += 2
                player.index += 1
                cameraY += 3
            if player.rect.x > width - 15:
                player.rect.x -= 2
                player.index += 1
                cameraX -= 3
                #mapcontrol.LoadMap(map, cameraX, cameraY)
            if player.rect.x < 0:
                player.rect.x += 2
                player.index += 1
                cameraX += 3
            if player.rect.y < cameraY:
                cameraY -= 3
            if player.rect.x < cameraX:
                cameraX -= 3
            if player.rect.y > (cameraY + (mapcontrol.length*32) - 15):
                cameraY += 3
            if player.rect.x > (cameraX + (mapcontrol.count*32) - 15):
                cameraX += 3

            sprite_list.draw(screen)
            pygame.display.update()
            clock.tick(30)
            mapcontrol.CleanUp()
            gamestate.state_eval()
    Loop(screen, map, cameraX, cameraY)


def Start_Screen():
    gamestate.state = 'in_progress'
    class Menu_Load_bt(pygame.sprite.Sprite):
        def __init__(self):
            pygame.sprite.Sprite.__init__(self)
            self.image = pygame.image.load("C:\Users\Software Development\Desktop\Menu_Load_bt.png")
            self.rect = self.image.get_rect()
            self.rect.x, self.rect.y = 280, 250
        def update(self):
            GUI.blit(self.image, (self.rect.x, self.rect.y))
    class Menu_Exit_bt(pygame.sprite.Sprite):
        def __init__(self):
            pygame.sprite.Sprite.__init__(self)
            self.image  = pygame.image.load("C:\Users\Software Development\Desktop\Menu_Exit_bt.png")
            self.rect = self.image.get_rect()
            self.rect.x, self.rect.y = 300, 350
        def update(self):
            GUI.blit(self.image, (self.rect.x, self.rect.y))
    class Menu_Sel(pygame.sprite.Sprite):
            def __init__(self):
                pygame.sprite.Sprite.__init__(self)
                self.image = pygame.image.load("C:\Users\Software Development\Desktop\Menu_Select.png")
                self.rect = self.image.get_rect()
                self.dir = None
                self.rect.x, self.rect.y = 470, 270
            def update(self):
                if self.dir == None:
                    pass
                if self.dir == 'down':
                    self.rect.y = 350
                if self.dir == 'up':
                    self.rect.y = 270
                GUI.blit(self.image, (self.rect.x, self.rect.y))
    menu_load_bt = Menu_Load_bt()
    menu_exit_bt = Menu_Exit_bt()
    menu_sel = Menu_Sel()
    images = pygame.sprite.Group()
    GUI = pygame.display.set_mode((800, 600))
    pygame.display.set_caption("Main Menu")
    background = pygame.image.load("C:\Users\Software Development\Desktop\Menu_Back.png")
    while gamestate.state == 'in_progress':

            images.add(menu_load_bt)
            images.add(menu_exit_bt)
            images.add(menu_sel)
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    pygame.quit()
                if event.type == pygame.KEYDOWN:
                    if event.key == pygame.K_DOWN:
                        menu_sel.dir = 'down'
                    if event.key == pygame.K_UP:
                        menu_sel.dir = 'up'

                    if event.key == pygame.K_SPACE or event.key == pygame.K_RETURN: 
                        if menu_sel.rect.y == 270:
                            gamestate.state = 'local_map'
                        if menu_sel.rect.y == 350:
                            sys.exit()

            gamestate.state_eval()
            images.update()
            GUI.blit(background, (0,0))
            images.draw(GUI)
            pygame.display.update() 

gamestate.state_eval()

谢谢。

我按照你所说的做了 Paul,我发现瓶颈出现在 mapcontrol.LoadMap(),但问题是我的程序要正常工作,我需要在每个循环中重绘整个地图。也许我应该只在需要滚动它时才重新绘制它(这就是为什么我一次又一次地绘制它,这样可以逐步移动每个图块。)?

我尝试这样做,并且 FPS 是正确的 (30),但是当我尝试向下滚动时(如果 player.rect.y > height 等),fps 直线下降。然而,这个函数(mapcontrol.LoadMap())对于滚动过程是必不可少的。建议???

【问题讨论】:

  • 只需将所有静止的(例如,不移动的)图块渲染为位图或其他东西......然后你只需要担心它的位置以及任何精灵(定义为非静止的精灵(例如移动)瓷砖)
  • 您能否说得更具体一点,我不确定如何将我的固定图块“渲染”为“位图”...

标签: python optimization refactoring


【解决方案1】:

专业提示:查看循环。程序几乎将所有时间都花在执行循环上。寻找循环内部和可能在外部的东西。

例如,函数 CalcMapBorders: 在 for 循环中,您一遍又一遍地打开同一个文件。您为文件中的每一行增加一个变量 self.lines。但它在一个循环内,所以当函数完成时,self.lines 等于行数乘以循环迭代次数。我不知道你想在这里做什么,但这太疯狂了。

您将变量 self.count 设置为每行的长度,然后在处理下一行时覆盖它。因此,当您完成时,该变量等于 last 行的长度。在此过程中,您已经无数次设置和破坏了这个变量。

外部 while 循环绝对不会执行任何操作,因为它不会被执行两次(您将循环变量 i 设置为 False 内部循环的每一步)。消除while,以及变量i。

帮自己一个忙,把类定义放在最外层。将函数放在类中,而不是相反。您有嵌套函数、嵌套类、实例化嵌套类的嵌套函数。在 12 年的 Python 编程中,我从未(嗯,几乎从未)做过一些事情。这是完全合法的,但它会导致几乎不可能遵循的程序结构。当结构如此不清楚时,很难发现冗余和不必要的代码。

【讨论】:

  • 我肯定会更改大部分内容,因为这主要是无意的,但 count 属性意味着只获取最后一行。不管怎样,我会重构我的代码,然后看看事情会如何发展。
猜你喜欢
  • 2022-11-10
  • 1970-01-01
  • 1970-01-01
  • 2010-10-27
  • 2012-04-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-11-17
相关资源
最近更新 更多