There’s recently been a few requests, both on the forums and the discord, around dealing with object selection. More specifically, users are running into issues when they have to deal with two instances of the same object colliding.
Doing some digging into how other visual scripting systems do it (Game Maker Studio, Construct 3, etc), GD5 seems to be missing a few different type of “Pick” conditions.
Right now, the engine has the following for multi-object selection:
Pick random
Pick All
Pick nearest
Looking at other engines, it really seems like GD5 is missing the following:
Pick nth object - This seems to index all objects involved in the condition, with 0 being the initial object, 1 being the next, etc. This would be exceptionally useful when checking collisions against the same object, or object group.
Pick Top Z Order - Relatively self explanatory
Pick Bottom Z Order - Relatively self explanatory
Pick Z order (with =></ equations and expressions for values) - Relatively self explanatory.
Honestly, even just “Pick nth object” would help solve a lot of complexities users are facing with object selection when dealing with groups/same objects, but all of the listed options above would be helpful.
Sadly, this isn’t really something I can implement myself as it seems like it’ll need the engine to check the backend UIDs of each instance, but I wanted to go ahead and voice the request.
Thanks all for your time at least reading this far.
Pick nth, or at least a Pick This and Pick That.
Object.This() is in collision with Object.That / Delete Object.This.
Just adding this in case it would be much easier to implement than nth Object.
Two choices would still be helpful.
users are running into issues when they have to deal with two instances of the same object colliding.
I have this with loose scenery pieces colliding with and damaging each other, I use two identical groups, “Scenery” and “Scenery_Passive”.
When Scenery is colliding with Scenery_Passive, Scenery_Passive gets damaged.
I’m looking at it again now and I’m sure I could improve the events, but at least approximately it works because I see scenery pieces getting damaged and destroyed after colliding with other scenery pieces.
Edit: I did improve the events, and now it works even better.
Agree that all of that would be useful. I do not want to introduce pick the “highest/lowest” value for variables, position, etc… because this will create an explosion in the number of actions/conditions. But for Z order, it’s true that it’s fairly common to get the highest/lowest one. A bit less sure about the last one though.
The thing is that I’m not entirely sure we can guarantee the order of objects. This means that from one game to the other, or one version of GDevelop to the other, the “first” object would not be the same.
Also not sure this is the best solution to the problem, the problem being: “How to differentiate MyObject when I write MyObject is in collision with MyObject”.
So if we go back to the problem:
Can we explain more what are the issues? If two objects collide, what do you want to do exactly? You could use sub events to differentiate them according to somethings (like were they moving or not, to add some damages of things like that).
What you can’t get currently is the “pairs” of colliding instances (which object instance was colliding with which one), but this is 1) the same if you enter two different objects and 2) would not be solved by a Pick nth?
It’s not obvious but indeed when you “pick all objects”, you basically start again and the initial state, which is: “Consider all the objects, then maybe the next conditions will refine the picked objects”.
So, I’m not 100% sure on this, but my minimap example works by checking collisions on a set of created objects, and those objects always end up with the same object # in the debugger every time it is launched. I’m not sure this is pure luck or if objects are always going to test in the same order, or what.
So this presents itself in numerous different ways, but I THINK it is always collision focused. Here’s a few examples I remember from the discord:
You are making a tetris-like game/block like game. All of your different piece objects are in a “tetromino” group. You want to set up an event that is “Separate objects” between two different members of the group, and only want to force the one that is already moving to be separated. In other engines, the one that is moving is the one treated as “initiating” the collision, so it is the first in the object list. You’d pick the “first” object and move only that one away, or if you wanted to move the “stationary” object you’d pick the second/third/last/etc object. Even with object IDs today, you cannot effectively target the different objects.
Similar to the above, you’re making a jigsaw puzzle game. All of your events require the puzzle pieces to be in a group, but when you drop a puzzle piece you want the piece dropped to be moved/snapped to the correct position (separated from the existing jigsaw piece) and the stationary piece to remain stationary.
I found that a lot of engines have this same type of issue, but have this pick nth behavior as a way to solve it. I feel like it could be important to clear up the above type of confusion:
With all of the above in mind, I do also think there are benefits of something that says “Pick Nth object” as a condition in general, even ignoring just collisions, as it would let you pick whatever order of object meets the conditions in the same box. I would think this would just work based off whatever order they show up as in the debugger, since that does truly seem static for me:
With the Multiplayer Platformer with gamepads example, the first coin on the lower left is always#40 for me, no matter how many times I restart, or even if I create a whole new example project.
So this presents itself in numerous different ways, but I THINK it is always collision focused. Here’s a few examples I remember from the discord:
You are making a tetris-like game/block like game. All of your different piece objects are in a “tetromino” group. You want to set up an event that is “Separate objects” between two different members of the group, and only want to force the one that is already moving to be separated. In other engines, the one that is moving is the one treated as “initiating” the collision, so it is the first in the object list. You’d pick the “first” object and move only that one away, or if you wanted to move the “stationary” object you’d pick the second/third/last/etc object. Even with object IDs today, you cannot effectively target the different objects.
Similar to the above, you’re making a jigsaw puzzle game. All of your events require the puzzle pieces to be in a group, but when you drop a puzzle piece you want the piece dropped to be moved/snapped to the correct position (separated from the existing jigsaw piece) and the stationary piece to remain stationary.
For Zuma-like and Bust-a-Move-like examples, I used 2 objects:
a thrown bubble
board bubbles
When the thrown bubble is added to the board, it’s replaced by a board bubble. I think it allows clear events and I wouldn’t do it otherwise even if I were using another programming language.
With all of the above in mind, I do also think there are benefits of something that says “Pick Nth object” as a condition in general, even ignoring just collisions, as it would let you pick whatever order of object meets the conditions in the same box. I would think this would just work based off whatever order they show up as in the debugger, since that does truly seem static for me:
With the Multiplayer Platformer with gamepads example, the first coin on the lower left is always #40 for me, no matter how many times I restart, or even if I create a whole new example project.
The instance order probably follows this logic:
It’s the order they were added in the editor which is impossible to change in the editor without removing every instance and adding them one by one (and this index is should stay hidden).
When instances are filtered by conditions, the indexes are changing to feel the wholes.
None of the 2 previous points are part of any contract so they can change when needed for features or optimizations.
This is why I agree with reservations expressed by Florian and I think that relying on this index is against good practices and should not be used in extensions.
For context here, order also seems to stay the same if created via events with repeats, so I think the logic is still sound. Maybe due to potential risks, removing Nth may make sense, but first v last seems like something that would make sense to have an option for, if nothing else.
Also, your multi-object solution solves that specific scenario, but doesn’t solve collision between same instances or many other use cases. The solution also seems pretty uncommon compared to the other engines out on the market from a logic perspective, by which I mean GDevelop does not have a native function for this compared to:
Defold does this by having the concept of “Self” and “other”, even within instances of the same object.
CopperCube seems to do this by having bespoke actions for collisions in their scripting, “Pick first impact” and “Pick Last impact”, basically.
While I recognize the method used in the current community extension may not be ideal, rendering it inoperable reduces not just ease of use for game developers, it reduces functionality of GDevelop.
I’m not saying that the current method must be maintained, but some method needs to be maintained and should not be a far future roadmap item. Whether that’s a different iteration of the current concept from the extension, adding some form of “This” and “Other” instance concept, adding a publicly visible UID, or something else entirely is fine, but some method that matches what is available on the engine market elsewhere is important.
Edit:
Interestingly, GMS 2 actually maintains a list of objects within collision altogether, which you can also pull from directly. collision_line_list
I don’t understand how “first” and “last” can be useful for collision when the same object is used in both parameters because what make them different from each other?
It doesn’t help for your previous example, does it?
Similar to the above, you’re making a jigsaw puzzle game. All of your events require the puzzle pieces to be in a group, but when you drop a puzzle piece you want the piece dropped to be moved/snapped to the correct position (separated from the existing jigsaw piece) and the stationary piece to remain stationary.
From what I can tell, in other engines the “first” is the first item in the collision list, the last is the last item. How that’s defined is different per engine. For this specific use case, what you would want to happen is to pick whichever object was first added to the currently selected object list.
I know that the current implementation isn’t exactly that (I think it’s picking the first created instance?), but it is still more useful than a Pick Random, or in many cases pick based off ZOrder.
What I meant is that is that both instances are from the same object so why would one be treated differently than the other based on “last” or “first”?
For instance, if we suppose that only one collision happens at the same time, I could want to:
check collision between instances of the same object
pick the instances that have not the greatest speed to explode them
But, I think the condition will always be something specific about the instances state and never about “first” and “last”.
Can you explain what “first” and “last” means with a practical case?
Yep, here are some of the examples I’ve seen when digging up the above links:
Matching the current method from the extension (First created/last created):
Having a mirrored instance player character that does the opposite of the player’s inputs while the player is moving around normally. The instance gets created/destroyed depending on the scenario, but by having the “last instance” condition/logic they were able to keep all of their event logic for damage/attacks/etc and just invert the movement controls if it is (or is not) the last instance.
A game where they were playing a duck being followed by a bunch of little ducklings. Every egg you found would create another duckling following you, and if you took damage the last created duckling would get destroyed.
Could you do some of these by manually creating IDs and then looping through them all to find the highest ID? Sure, but that also would be much less efficient and much more variables to maintain manually. Maybe a better name would be “Pick first created instance” and “Pick last created instance” to match the current scenario. Same with Pick Nth created instance.
Matching the other scenario I mentioned above (First to the current list/last to the current list) from some of the discussion I saw while digging around the GMS2 stuff, it was mostly the collision scenario I had mentioned before.
It allows to queue objects. It needs a bit of setup because the queue needs an object to hold the stacks but it has many features:
Instances can be dispatched in several stacks
Instances in a stack can comes from a group and you still have full control over the instance order
Stacks can contains stacks (instances from a stack can have the behavior too and contains other instances)
Stacks can be split and merged
Instances can be inserted or removed at a given index
Stacks can be shuffled
Instances from a range of indexes can be picked
I did a Zuma-like and a Klondike with this extension. They have complicated mechanics that couldn’t be built just relying on creation order.
I don’t understand the use-cases about collision. What you said about accessing each group of instances in collision is interesting but I guess it was not your point.
Could you do a screenshot of some events? I think I’ll understand better this way.
So I follow what you’re saying on these, but I think we might be speaking to slightly different points. This isn’t so much that some of these things are impossible to do today, it’s just much simpler with more picking options.
The above doesn’t seem to be true from what I can tell running through it. Having an isCloned boolean is another event action and condition you have to track, manage, and flip as needed. Vs a single condition of “Pick last (created) of Blah” in the 4 movement events for the mirrored object.
To reiterate, it’s not that these things can’t be done without this pick functionality, it’s that the level of effort and user experience is much simpler via something that you can use to just say “pick the last item”.
The object stack extension is very powerful and useful. but it contains much more setup, (behind the scenes) event overhead, and manual event maintenance than a single “pick last” or “pick first” would be for the duckling scenario above.
Overall the additional picking options help users articulate and implement the base concepts they’re trying to do much faster. If they need to get into more involved scenarios, then I agree that the high level picking conditions/actions wouldn’t apply. That same thing could be said for stuff like “pick random” or “pick all”, however.
Pick First/Last is more of a user experience/quality of life need, even though there are other more complex/alternate ways to accomplish them technically.
Pick “nth” might be an actual technical need (the ability to pick a specific instance out of a list of them) that you cannot easily accomodate today (without manually IDing each instance). Surfacing the “Creation order” (Or UID, if that increments?) as an attribute of object instances may accommodate this, but that’s probably a deeper conversation than the above.
There are no antipatterns from the above (the concepts, not necessarily how it’s enabled in this extension), the creation order of objects are already something that exists somewhere in the backend of the engine, the UIDs of the objects are are already somewhere in the backend of the engine, etc. The need is more about exposing those in a user friendly way for folks that need access to that more immediately.
Other engines accomodating this in the ways mentioned above show that it isn’t an isolated need, either.
the boolean must be set after creating the clone (but variables are a key concept of GDevelop)
and the “pick last” (which is a new concept to learn) is replaced by a variable condition
it also avoids a comment like “pick the cloned instance because it was created last” because the variable name speak for itself
So UX side, I think variables are better for this case at least.
Allowing concise events is important but there is also to consider:
readability, saving time writing a code is often not a good idea if it’s hard to understand it later
flexibility, conditions and actions that can allow to build from simple to advance logic is less concept to learn
For these 3 aspects, I need use-cases to have an idea.
The extension was relying on the picking index. The picking index can change according to algorithms used to filter instances (R-trees completely change instances order for instance).
Relying on the creation order (the UID) would indeed make a lot more sense. But, use-cases must prove its usefulness. Because for UX, less is better.
Features don’t necessarily come from needs. Maybe it was added because it’s in every engines and easy to implement. I want to make sure it can be better than any existing solution at least for a common use-case.
I think the UX side is fixed if the condition is updated to “Pick lastest created instance (object name)” or something similar.
Then you have a single condition to accommodate the mirrored player scenario, and don’t need to add comments to explain comment. Less variables to maintain in this method, too.
I think the ducklings scenario actually speaks to flexibility and legibility.
As an example, it really is just a single event with conditions of:
PlayerDuck is in collision with DamagingObject
Pick Last Created instance of Ducklings
Trigger once (or a timer, or a knockback variable, etc)
And action of:
Delete last duckling
That’s a pretty clean event, makes sense at a glance, and doesn’t require comments. No setup required for tracking the ducklings, etc.
As far as exposing the UID in some way (even if it is a translation from the backend UID to just a truncated “ID” parameter on objects that’s just an incrementing number), that eliminates a lot of manual variable work if you’re applying IDs on creation, or a For Each event at the beginning of the scene if you’re applying IDs after loading data, etc.