我在PyGame中进行的模拟性能极差

问题描述

我在学校中的项目中遵循了基于PyGame磁贴的教程,但我从未打算做游戏,而只是想模拟一个生态系统。不幸的是,当我运行程序时,性能非常差,只能运行几秒钟,然后窗口才能停止应答。

此刻我唯一要做的就是在一块草的能量达到80时放一块新草。

该怎么办?一切都在update方法内部是否不好?我可以使用事件或其他方法使检查间隔更长吗?我知道每一帧都有很多数学运算,但是不知道怎么做。

这是我的代码

main.py:

#!/usr/bin/python3

#Importing necessary libraries
import pygame as pg,sys,random
from settings import *
from sprites import *

class Sim:
    #Initialize the game window,etc.
    def __init__(self):
        pg.init()
        self.screen = pg.display.set_mode((WIDTH,HEIGHT))
        pg.display.set_caption(TITLE)
        self.clock = pg.time.Clock()

        self.running = True

    def new_grass(self,pos):
        for g in self.grass:
            if pos != g.pos:
                Grass(self,pos)

    #Start a new generation
    def new(self):
        self.all_sprites = pg.sprite.Group()
        self.grass = pg.sprite.Group()
        Grass(self,(10,15))
        self.run()

    #Main loop
    def run(self):
        self.simulating = True
        while self.simulating:
            self.clock.tick(FPS)
            self.events()
            self.update()
            self.draw()

    #Update things on screen
    def update(self):
        self.all_sprites.update()

    #Draw a grid on screen
    def draw_grid(self):
        for x in range(0,WIDTH,TILESIZE):
            pg.draw.line(self.screen,BLACK,(x,0),HEIGHT))
        for y in range(0,HEIGHT,(0,y),(WIDTH,y))

    #Draw things on screen
    def draw(self):
        pg.display.set_caption("{:.2f}".format(self.clock.get_fps()))
        self.screen.fill(DARK_GREEN)
        self.draw_grid()
        self.all_sprites.draw(self.screen)
        #After drawing everything,flip the display
        pg.display.flip()

    #Events that might happen
    def events(self):
        for event in pg.event.get():
            #Check for the user closing the window
            if event.type == pg.QUIT:
                if self.simulating:
                    self.simulating = False
                self.running = False

s = Sim()
while s.running:
    s.new()
pg.quit()

sprites.py:

#!/usr/bin/python3

import pygame as pg,random
from settings import *
vec = pg.math.Vector2

class Grass(pg.sprite.Sprite):
    def __init__(self,sim,cord):
        self.groups = sim.all_sprites,sim.grass
        pg.sprite.Sprite.__init__(self,self.groups)
        self.sim = sim
        self.image = pg.Surface((TILESIZE/2,TILESIZE/2))
        self.image.fill(GREEN)
        self.cord = cord
        self.rect = self.image.get_rect()
        self.pos = vec(cord) * TILESIZE / 2
        self.rect.topleft = self.pos
        self.spread = vec(random.randint(-1,1),random.randint(-1,1))
        self.energy = 20

    def update(self):
        if self.energy <= 80:
            self.energy += 10

        if self.energy >= 80:
            self.sim.new_grass((self.cord + self.spread))

settings.py:

#Options/settings
TITLE = "EcoSim"
WIDTH = 480
HEIGHT = 600
FPS = 30
TILESIZE = 32
GRID_WIDTH = WIDTH / TILESIZE
GRID_HEIGHT = HEIGHT / TILESIZE

#Colors
WHITE = (255,255,255)
BLACK = (0,0)
GREEN = (0,0)
DARK_GREEN = (0,100,0)
broWN = (150,75,0)

解决方法

主要问题是方法Sim.new_grass

class Sim:
   # [...]

   def new_grass(self,pos):
       for g in self.grass:
           if pos != g.pos:
               Grass(self,pos)

该方法生成的Grass对象比预期的多得多。它事件在同一位置生成多个草对象。对于g所在的每个对象(pos != g.pos),将构造一个Grass的新实例。

如果所需位置中没有any Grass对象,则需要创建Grass的新实例:

class Sim:
   # [...]

   def new_grass(self,pos):
        if not any(pos == g.pos for g in self.grass):
            Grass(self,pos)
,

问题在这里:

def new_grass(self,pos):
    for g in self.grass:
        if pos != g.pos:
            Grass(self,pos)

这是因为每次在另一个位置上已经存在草时,都会添加一个Grass对象。我认为您的意思是根本不存在该职位。

您的程序中存在一些错误,主要是上述错误,但是参数pos实际上应该是coord。我在代码中强调了一些改进之处:

#!/usr/bin/python3

import pygame as pg,random

TITLE = "EcoSim"
WIDTH = 480
HEIGHT = 600
FPS = 30
TILESIZE = 32
GRID_WIDTH = WIDTH / TILESIZE
GRID_HEIGHT = HEIGHT / TILESIZE

WHITE = (255,255,255)
BLACK = (0,0)
GREEN = (0,0)
DARK_GREEN = (0,100,0)
BROWN = (150,75,0)

vec = pg.math.Vector2

class Grass(pg.sprite.Sprite):
    # This creates the image just once!
    IMAGE = pg.Surface((TILESIZE/2,TILESIZE/2))
    IMAGE.fill(GREEN)

    def __init__(self,cord):  # Remove 'sim'.
        # self.groups = sim.all_sprites,sim.grass
        # pg.sprite.Sprite.__init__(self,self.groups)
        # self.sim = sim
        super().__init__()
        self.image = Grass.IMAGE  # All reference the same image.
        self.cord = cord
        self.rect = self.image.get_rect()
        self.pos = vec(cord) * TILESIZE / 2
        self.rect.topleft = self.pos
        self.energy = 20
        self.spread = vec(random.randint(-1,1),random.randint(-1,1))

    def update(self):
        if self.energy <= 80:
            self.energy += 10

        # Make Sim check for this.
        # if self.energy >= 80:
        #     self.sim.new_grass((self.cord + self.spread))


class Sim:
    def __init__(self):
        pg.init()
        pg.display.set_caption(TITLE)
        self.screen = pg.display.set_mode((WIDTH,HEIGHT))
        self.clock = pg.time.Clock()
        self.all_sprites = pg.sprite.Group()  # Create *ALL* attributes in `__init__`
        self.grass = pg.sprite.Group()
        self.running = True
        self.simulating = False

    # You're passing coord here,not pos! And you also want to add
    # the grass only if the coord is not already present in the list.
    def new_grass(self,coord):
        if coord not in (x.cord for x in self.grass):
            grass = Grass(coord)
            self.grass.add(grass)
            self.all_sprites.add(grass)

    def new(self):
        self.all_sprites = pg.sprite.Group()
        self.grass = pg.sprite.Group()
        grass = Grass((10,15))  # Grass is now pure and doesn't have any side-effects,which makes the code much clearer.
        self.grass.add(grass)
        self.all_sprites.add(grass)
        self.run()

    def run(self):
        self.simulating = True
        while self.simulating:
            self.clock.tick(FPS)
            self.events()
            self.update()
            self.draw()

    def update(self):
        self.all_sprites.update()
        # Let Sim decide the fate of the grass. Don't let Grass add
        # itself.
        for grass in self.grass:
            if grass.energy >= 80:
                self.new_grass((grass.cord + grass.spread))

    def draw_grid(self):
        for x in range(0,WIDTH,TILESIZE):
            pg.draw.line(self.screen,BLACK,(x,0),HEIGHT))
        for y in range(0,HEIGHT,(0,y),(WIDTH,y))

    def draw(self):
        pg.display.set_caption("{:.2f}".format(self.clock.get_fps()))
        self.screen.fill(DARK_GREEN)
        self.draw_grid()
        self.all_sprites.draw(self.screen)
        pg.display.flip()

    def events(self):
        for event in pg.event.get():
            if event.type == pg.QUIT:
                self.simulating = False  # There was an unnecessary if here.
                self.running = False

s = Sim()
while s.running:
    s.new()
pg.quit()