Functions - A Proposal

I think I mostly disagree with this statement. With this logic, we should move all elements that are non-essential to getting started, like the instance selector, objects groups, layer effects, etc away into a more advanced editor. Those concepts do not need to be thrown at the faces of beginners: the tabs I gave as example are hidden by default, and functions can also be. By default, you are on your scene events sheet, and can just use that, only getting functions if you open the panel to switch to a function. Thereby, you get the power when you need it, without being overwhelmed at the start.

When I say at any time, I did not mean “randomly between two other events”. I meant it just like you would with the javascript events loop: you know that the events will always be called sequentially in order of triggering in between ticks, not in the middle of your normal code, what i propose is like promises. JavaScript being single threaded, it is not even possible to do otherwise anyways. The only exception could maybe be game lifecycle events, for example we might decide to run the object deleted callbacks the moment the “Delete object” action is called, but that is also a very clear and known time of execution. The game remains deterministic. I don’t see any “events callback function” being a problem, not any more than the on object deleted and pre events callback functions extensions already have.

It’s fair to want to delay certain side effects to a certain time, I believe - but I also think that it’s not the responsibility of the different extensions to implement queues, but of the user. Any generic implementation we can currently do either has a big limitation or expects the user to know and implement stupidly complex extra setup.

Example:

P2P dataloss mode - Does not queue, only keeps latest event. Issues: events are missed if more than one is received in a single frame, which usually is the case for multiplayer.

P2P no-dataloss mode - Pops every frame. Issues: Can only handle one event trigger every frame + expects the user’s events to handle P2P events on every frame.

P2P advanced evens handling mode. Pops at the end of a while loop. Issues: Require the user to write a while loop and add an action to pop from the queue a the end of it (& built on top of P2P no-dataloss, so technically still requires users to handle P2P events every frame, although that is a fixable implementation detail and not a problem bound to the queue system).

Multitouch - Empties every frame. Issues: Requires the user to code a for loop to go through all the touches started between two frames + expects the user to handle touch events every frame.

THNK - Pops every time the condition is called (except the first time of every frame). Issues: Events can only be handled in a single place, as after handling all events the queue is empty, limiting usage in extensions, and expects the user to put the condition in a while event if they want all events to be handled within a single server tick (which 100% of the users want).

Basically, since we cannot handle multiple triggers within a single frame without the user having to implement a loop (and a lot of users don’t even know GDevelop have those or how to use these), nor can we know when all intended consumers of a queue have consumed it for certain.

So if I want all messages in a multiplayer server to be process all queued messages in the order they were received, with the server code not running every frame, the best API I can think of would have to expect users to come up with this insanity:

This goes against one of the core goals of GDevelop: it should be self documenting - by simply looking at the actions and conditions, you should be able to have some understanding of what they do and how to use them without having to look up any documentation. The expectation is also bad because, if the user does not use the events exactly like this, they will fall in all sorts of pitfals and “undefined behavior”. Here, a self-documenting, intuitive, fast to write and generally high UX experience would be to just be able to do this:

…which is what event handling functions would allow to do!
Users are not stupid, and will know that the function will not magically execute at the specific moment they need it to if they really need the effects to bee executed at a specific moment of the events loop. Since they know their requirements, and what & how their queues will be used, they can implement them fairly easily:


Or even simpler depending on the type of event:

Additionally, in some cases, you might want to offload some work out of the main events loop, for example if a game client wants to request the game version before fully connecting, queueing the request on the server, makes little sense: it wastes memory and time in the game loop, and it slows down respond times artificially, where responding directly when the message is received would not have any of those problem and not have any bad side effects.

So, to sum up, the way I see it, queues are a poor take at the problem, that artifically limits the user, provides a terrible UX, add a lot of work for the devs, break the single-responsibility principle, degrade performance, and all of that already excluding the compromises each queue in question have to make that create aa fragmented experience, with always some kind of limit being present.
I think that functions events handlers would have very sparse instances of breaking determinism of the game or of executing side effects at a moment where they should not, and that in those cases, users would be able to recognize it and delay the handling of the event by themselves, while removing all pitfalls and limitations, providing a better UX, being faster to create, and providing overall more control and performance.

Well, many many users use them extensively. Especially in level base games: people will have their players, base enemies, platforms, collectibles, etc as global objects and use different scenes to build different levels with the same objects. Sharing events is already possible via external events, but those are pretty inconvenient: they need a linked scene, they cannot be recursive, they cannot take parameters…

Extensions are inconvenient for many reasons: the creation and usage of extensions is relatively hidden away in the IDE, you need to know that extensions are made with events and creatable from the GDevelop IDE, …

In the eyes of a user that never opened an extension before, when they think “I need to make some functionality accessible globally”, never are they going to think of extensions - for any classic user, extensions are a way to install new features in GDevelop, not for their game mechanics. However, a menu called “Global functions” would be something that’d catch their eye, and they’ll find a cool & good way to share logic across scenes. Game specific logic does not make sense logically to put in extensions, even if they could certainly be used this way - in fact, internally, global functions would probably be implemented as an events based extension, as to avoid code duplication.

I think that organisation is already reason enough, but that is not all I see to it.
Functions are not just external events with parameters, they allow for so much more…
Sharability is the least of most user’s concerns, this is a game engine first and foremost, not a social network. Users want ease of access, which extensions do not provide by being far away (not inside the events sheet), in another tab (one opened, an extension has to stay in a separate tab from the scene event sheet), and in a place most people wouldn’t even think of approaching for their game logic (Extensions in project manager). Users want productivity, which making a lot of parameters, for accessing objects and behaviors that do not need any special picking, goes against.

And of course, assuming we do add “function events handler”, people would want to be able to handle them on a specific scene.

Extensions are unsuitable, not necessarily from a functionality standpoint, but from a usability, a UX standpoint, hence why I suggest we expand functions to multiple other scopes.

Yet again, this is missing my point: You can most likely do many things with object functions that you can do with an events based behavior, but this is about giving a more direct access to the tools.

My thinking was also that object functions would have access to their context (other scene objects, or if it is a global object, other global objects), which woud make them more powerful than custom behaviors (e.g. you could just use a “Collider” object group) for the collision in your object’s event sheet to add collisions to it.


Sorry for taking so long to write an answer, my time is sparse and this was a lot to type :sweat_smile: