Trabajé en el mismo motor que coderanger. Tengo un punto de vista diferente. :)
Primero, no teníamos una pila de FSM, teníamos una pila de estados. Una pila de estados forma un solo FSM. No sé cómo se vería una pila de FSM. Probablemente demasiado complicado para hacer algo práctico.
Mi mayor problema con nuestra máquina de estado global era que era un conjunto de estados, y no un conjunto de estados. Esto significa, por ejemplo, ... / MainMenu / Loading era diferente de ... / Loading / MainMenu, dependiendo de si aparecía el menú principal antes o después de la pantalla de carga (el juego es asíncrono y la carga es principalmente impulsada por el servidor )
Como dos ejemplos de cosas esto hizo feo:
- Condujo, por ejemplo, al estado LoadingGameplay, por lo que tenía Base / Loading, y Base / Gameplay / LoadingGameplay para cargar dentro del estado Gameplay, que tenía que repetir gran parte del código en el estado de carga normal (pero no todos, y agregar algo más) )
- Tuvimos varias funciones como "si en el creador de personajes, vaya al juego; si está en el juego, vaya a la selección de personaje; si está en la selección de personaje, vuelva a iniciar sesión", porque queríamos mostrar las mismas ventanas de interfaz en diferentes estados, pero retroceder / avanzar Los botones todavía funcionan.
A pesar del nombre, no era muy "global". La mayoría de los sistemas de juego internos no lo usaban para rastrear sus estados internos, porque no querían que sus estados se burlaran de otros sistemas. Otros, por ejemplo, el sistema UI, podrían usarlo pero solo para copiar el estado en sus propios sistemas estatales locales. (Especialmente advertiría contra el sistema para los estados de la interfaz de usuario. El estado de la interfaz de usuario no es una pila, es realmente un DAG, y tratar de forzar cualquier otra estructura en él solo hará que las UI sean frustrantes de usar).
Lo que fue bueno fue aislar las tareas para integrar el código de los programadores de infraestructura que no sabían cómo se estructuraba realmente el flujo del juego, para que pudieras decirle al tipo que escribe el parche "pon tu código en Client_Patch_Update", y al tipo que escribe los gráficos cargando "ponga su código en Client_MapTransfer_OnEnter", y podríamos intercambiar ciertos flujos lógicos sin muchos problemas.
En un proyecto paralelo, he tenido mejor suerte con un conjunto de estados en lugar de una pila , sin tener miedo de hacer varias máquinas para sistemas no relacionados, y me niego a caer en la trampa de tener un "estado global", que es realmente solo una forma complicada de sincronizar las cosas a través de variables globales: seguro, terminarás haciéndolo cerca de una fecha límite, pero no diseñes con eso como tu objetivo . Básicamente, el estado en un juego no es una pila, y los estados en un juego no están todos relacionados.
El GSM también, como tienden a hacer los punteros de función y el comportamiento no local, hizo que la depuración sea más difícil, aunque depurar ese tipo de grandes transiciones de estado tampoco fue muy divertido antes de que lo tuviéramos. Los conjuntos de estados en lugar de las pilas de estados realmente no ayudan a esto, pero debes ser consciente de ello. Las funciones virtuales en lugar de los punteros de función pueden aliviar eso de alguna manera.