Skip to content

Commit

Permalink
fix: handle the cases where state chart is not ready when `set_expres…
Browse files Browse the repository at this point in the history
…sion_property` or `step` are called.
  • Loading branch information
derkork committed Feb 19, 2024
1 parent f4ec8d8 commit 117c1b7
Show file tree
Hide file tree
Showing 3 changed files with 25 additions and 3 deletions.
5 changes: 4 additions & 1 deletion CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,15 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

### Fixed
- The state chart now issues a better error message when being called while not yet ready ([#81](https://github.com/derkork/godot-statecharts/issues/81)).
### Improved
- The history in the state chart debugger now uses little icons to show the type history entry. This makes it easier to see what happened at a glance. A big thanks goes out to [Alireza Zamani](https://github.com/alitnk) for suggesting this improvement.

## [0.13.0] - 2024-01-30
### Breaking Change
- You can now have fully automatic transitions ([#71]((https://github.com/derkork/godot-statecharts/issues/71))). An automatic transition has no event and will be executed when the state is entered, any event is sent or any expression property is modified. Note that a state must be active for its automatic transitions to be considered.
- You can now have fully automatic transitions ([#71](https://github.com/derkork/godot-statecharts/issues/71)). An automatic transition has no event and will be executed when the state is entered, any event is sent or any expression property is modified. Note that a state must be active for its automatic transitions to be considered.

This change can potentially break existing state charts which have transitions with no event. Before this change, these transitions were only executed when the state was entered. After this change, there are more situations in which these transitions can be executed, so you might have to add additional guards to your transitions or use the `state_entered` signal to trigger your logic.

Expand Down
21 changes: 20 additions & 1 deletion addons/godot_state_charts/state_chart.gd
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,12 @@ func _ready() -> void:
## will process the event as soon as possible but there is no guarantee that the
## event will be fully processed when this method returns.
func send_event(event:StringName) -> void:
if not is_node_ready():
push_error("State chart is not yet ready. If you call `send_event` in _ready, please call it deferred, e.g. `state_chart.send_event.call_deferred(\"my_event\").")
return

if not is_instance_valid(_state):
push_error("StateMachine is not initialized")
push_error("State chart has no root state. Ignoring call to `send_event`.")
return

if _event_processing_active:
Expand Down Expand Up @@ -146,6 +150,14 @@ func _warn_not_active(transition:Transition, source:State):
## with the same name. E.g. if you set the property "foo" to 42, you can use the expression "foo == 42" in
## an expression guard.
func set_expression_property(name:StringName, value) -> void:
if not is_node_ready():
push_error("State chart is not yet ready. If you call `set_expression_property` in `_ready`, please call it deferred, e.g. `state_chart.set_expression_property.call_deferred(\"my_property\", 5).")
return

if not is_instance_valid(_state):
push_error("State chart has no root state. Ignoring call to `set_expression_property`.")
return

_expression_properties[name] = value
# run a property change event through the state chart to run automatic transitions
_state._process_transitions(&"", true)
Expand All @@ -154,6 +166,13 @@ func set_expression_property(name:StringName, value) -> void:
## Calls the `step` function in all active states. Used for situations where `state_processing` and
## `state_physics_processing` don't make sense (e.g. turn-based games, or games with a fixed timestep).
func step():
if not is_node_ready():
push_error("State chart is not yet ready. If you call `step` in `_ready`, please call it deferred, e.g. `state_chart.step.call_deferred()`.")
return

if not is_instance_valid(_state):
push_error("State chart has no root state. Ignoring call to `step`.")
return
_state._state_step()

func _get_configuration_warnings() -> PackedStringArray:
Expand Down
2 changes: 1 addition & 1 deletion manual/manual.md
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,7 @@ In deeper state charts, events will be passed to the active states going all the

> ⚠️ **Note:** The initial state of a state chart will only be entered one frame after the state chart's `_ready` function ran. It is done this way to give nodes above the state chart time to run their `_ready` functions before any state chart logic is triggered.
>
> This means that if you call `send_event` in a `_ready` function it will most likely not work as expected. If you must send an event in a `_ready` function, you can use `call_deferred` to delay the event sending by one frame, e.g. `state_chart.send_event.call_deferred("some_event")`.
> This means that if you call `send_event`, `set_expression_property` or `step` in a `_ready` function things will most likely not work as expected. If you must call any of these functions in a `_ready` function, you can use `call_deferred` to delay the event sending by one frame, e.g. `state_chart.send_event.call_deferred("some_event")`.
##### Multiple transitions on the same state
A single state can have multiple transitions. If this is the case, all transitions will be checked from top to bottom and the first transition that reacts to the event will be executed. If you want to have multiple transitions that react to the same event, you can use [guards](#transition-guards) to determine which transition should be taken.
Expand Down

0 comments on commit 117c1b7

Please sign in to comment.