You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 07:57:05