derkork / godot-statecharts

A state charts extension for Godot 4
MIT License
679 stars 33 forks source link

Add interface to get expression properties #110

Closed Frozenfire92 closed 1 month ago

Frozenfire92 commented 2 months ago

It would be nice to have an interface to get expression properties. Currently it seems like the state chart uses this solely for expression based guards, however I think it could also be useful for "temporary data related to states" that aren't necessarily used by guards. This has the benefit of not needing additional variables to be stored in your main script, as well as being able to debug it for free in the state chart debug window

Imagine the scenario of a game where a player can select a card from a hand. The states could be Selectable and Selected. There are transitions for the events "card_selected" and "card_placed" to go between them. During the Selected state the game displays a "preview card" based on the index/data of the card in the hand. You could store this as a currentSelectedCard variable, but why not part of the state chart (as it is state)

Today

stateChart.set_expression_property("selected_card_index", index)
stateChart.send_event("card_selected")

and then access it like so

if stateChart._expression_properties.has("selected_card_index"):
    prints(stateChart._expression_properties["selected_card_index"])

and then clear it like so

stateChart._expression_properties.erase("selected_card_index")

Proposal

it could be more ergonomic if the state chart exposed these functions

func get_expression_property(key: String, default: Variant = null):
    if _expression_properties.has(key): return _expression_properties[key]
    else: return default

# could also be called erase_expression_property to be consistent with Dictionary
func clear_expression_property(key: String):
    _expression_properties.erase(key)
derkork commented 2 months ago

I think it makes sense to be able to read the expression properties back from the state chart and it's easy enough to add, so I'm going to add this.

However setting an expression property is considerably more expensive than just storing a value in a custom variable. This is because whenever you change an expression property the state chart will check all currently active automatic transitions (which may use guards based on the expression properties). So I wouldn't recommend using this as a general purpose storage container but rather only store expression properties that your guards will actually use.

Frozenfire92 commented 2 months ago

However setting an expression property is considerably more expensive than just storing a value in a custom variable.

Fair point. This kind of gives me two thoughts

My game likely doesn't have the performance needs, but I think to get myself into a better habit I will instead store my own custom dictionary/variables

derkork commented 1 month ago

If you just need a general-purpose storage area for key-value pairs - every godot object has set_meta / get_meta.