Closed Granshmeyr closed 8 months ago
Sorry, I forgot to check the history debugger tab which indicates that I might be doing something wrong in a way that's unrelated to the StateChart:
EDIT: After looking at the above logs, the title of this post is not accurate. The StateChart is indeed receiving the first event of the array six times in a row, even though I'm attempting to iterate through it. My new title would be "SendEvent not sending correct event?" or something.
Sorry for spam, but I just found the 'solution'... I guess? Basically, I just changed stateChart.SendEvent(topplingEvent);
in my code to stateChart.Wrapped.CallDeferred("send_event", topplingEvent);
and now everything works perfectly fine. Of course, I've changed the Wrapped
property to public. I have zero understanding of threads or anything like that so I don't know why this solves my problem.
You have found a bug! Thank you very much for reporting this. I have tried to set up a minimal reproducer which looks like this:
extends Node
@onready var chart = $StateChart
func _ready():
chart.send_event("a")
chart.send_event("b")
chart.send_event("c")
func _on_state_chart_event_received(event):
print("got " , event)
if event == "a":
chart.send_event("d")
This should print
got a
got d
got b
got c
but it prints got a
in an endless loop. The reason for this is that the library will only process new events once the currently running event has been fully processed by all states and all callbacks have been called. If it would not do this, we'd get some interesting (read: extremely hard to understand) behaviour when a new event is sent in an event handler . So what the library does is, when it you send a new event while one is currently still being processed, it puts it into an internal queue and then processes them one by one. So what happened was:
a
a
- calls event_received
with a
a
- sends d
d
because a
is not fully processed yet.a
d
- calls event_received
with a
(<--- here is the bug, it calls the handler with the original event, not the queued one)a
- sends d
The reason why it works when you do a call_deferred
is because in this case you give each event time to fully play out before your insert the new one. So no events are queued and the buggy line in the library is never run.
I'm making a fix for this.
The fix is in and I also submitted it to the asset library. May take a while to materialize there because the approval process is manual there and I'm not sure how many people are currently available to approve it. The fix is really easy so if you want to fix your copy in the meantime, just change line 106 addons/godot_state_charts/state_chart.gd like this:
I'm trying to implement an input buffer in my game. This will allow the player to buffer 'inputs' during an attack animation. In this case however they are really buffering
SendEvent()
function calls.In my game there is an intermediary script called
StateChartQueue.cs
which forwards events to the StateChart node. At any point I can disable the forwarding of events and instead store them into a buffer. This buffer can later be 'flushed' to the StateChart node.The
event_received
signal, as emitted by the StateChart node, is used inStateChartQueue.cs
to validate the order of events flushed and received. This is so I can truly control the order in which the events are processed when flushing to the StateChart. What I'm trying:Relevant code
```csharp partial class DominoRunner : Node { readonly string[] events; readonly StateChart stateChart; string topplingEvent; int dominoIndex; internal DominoRunner(StateChart stateChart, string[] events) { this.events = events; this.stateChart = stateChart; } void OnEventReceived(StringName @event) { GD.Print($"Received {@event}."); if (@event != topplingEvent) { GD.Print($"{@event} was not {topplingEvent}."); //return; } dominoIndex++; if (dominoIndex >= events.Length) { GD.Print("Finished domino."); QueueFree(); return; } topplingEvent = events[dominoIndex]; GD.Print($"Toppling {topplingEvent}, index {dominoIndex}."); stateChart.SendEvent(topplingEvent); } internal void Topple() { dominoIndex = 0; topplingEvent = events[dominoIndex]; foreach (string str in events) { GD.Print($"{str} in domino list."); } stateChart.Connect("event_received", new Callable(this, nameof(OnEventReceived))); GD.Print($"Toppling initial event {topplingEvent}, index {dominoIndex}."); stateChart.SendEvent(topplingEvent); } static string[] SortEventsByWeight(string[] events) { DictionaryFull StateChartQueue.cs script
```csharp using System; using System.Collections.Generic; using System.Linq; using Godot; using GodotStateCharts; using Grindle; public partial class StateChartQueue : Node2D { readonly DictionaryThe problem is that
OnEventReceived(StringName @event)
is being passed the same event string every time it is called, which is mainly what I don't understand. Because of this, I just can't use the event to the effect that I want. Here is a log showing this:Relevant logs
``` Not recording onAs you can hopefully see, the StateChart node is passing the first event I sent to it for this entire 'loop'. There is still so much I don't know about game engines and programming in general, so maybe I'm just dumb. Any idea what the issue might be?
Info: I'm executing Record() and Flush() from an AnimationPlayer.