Pick by instance?

I read the following by a core contributor @arthuro555

In GDevelop, there are no object IDs. You got to think the other way around: instead of having a list where you put objects in, you put attributes on objects. You can then operate on the objects of your liking using object conditions to tell GDevelop what conditions you would like an object to meet in order to have your actions applied to them.

I disagree. Functionally speaking whether you are picking by a purposely designated variable OR an internal UUID - they’re both filtering by a condition. There’s not really a difference between a variable called “objectName” = “XYZ” and an internal UUID called “XYZ” for some object.

The only difference is that if GDevelop provided UUID automatically it would save us (the users) from having to remember to designate a randomly generated arbitrary ID value in an instance variable at object creation time.

Let’s say I have many random Vacuum object that move around the level and can “eat” other objects. The logical thought process behind something like this would be this:

1. Give Vacuum a Set/Array called "EatenObjs"
2. If Vacuum hovers over object X then add Object.UUID to EatenObjs array

There are thousands of situations where a game might need to manage an array of pointers to objects. Those objects might have nothing in common with each other. The easiest way to identify them is with… an identifier.

It’s not like offering a built-in UUID somehow is mutually exclusive with filtering by other instance variables.

Trying to run through some mental gymnastics to contrive a set of instance variables that can uniquely identify an instance of an object when there exists in literally EVERY other programming environment some REF id equivalent seems like a baffling design decision.

Additionally, if GDevelop is positioning itself as a learning tool for prospective devs, they’re going to be rather surprised when they graduate to more industry standard offerings (Unreal Engine, Unity, etc) and find out that everything has a unique ID (be it a designed GUID or a reference pointer in memory).

To be clear, in case I wasn’t in that older post of mine, what you are asking to do here is arguably easier to do in GDevelop. The way you’d do it is simply have an “isEaten” boolean on an object variable. To add an object to the set of eaten objects, you would simply set that boolean variable to true, and to iterate over/check for an object’s presence in the set/check if the set is empty, you can simply do use the “The variable isEaten of the object is true”:

GDevelop’s object management system is direct and declarative. An UUID system is an unnecessary indirection that makes code less natural and unnecessarily hard to reason about, when there is an easy and more idiomatic way to do it.

I disagree. If you can come to a point where you can identify an object needs to belong to a certain group of objects, you have a handle to the object and can set a boolean or other kind of variable on it. Again, an external data structure of UUIDs is an unnecessary abstractions.

That’s a terrible non-argument. Every engine have different APIs and design, and saying some other engines have a pattern is in no way an argument for others to adopt it.

@arthuro555

Ridiculous. Yes isEaten could cover this one example that I made up. You missed the point entirely. What if I have a bag of non-related items, and they could be in in GameObject A “BagOfItems” and GameObject B “BagOfItems”.

Hell what if there are an unlimited number of GameObjects all of which have a concept of “BagOfItems”. Furthermore, these items are mostly unrelated objects. And now lets presume that an item can be shared between these GameObjects.

So:

GameObjectA.BagOfItems = [UUID_1, UUID_3, UUID_4]
GameObjectB.BagOfItems = [UUID_3, UUID_4, UUID_5]
etc.

These pointers can be moved around, shared, etc. Now what do you do? Boolean won’t work. Anything else would be inelegant at best.

And deliberately removing the ability to reference an instance by a UUID just to make the engine “distinct” is an equal non-argument.

And just to be absolutely clear, there’s a reason that I’ve seen a number of posts in GDevelop with kludgy workarounds like:

It’s because there are many situations where being forced to create a venn diagram of conditionals that uniquely and more importantly persistently identify one object when a simple and logical ID would suffice is unnatural and confusing.

Having both the ability to filter by conditions and the ability to filter by UUIDs are not mutually exclusive concepts. Scirra’s Construct released a year before GDevelop and has support for both.

EDIT: Actually looking at your isEaten example - it ignores the problem. The stated goal was that each vacuum knows exactly the list of objects that it has eaten. Your boolean only tracks whether or not an item has been eaten.

Indeed, I misread and misunderstood your example and point, I apologize. My understanding was that you were arguing there is no more better way than using an UUID list/set for keeping track of a group of objects in general, which is a point I would’ve disagreed with - as I have shown, object picking allows in many cases for much more elegant, declarative, and easily parse-able by even a non-coder.

I agree that in many such niche cases, GDevelop is currently lacking. However, I do not believe an UUID is an elegant solution, although my idea of a suitable solution would be practically not much different - a dedicated object list variable type, that one could directly use in place of an object name in events.

This would have multiple advantages over a UUID:

  • Less of a learning curve: A UUID is a relatively abstract concept that would give many users of GDevelop a more difficult time. A list of objects is easier to understand, and would allow for a more natural language scripting approach (“Add picked instances of Object to an objects list” instead of “Append text Object.UUID() to an array variable”)
  • Better usability: You skip the translation from UIDs to objects, you just use the variable with the list in place of an object type in events
  • Better performance: An list of direct references to objects can just be iterated over in-place. UUIDs translation needs a lookup from a hashmap and would be much more inefficient (and kill GC optimisations)
  • Better correctness: if an object doesn’t exist anymore, it will not be taken into account even if is still in the objects list (we already have the code in place for objects to remove themselves out of long lived objects lists on deletion, originally made for storing the picked objects lists while waiting for asynchronous events to finish)

It would technically have some restrictions compared to an UUID, because of the facts that objects lists are sets and if you need to track an order or to store objects multiple times in the same list it will not be possible.
Personally I cannot think on the top of my head of any cases where that would be the case where it wouldn’t be better to store the data in a separate data structure like a scene or global variable anyways (e.g. inventories), but feel free to prove me wrong.

I never said we were doing it to “make us distinct”, the point is that the GDevelop team believes UUIDs are an anti-pattern that tends to make code unnecessarily harder to reason about, in the context of GDevelop at least. The fact Construct or any other engine implemented an object uuid system is completely irrelevant, and the fact that it is possible to implement is irrelevant.

The lack of IDs are a design decision of GDevelop because the Team genuinely believes that makes it a better engine, in the sense of bringing it closer to GDevelop’s goals.

Most game developers disagree with GDevelop’s design already just for being based around visual scripting or for being coded in TypeScript. If you dislike the GDevelop Team’s design decisions, maybe you should try forking it or using another engine, because at the end of the day GDevelop is a product of the team’s vision and ideas, if you feel these do not align with your own opinions, GDevelop’s may not be an engine for you.

To make my own perspective clearer:

In my personal experience browsing the various socials of GDevelop, my impression has been that most of the time such a workaround is used, it is not because it is necessary, but because the user did not understand object picking and/or linking features correctly and assumed that they need to do this, when there was an easier, idiomatic way to do what they were trying to do in GDevelop.

Since these are solutions that technically work and resolve the problem, although unideal, overly verbose & complex, compared to the idiomatic solution, these solutions get reshared often by members that are relatively active in the communities yet relatively new and inexperienced with GDevelop, and misinterpret this as the only solution.

Because I have seen users do so oftentimes, I jumped to conclusions, and assumed that you were suggesting we need UUID lists for all things object picking/filtering, when conditions can offer a much nicer and elegant solution a lot of the time.

Good conversation @arthuro555 .

I’d posit that being able to easily model any kind of tree (or many-to-many) ontology with simple container lists is a pretty common use case for any game of reasonable complexity, but maybe it’s a non-issue for the vast majority of GDevelop users.

I also think as a person with dev experience, the idea of being able to filter by instanceA.UUID just brings a bit of peace of mind since I can always know that such a filter can only return 0 or 1 instances depending on whether that instance still exists.

It does sound like this is the design philosophy of GDevelop and more of an ideological debate than necessarily a technical one. I think for now I will likely continue to develop in Unity and Defold but may take a look at GDevelop at a later time since it does have a lot of strengths.

Cheers.

1 Like