gd,refacto: added state chart addon and namespace cleanup

This commit is contained in:
2025-06-05 14:47:51 +02:00
parent 8818e77d23
commit 5c36765a36
239 changed files with 10430 additions and 115 deletions

View File

@ -0,0 +1,115 @@
using Godot;
using GodotStateCharts;
namespace Movementtests.godot_state_charts_examples.csharp;
/// <summary>
/// This is an example of how to use the state chart from C#.
/// </summary>
// ReSharper disable once CheckNamespace
public partial class CSharpExample : Node2D
{
private StateChart _stateChart;
private Label _feelLabel;
private int _health = 20;
private StateChartState _poisonedStateChartState;
public override void _Ready()
{
// Get the state chart node and wrap it in a StateChart object, so we can easily
// interact with it from C#.
_stateChart = StateChart.Of(GetNode("%StateChart"));
// Get the poisoned state node and wrap it in a State object, so we can easily
// interact with it from C#.
_poisonedStateChartState = StateChartState.Of(GetNode("%Poisoned"));
// The the UI label.
_feelLabel = GetNode<Label>("%FeelLabel");
RefreshUi();
}
/// <summary>
/// Called when the drink poison button is pressed.
/// </summary>
private void OnDrinkPoisonButtonPressed()
{
// This uses the regular API to interact with the state chart.
var currentPoisonCount = _stateChart.GetExpressionProperty("poison_count", 0);
currentPoisonCount += 3; // we add three rounds worth of poison
_stateChart.SetExpressionProperty("poison_count", currentPoisonCount);
_stateChart.SendEvent("poisoned");
// Ends the round
EndRound();
}
/// <summary>
/// Called when the drink cure button is pressed.
/// </summary>
private void OnDrinkCureButtonPressed()
{
// Here we use some custom-made extension methods from StateChartExt.cs to have a nicer API
// that is specific to our game. This avoids having to use strings for property names and
// event names and it also helps with type safety and when you need to find all places where
// a certain property is set or an event is sent.
_stateChart.SetPoisonCount(0);
_stateChart.SendCuredEvent();
// Ends the round
EndRound();
}
/// <summary>
/// Called when the next round button is pressed.
/// </summary>
private void OnWaitButtonPressed()
{
// Ends the round
EndRound();
}
private void EndRound()
{
// then send a "next_round" event
_stateChart.SendEvent("next_round");
// and finally call Step to calculate this round's effects, based on the current state
_stateChart.Step();
// Then at the beginning of the next round, we reduce any poison count by 1
_stateChart.SetPoisonCount( Mathf.Max(0, _stateChart.GetPoisonCount() - 1));
// And update the UI
RefreshUi();
}
private void OnPoisonedStateStepped()
{
// when we step while poisoned, remove the amount of poison from our health (but not below 0)
_health = Mathf.Max(0, _health - _stateChart.GetPoisonCount());
}
private void OnNormalStateStepped()
{
// when we step while not poisoned, heal 1 health, up to a maximum of 20
_health = Mathf.Min(20, _health + 1);
}
private void RefreshUi()
{
_feelLabel.Text = $"Health: {_health} Poison: {_stateChart.GetPoisonCount()}";
}
private void OnDebugButtonPressed()
{
// States have an "Active" property that can be used to check if they are currently active.
// Note that you should usually not use this in your game code, as it sort of defeats the
// purpose of using a state chart. But it can be useful for debugging.
GD.Print("Are we poisoned? ", _poisonedStateChartState.Active ? "Yes" : "No");
}
}

View File

@ -0,0 +1 @@
uid://cpw6fi5fyccic

View File

@ -0,0 +1,9 @@
This example requires the .NET version of Godot to work. It will not work with the
standard version of Godot. You can download the .NET version of Godot from
https://godotengine.org/download. Note that the Steam version of Godot is the
standard version and will not work with this example.
If you just freshly imported this into Godot and have not yet added other C# files
to the project, you will need to initialize the project for C# by going to
Project -> Tools -> C# -> Create C# Solution. Otherwise the demo will not
work because its files have not been compiled.

View File

@ -0,0 +1,27 @@

using GodotStateCharts;
namespace Movementtests.godot_state_charts_examples.csharp;
/// <summary>
/// This is an example on how to add extension methods to the state chart class to get
/// more type safety and a nicer API.
/// </summary>
// ReSharper disable once CheckNamespace
public static class StateChartExt
{
public static void SetPoisonCount(this StateChart stateChart, int poisonCount)
{
stateChart.SetExpressionProperty("poison_count", poisonCount);
}
public static int GetPoisonCount(this StateChart stateChart)
{
return stateChart.GetExpressionProperty("poison_count", 0);
}
public static void SendCuredEvent(this StateChart stateChart)
{
stateChart.SendEvent("cured");
}
}

View File

@ -0,0 +1 @@
uid://btrfg0cn5wgrg

View File

@ -0,0 +1,109 @@
[gd_scene load_steps=9 format=3 uid="uid://dlxg641x8w8tn"]
[ext_resource type="Script" uid="uid://cpw6fi5fyccic" path="res://godot_state_charts_examples/csharp/CSharpExample.cs" id="1_fkf0f"]
[ext_resource type="Script" uid="uid://couw105c3bde4" path="res://addons/godot_state_charts/state_chart.gd" id="2_b878w"]
[ext_resource type="Script" uid="uid://jk2jm1g6q853" path="res://addons/godot_state_charts/compound_state.gd" id="3_ck7cw"]
[ext_resource type="Script" uid="uid://cytafq8i1y8qm" path="res://addons/godot_state_charts/atomic_state.gd" id="4_ovkn7"]
[ext_resource type="Script" uid="uid://cf1nsco3w0mf6" path="res://addons/godot_state_charts/transition.gd" id="5_um7g8"]
[ext_resource type="Script" uid="uid://le5w1cm0ul8p" path="res://addons/godot_state_charts/expression_guard.gd" id="6_ecpvf"]
[ext_resource type="PackedScene" uid="uid://bcwkugn6v3oy7" path="res://addons/godot_state_charts/utilities/state_chart_debugger.tscn" id="7_yrwaw"]
[sub_resource type="Resource" id="Resource_j6fet"]
script = ExtResource("6_ecpvf")
expression = "poison_count <= 0"
[node name="csharp_example" type="Node2D"]
script = ExtResource("1_fkf0f")
[node name="InfoLabel" type="Label" parent="."]
offset_left = 19.0
offset_top = 33.0
offset_right = 293.0
offset_bottom = 111.0
text = "This is a demo on how to use Godot State Charts with C#.
We have a turn-based game here. You can click \"Drink Poison\" to ingest 3 poison. \"Drink Cure\" will clear all poison. Wait will just wait a round. At the end of each round poison is subtracted from health or health regenerates if no poison is in the system. Print Debug prints out whether we are currently poisoned."
autowrap_mode = 2
[node name="FeelLabel" type="Label" parent="."]
unique_name_in_owner = true
offset_left = 23.0
offset_top = 367.0
offset_right = 63.0
offset_bottom = 390.0
[node name="HBoxContainer" type="HBoxContainer" parent="."]
offset_left = 90.0
offset_top = 407.0
offset_right = 540.0
offset_bottom = 447.0
[node name="DrinkPoisonButton" type="Button" parent="HBoxContainer"]
layout_mode = 2
size_flags_horizontal = 3
text = "Drink Poison"
[node name="DrinkCureButton" type="Button" parent="HBoxContainer"]
layout_mode = 2
size_flags_horizontal = 3
text = "Drink Cure"
[node name="WaitButton" type="Button" parent="HBoxContainer"]
layout_mode = 2
size_flags_horizontal = 3
text = "Wait"
[node name="PrintDebugButton" type="Button" parent="HBoxContainer"]
layout_mode = 2
size_flags_horizontal = 3
text = "Print Debug"
[node name="StateChart" type="Node" parent="."]
unique_name_in_owner = true
script = ExtResource("2_b878w")
track_in_editor = true
[node name="Root" type="Node" parent="StateChart"]
script = ExtResource("3_ck7cw")
initial_state = NodePath("Normal")
[node name="Normal" type="Node" parent="StateChart/Root"]
script = ExtResource("4_ovkn7")
[node name="To Poisoned" type="Node" parent="StateChart/Root/Normal"]
script = ExtResource("5_um7g8")
to = NodePath("../../Poisoned")
event = &"poisoned"
delay_in_seconds = "0.0"
[node name="Poisoned" type="Node" parent="StateChart/Root"]
unique_name_in_owner = true
script = ExtResource("4_ovkn7")
[node name="To Normal On Wear Off" type="Node" parent="StateChart/Root/Poisoned"]
editor_description = "This transition checks at the beginning of the round i the poison count is 0 and if so transitions back to normal state."
script = ExtResource("5_um7g8")
to = NodePath("../../Normal")
event = &"next_round"
guard = SubResource("Resource_j6fet")
delay_in_seconds = "0.0"
[node name="To Normal On Cure" type="Node" parent="StateChart/Root/Poisoned"]
editor_description = "This transition immediately goes back to normal state when the cure is taken."
script = ExtResource("5_um7g8")
to = NodePath("../../Normal")
event = &"cured"
delay_in_seconds = "0.0"
[node name="StateChartDebugger" parent="." instance=ExtResource("7_yrwaw")]
offset_left = 309.0
offset_top = 4.0
offset_right = 636.0
offset_bottom = 370.0
initial_node_to_watch = NodePath("../StateChart")
[connection signal="pressed" from="HBoxContainer/DrinkPoisonButton" to="." method="OnDrinkPoisonButtonPressed"]
[connection signal="pressed" from="HBoxContainer/DrinkCureButton" to="." method="OnDrinkCureButtonPressed"]
[connection signal="pressed" from="HBoxContainer/WaitButton" to="." method="OnWaitButtonPressed"]
[connection signal="pressed" from="HBoxContainer/PrintDebugButton" to="." method="OnDebugButtonPressed"]
[connection signal="state_stepped" from="StateChart/Root/Normal" to="." method="OnNormalStateStepped"]
[connection signal="state_stepped" from="StateChart/Root/Poisoned" to="." method="OnPoisonedStateStepped"]