问题描述
我在学校中的项目中遵循了基于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()