Pygame状态机模板疑问:状态激活时执行的函数提前触发
问题与解决方案
问题描述
启动main.py时控制台立即打印了“first screen start”,但该打印逻辑本应仅在状态切换到First状态时执行一次。同时存在三个疑问:
- 是否所有状态在代码启动时就已执行初始化?
- 这种设计在复杂游戏中是否会存在问题?
- 是否需要将
print_something_one_time函数移至其他位置,确保仅在First状态激活时执行?
代码文件
game.py
import pygame class Game(object): def __init__(self, screen, states, start_state): self.done = False self.screen = screen self.clock = pygame.time.Clock() self.fps = 30 self.states = states self.state_name = start_state self.state = self.states[self.state_name] def event_loop(self): for event in pygame.event.get(): self.state.get_event(event) def flip_state(self): current_state = self.state_name next_state = self.state.next_state self.state.done = False self.state_name = next_state persistent = self.state.persist self.state = self.states[self.state_name] self.state.startup(persistent) def update(self, dt): if self.state.quit: self.done = True elif self.state.done: self.flip_state() self.state.update(dt) def draw(self): self.state.draw(self.screen) def run(self): while not self.done: dt = self.clock.tick(self.fps) self.event_loop() self.update(dt) self.draw() pygame.display.update()
main.py
import pygame import sys from data.states.splash import Splash from data.states.first import First from game import Game pygame.init() pygame.mouse.set_visible(False) screen = pygame.display.set_mode((800, 600)) states = { "SPLASH": Splash(), "FIRST": First(), } game = Game(screen, states, "SPLASH") game.run() pygame.quit() sys.exit()
base.py
import pygame class BaseState(object): def __init__(self): self.done = False self.quit = False self.next_state = None self.screen_rect = pygame.display.get_surface().get_rect() self.persist = {} self.font = pygame.font.Font(None, 24) def startup(self, persistent): self.persist = persistent def get_event(self, event): pass def update(self, dt): pass def draw(self, surface): pass
splash.py
import pygame from .base import BaseState class Splash(BaseState): def __init__(self): super(Splash, self).__init__() self.title = self.font.render( "SPLASH STATE", True, pygame.Color((227, 227, 227)) ) self.title_rect = self.title.get_rect(center=self.screen_rect.center) self.next_state = "FIRST" self.time_active = 0 def update(self, dt): self.time_active += dt if self.time_active >= 3000: self.done = True def draw(self, surface): surface.fill(pygame.Color("black")) surface.blit(self.title, self.title_rect)
first.py(原代码)
import pygame from .base import BaseState class First(BaseState): def __init__(self): super(First, self).__init__() self.title = self.font.render( "This is the first screen", True, pygame.Color((227, 227, 227)), ) self.title_rect = self.title.get_rect(center=self.screen_rect.center) self.time_active = 0 self.print_something_one_time() def get_event(self, event): if event.type == pygame.QUIT: self.quit = True elif event.type == pygame.KEYUP: if event.key == pygame.K_ESCAPE: self.quit = True def draw(self, surface): surface.fill(pygame.Color("black")) surface.blit(self.title, self.title_rect) def print_something_one_time(self): print("first screen start")
解决方案
问题根源
在main.py的states字典中,你直接实例化了Splash和First对象,这意味着程序启动时,First类的__init__方法会立即执行,其中调用的print_something_one_time自然也会立刻触发,和是否切换到该状态无关。
复杂游戏中的潜在问题
这种提前实例化所有状态的设计,在复杂游戏里会引发两个核心问题:
- 资源浪费:所有状态的资源(如高清图片、音效、动画)会在游戏启动时全部加载,占用大量内存,尤其当状态数量多、资源体积大时,会显著拖慢启动速度。
- 逻辑混乱:仅应在状态激活时执行的初始化逻辑(比如数据重置、网络请求)会提前触发,导致不可预期的行为。
修复方法
将print_something_one_time移至First类的startup方法中。因为Game类的flip_state方法在切换状态时,会主动调用新状态的startup函数,确保只有当First状态被激活时,才执行这个一次性操作。
修改后的first.py代码:
import pygame from .base import BaseState class First(BaseState): def __init__(self): super(First, self).__init__() self.title = self.font.render( "This is the first screen", True, pygame.Color((227, 227, 227)), ) self.title_rect = self.title.get_rect(center=self.screen_rect.center) self.time_active = 0 def startup(self, persistent): super().startup(persistent) self.print_something_one_time() def get_event(self, event): if event.type == pygame.QUIT: self.quit = True elif event.type == pygame.KEYUP: if event.key == pygame.K_ESCAPE: self.quit = True def draw(self, surface): surface.fill(pygame.Color("black")) surface.blit(self.title, self.title_rect) def print_something_one_time(self): print("first screen start")
额外优化建议
如果游戏状态较多,建议修改状态管理逻辑,采用延迟实例化:即仅在需要切换到某个状态时,才创建该状态的实例。这样可以避免启动时加载所有资源,优化内存占用和启动速度。例如修改main.py的states为类引用,而非实例:
states = { "SPLASH": Splash, "FIRST": First, }
然后在Game的flip_state方法中动态创建实例:
def flip_state(self): current_state = self.state_name next_state = self.state.next_state self.state.done = False self.state_name = next_state persistent = self.state.persist # 动态实例化新状态 self.state = self.states[self.state_name]() self.state.startup(persistent)
同时注意修改Game的__init__方法,初始化时创建起始状态的实例:
def __init__(self, screen, states, start_state): self.done = False self.screen = screen self.clock = pygame.time.Clock() self.fps = 30 self.states = states self.state_name = start_state # 实例化起始状态 self.state = self.states[self.state_name]()
内容的提问来源于stack exchange,提问作者Diana Mele
相关产品推荐
相关产品推荐

