Closed thely closed 9 months ago
I don't think you will need a history state for that. I was able to rig this up with a fairly simple state chart like this:
Then a minimal script which just shows/hides the menus depending on which state we are in:
extends Control
@onready var move_menu = %MoveMenu
@onready var action_menu = %ActionMenu
@onready var target_select = %TargetSelect
@onready var target_confirm = %TargetConfirm
func _on_moving_state_entered():
move_menu.visible = true
action_menu.visible = false
target_select.visible = false
target_confirm.visible = false
func _on_action_selecting_state_entered():
move_menu.visible = true
action_menu.visible = true
target_select.visible = false
target_confirm.visible = false
func _on_target_selecting_state_entered():
move_menu.visible = true
action_menu.visible = true
target_select.visible = true
target_confirm.visible = false
func _on_attacking_state_entered():
move_menu.visible = true
action_menu.visible = true
target_select.visible = true
target_confirm.visible = true
The buttons can just send events to the state chart to switch state and then you get something like this:
https://github.com/derkork/godot-statecharts/assets/327257/578d4ed8-ca6d-4eb9-b52e-cdb270b4130d
Here's a ZIP-File with the scene if you want to have a look.
I think your approach can be simplified by not caring about how you come into a certain state. When a state is entered, you just set up things how they are supposed to be in that state. So if you are in the target select state, you just show the menus that should be visible in that state and disable input to all menus that should not have input. This way you don't need to think of all variants how a state can be entered. You just say "when i am in this state, things should be like this".
I may be oversimplifying the case here, but from the requirements you've given, this approach should do the trick nicely.
I appreciate this response! I had considered this before, but it felt somewhat counter to the idea of state charts, and felt like it would absolutely balloon as my menu needs grew.
But your idea sparked something else for me, which I'll leave here if it helps anyone else. I felt like there must be some way for the individual states to determine their visibility. I gave my base menu class an enum that could be STATE_NONE
, STATE_ESCAPE
, or STATE_SELECT
, which is changed with these two functions:
func press_continue():
exit_state = STATE_SELECT
menu_select.emit()
func press_cancel():
exit_state = STATE_ESCAPE
menu_cancel.emit()
Then I changed my exit function to work with that:
func exit(complete: bool = false):
if exit_state == STATE_ESCAPE or complete:
self.visible = false
So if the state is exiting from pressing escape to cancel, hide the window. Otherwise, stay open. Then the inherited menus can call this super.exit()
first, then disable their inputs.
As for complete
: I made a final state, Action Confirmed, that auto-transitions back to the MoveState
after 0.25s, sending exit()
to all the open menus with a binds of true
. That way, once an action is confirmed, all the open menus hide themselves.
Glad you found a solution that works for you!
I'm not sure if my "issue" here is with this plugin or with state charts, but here goes.
I'm in the very early stages of doing a tile-based battler, similar to Fire Emblem or FFTA. During a single turn of combat, the menu works like this:
I had previously set up my menu structure as a fairly simple FSM with an undo stack, so that I could keep menus onscreen (similar to something like the original Pokemon games, where successive menus appear on top of previous ones). A given menu's
exit()
method only got called if the undo was called, as otherwise I wanted to keep them all visible.I switched my previous implementation to StateCharts instead. In the new implementation, the latter three states are grouped under a parent
CompoundState
, as I initially wanted all their "Cancel on escape" transitions to move to aHistoryState
that never worked (it always went back to its default, not to the previous state).HistoryState
working or no, the problem I'm having now is that while there's only onestate_exited()
signal, all the attack-selection menus have two ways of being "exited:" moving to the next state via spacebar, or reverting to a previous state with escape. In the first case, I want to keep the exiting menu onscreen but disable its inputs, and in the other case I want to hide the menu entirely.It occurs to me that to make this work, the state would need to know the transition that caused it, but that feels like it's beyond the scope of a state chart. That, or every menu would need two states somehow, but that feels like it's inviting state explosion.
Any thoughts? I know this example is fairly simple, but I'd like to be able to use this for more complicated menus later on.