Skip to content

Commit

Permalink
chore: add documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
derkork committed Sep 27, 2023
1 parent 868489e commit 7e9265e
Show file tree
Hide file tree
Showing 4 changed files with 184 additions and 2 deletions.
7 changes: 6 additions & 1 deletion CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [0.5.0] - 2023-09-27
### Added
- Compound states now have two additional signals `on_child_state_entered` and `on_child_state_exited` which allow running common code that should run whenever a child state of the compound state is entered or exited. This is for example useful for resetting some internal state. A big thanks goes out to [Ian Sly](https://github.com/uzkbwza) for sending a PR with this feature.
- Compound states now have two additional signals `child_state_entered` and `child_state_exited` which allow running common code that should run whenever a child state of the compound state is entered or exited. This is for example useful for resetting some internal state. A big thanks goes out to [Ian Sly](https://github.com/uzkbwza) for sending a PR with this feature.
- You can now use the new stepping mode to run code depending on the currently active states in a turn-based game. A new demo was added to show how this works. It is located at `godot_state_charts_examples/stepping`. There is also a section explaining this mode in the [documentation](manual/manual.md#stepping-mode). Another big thanks goes out to [Ian Sly](https://github.com/uzkbwza) for sending a PR with this feature.

### Fixed
- In the platformer demo the player now keeps its orientation (left or right) when standing still. Before it would always face right when standing still. In addition the handling of animations was greatly simplified. A big thanks goes out to [Renato Rotenberg](https://github.com/Brawmario) for sending a PR and giving some great advice on how to improve the handling of animations in the platformer demo.



## [0.4.5] - 2023-09-13
Expand Down
59 changes: 59 additions & 0 deletions godot_state_charts_examples/stepping/stepping.gd
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
extends Node2D


@onready var _add_coal_to_drill_button:Button = %AddCoalToDrillButton
@onready var _coal_available_label:Label = %CoalAvailableLabel
@onready var _coal_in_drill_label:Label = %CoalInDrillLabel
@onready var _state_chart:StateChart = %StateChart


var _coal_available:int = 0:
set(value):
_coal_available = value
# update the UI when this changes
_coal_available_label.text = str(_coal_available)
_add_coal_to_drill_button.disabled = _coal_available == 0

var _coal_in_drill:int = 0:
set(value):
_coal_in_drill = value
# update the UI when this changes
_coal_in_drill_label.text = str(_coal_in_drill)
if _coal_in_drill == 0:
# if there is no more coal in the drill send the
# coal_depleted event
_state_chart.send_event("coal_depleted")
else:
# otherwise send the coal_available event
_state_chart.send_event("coal_available")


func _ready():
_coal_available = 1 # we start with 1 coal


func _on_add_coal_to_drill_button_pressed():
# take one coal from the pile and put it into the generator
_coal_available -= 1
_coal_in_drill += 1


func _on_drill_has_coal_state_stepped():
# when we are in this state, we produce 2 coal and consume one of the coal in
# the drill
_coal_available += 2
_coal_in_drill -= 1


func _on_drill_has_no_coal_state_stepped():
# when we are in this state, the drill has no coal so we just flash
# the label red.
_coal_in_drill_label.modulate = Color.RED
create_tween().tween_property(_coal_in_drill_label, "modulate", Color.WHITE, 0.5)


func _on_next_round_button_pressed():
# when the next round button is pressed we handle all currently active states
_state_chart.step()


103 changes: 103 additions & 0 deletions godot_state_charts_examples/stepping/stepping.tscn
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
[gd_scene load_steps=8 format=3 uid="uid://iewxecs0uwss"]

[ext_resource type="Script" path="res://godot_state_charts_examples/stepping/stepping.gd" id="1_8d8ax"]
[ext_resource type="PackedScene" uid="uid://bcwkugn6v3oy7" path="res://addons/godot_state_charts/utilities/state_chart_debugger.tscn" id="1_u4aev"]
[ext_resource type="Script" path="res://addons/godot_state_charts/state_chart.gd" id="2_7ehwm"]
[ext_resource type="Script" path="res://addons/godot_state_charts/parallel_state.gd" id="4_ohpt6"]
[ext_resource type="Script" path="res://addons/godot_state_charts/compound_state.gd" id="5_xbwwa"]
[ext_resource type="Script" path="res://addons/godot_state_charts/atomic_state.gd" id="6_n6kwq"]
[ext_resource type="Script" path="res://addons/godot_state_charts/transition.gd" id="7_78ppn"]

[node name="SteppingExample" type="Node2D"]
script = ExtResource("1_8d8ax")

[node name="StateChartDebugger" parent="." instance=ExtResource("1_u4aev")]
offset_left = 353.0
offset_top = 5.0
offset_right = 635.0
offset_bottom = 326.0
initial_node_to_watch = NodePath("../StateChart")

[node name="NextRoundButton" type="Button" parent="."]
offset_left = 500.0
offset_top = 421.0
offset_right = 611.0
offset_bottom = 454.0
text = "Next Round"

[node name="AddCoalToDrillButton" type="Button" parent="."]
unique_name_in_owner = true
offset_left = 371.0
offset_top = 422.0
offset_right = 482.0
offset_bottom = 455.0
text = "Add Coal"

[node name="StateChart" type="Node" parent="."]
unique_name_in_owner = true
script = ExtResource("2_7ehwm")

[node name="Root" type="Node" parent="StateChart"]
script = ExtResource("4_ohpt6")

[node name="Drill" type="Node" parent="StateChart/Root"]
script = ExtResource("5_xbwwa")
initial_state = NodePath("Drill Has No Coal")

[node name="Drill Has Coal" type="Node" parent="StateChart/Root/Drill"]
editor_description = "When in this state, the drill has coal and will produce two more coal per round."
script = ExtResource("6_n6kwq")

[node name="On Coal Depleted" type="Node" parent="StateChart/Root/Drill/Drill Has Coal"]
script = ExtResource("7_78ppn")
to = NodePath("../../Drill Has No Coal")
event = &"coal_depleted"

[node name="Drill Has No Coal" type="Node" parent="StateChart/Root/Drill"]
editor_description = "When in this state, the drill will do nothing."
script = ExtResource("6_n6kwq")

[node name="On Coal Available" type="Node" parent="StateChart/Root/Drill/Drill Has No Coal"]
script = ExtResource("7_78ppn")
to = NodePath("../../Drill Has Coal")
event = &"coal_available"

[node name="GridContainer" type="GridContainer" parent="."]
offset_left = 366.0
offset_top = 333.0
offset_right = 518.0
offset_bottom = 389.0
columns = 2

[node name="Label" type="Label" parent="GridContainer"]
layout_mode = 2
text = "Coal mined: "

[node name="CoalAvailableLabel" type="Label" parent="GridContainer"]
unique_name_in_owner = true
layout_mode = 2
text = "0"

[node name="Label2" type="Label" parent="GridContainer"]
layout_mode = 2
text = "Coal in drill:"

[node name="CoalInDrillLabel" type="Label" parent="GridContainer"]
unique_name_in_owner = true
layout_mode = 2
text = "0"

[node name="Info" type="Label" parent="."]
offset_left = 13.0
offset_top = 22.0
offset_right = 322.0
offset_bottom = 254.0
text = "This is a super-simple example on how to use the \"step\" function to use a state chart in a turn-based game.
This simulates a coal-driven mining drill which consumes one coal per round and produces two coal per round when running. You can press the \"Add Coal\" button to add coal into the mining drill. Press \"Next Round\" to simulate the next round. This will call the \"step\" method and then execute code depending on which state the mining drill is currently in (e.g. either mine 2 coal or do nothing if the drill is off because it has no more coal)."
autowrap_mode = 2

[connection signal="pressed" from="NextRoundButton" to="." method="_on_next_round_button_pressed"]
[connection signal="pressed" from="AddCoalToDrillButton" to="." method="_on_add_coal_to_drill_button_pressed"]
[connection signal="state_stepped" from="StateChart/Root/Drill/Drill Has Coal" to="." method="_on_drill_has_coal_state_stepped"]
[connection signal="state_stepped" from="StateChart/Root/Drill/Drill Has No Coal" to="." method="_on_drill_has_no_coal_state_stepped"]
17 changes: 16 additions & 1 deletion manual/manual.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ The plugin comes with a few examples. You can find them in the `godot_state_char
- `history_states` - an example that shows how you can use history states to implement a state machine that can remember the last active state of a compound state.
- `performance_test` - this example is a small test harness to evaluate how larger amounts of state charts will perform. It contains a state chart in `state_chart.tscn` which you can adapt to match your desired scenario. The actual performance will depend on what callback signals you will use so you should adapt the state chart in `state_chart.tscn` to match your scenario. Then there are scenes named `ten_state_charts.tscn`, `hundred_state_charts.tscn` and `thousand_state_charts.tscn` which each contain 10, 100 or 1000 instances of the state chart from `state_chart.tscn`. You can run these scenes to see how many instances of the state chart you can run on your machine. Use the profiler to see how much time is spent in the state chart code.
- `order_of_events` - an example state chart to explore in which order events are fired. See also the [appendix](#order-of-events) for more information.
- `stepping` - an example on how to use stepping mode in a turn-based game. See also the section on [stepping mode](#stepping-mode) for more information.

### The _State Chart_ node

Expand All @@ -65,11 +66,11 @@ States are the building blocks from which you build your state charts. A state c
- `event_received(event)` - this signal is emitted when an event is received by the state while the state is active. The event is passed as a parameter.
- `state_processing(delta)` - this signal is emitted every frame while the state is active. The delta time is passed as a parameter. The signal will obey pause mode of the tree, so if the node is paused, this signal will not be emitted.
- `state_physics_processing(delta)` - this signal is emitted every physics frame while the state is active. The delta time is passed as a parameter. The signal will obey pause mode of the tree, so if the node is paused, this signal will not be emitted.
- `state_stepped()` - called whenever the `step` method of the state chart is called. See [stepping mode](#stepping-mode) for more information on stepping mode.
- `state_input(input_event)` - called when input is received while the state is active. This is useful to limit input to certain states.
- `state_unhandled_input(input_event)` - called when unhandled input is received while the state is active. Again this is useful to limit input to certain states.



#### Atomic states

<img src="../addons/godot_state_charts/atomic_state.svg" width="32" height="32" align="left"> Atomic states are the most basic type of state. They cannot have child states. Atomic states have no additional properties.
Expand Down Expand Up @@ -229,6 +230,20 @@ The debugger will only track state changes of the currently watched state chart.

If you want to disable the history tracking, you can unset the _Auto Track State Changes_ checkbox in the editor UI.

## Stepping mode

If you have a turn based game where you want to execute code depending on which state you are in but you don't want to run this code every frame in `_process` or `_physics_process` but rather every turn, you can use stepping mode. In this case, you will connect your state handling code not to the `state_processing` or `state_physics_processing` signals, but rather to the `state_stepped` signal.

Then you call the `step` function of the state chart whenever want to calculate the "next round".

```gdscript
func _on_next_round_button_pressed():
state_chart.step() # calculate the next round based on the current state
```

This will emit the `state_stepped` signal for all states which are currently active. You can connect your code to this signal to execute it every time the state chart is stepped.


## Tips & tricks

### Keep state and logic separate
Expand Down

0 comments on commit 7e9265e

Please sign in to comment.