Hello, everyone! I am Chad Wolfe, founder of Cairo Creative Studios LLC, and today I come to you with a relatively mad and also extremely useful replacement for Event Signals. (Construct 2 style Functions)
Something I've been eyeballing for quite some time is this addon for Construct 3: https://www.construct.net/en/game-assets/addons/object-signals-custom-3727
This Addon powers up Construct 3 by extending Objects with what are typically called Event/Delegates¹ and Class Properties² in other programming languages.
Unfortunately, I'm running a bit low on funds, so I can't buy it yet. As a coder, I can also write such tools myself, and I was actually about to... Then, a thought came to me. We already have Events that are declared with a string. So, why not just use those?
Using Ajax, you can try to fetch something by a URL that makes no sense at all. The URL I've requested is just "/fail". Then, I store the name of the Event within the AJAX Tag, wrapping the actual call of the Event within a Function.
Some of you may not have used Unity before, so you might not be familiar with UnityEvents and the way they were constructed, but they're very similar in design to what I've done here.
For now, instead of using UnityEvents as an example, I'm going to use my ActionImplementations from the Cairo Framework for Unity to demonstrate what I've done.
You can see there are 6 definitions of ActionImplementation, each class definition containing one more Type argument than the definition before it. "T1, T2, T3, ..."
UnityEvents are the same, and so are these Event Signals I've created within Construct. The point is, you call the Event function that has the amount of Arguments you'd like to pass:
The Event's Name and all of it's Parameters are stored within the ActiveEvents Dictionary, along with the Parameter values. These are stored temporarily for the Event that was just called.
To get the Value of the Parameter whenever the Ajax Error has occured, you can simply inline any one of these functions, which give you a different value depending on your needs:
When the Ajax Error is finally processed, a For Each Key loop is ran on the ActiveEvents Dictionary to clear out the Event:
Now, calling the Events and subscribing callbacks is as simple as calling the Call Event Function, and adding a subscriber as Ajax > On Tag Error:
And... It works!
Well, sort of. A problem still exists here. Ajax Errors do not occur on the tick that the Get Tag action is called. On my machine they occur approximately 12 game ticks later. This is kind of a big deal. Imagine setting up signals for your game controls, and you have a signal for the last pressed button. Pressing more than one button in rapid succession would result in the latest pressed button overriding the button presses of all the previous buttons that were pressed, because the Event might have the same name.
You want to be able to call the Event multiple times with different parameters, and often you will want to do that every tick. The time dilation between the Ajax Request and the Error is less of a concern for my own purposes, but I do need to have the correct parameters.
EDIT: Actually, I was incorrectly tracking the time between the Signal and Callback at this point. This has been updated, and both the SignalTick and CallbackTick properly check the time at which they're called. But, the approach given below still works better to ensure proper Parameter values.
That said, I'm going to change my code just a little bit, then I will add to this tutorial. Be back in 15...
Okay! I'm back. I've changed up a few things.
As you can see, instead of using one Dictionary to store all "active" events, I create a dictionary for each Event that gets called, give them the Name of the Event as an Instance Variable, and add the Parameters of the Event as it's content. This way, each Event call is handled independently.
Getting the Parameters picks all the Events that were created with the given Name, and then picks the current Event (which is automatically updated based on the amount of Event calls with the name that occur, decrementing with each discarded Event.
Here you can see that when the "On Error" for the Event is called, the EventIndex for the Events with the given Name are incremented, then when they are removed, the EventIndex is decremented (to ensure the index does not go out of bounds, exceeding the count of Event Dictionaries of the given Name).
To Wrap up, this works exactly as intended. Given the following Event Sheet, the "Event Signal" declares twice, one after the other, and passes the unique parameter values that were passed to the Event.
With this, I leave you the link to the template .c3p which will allow you to plug and play this functionality into your own project. Bringing back the classic Construct 2 functions to Construct 3, at long last, for all to use.