-
Notifications
You must be signed in to change notification settings - Fork 57
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ObjectStack extension for card games #218
Conversation
Looks awesome! I've not checked the internals yet, but the video is very impressive. Definitely looks like a great example to add :) |
Finally got the time to review this! :) I think the main thing is the usage of In the case of your extension, this means that if for example the I don't have a good solution for now - but I have the feeling we're exposing some strange behaviors here if we have more than one "stack object" having the behavior and used at once. Not sure if we can do better, but I can already see confused users asking themselves why only some cards in their stacks are picked by the condition when they have multiple stacks 🤔 Does this make sense? |
Actually I do have a good solution! Which is to use the API made to filter two list of objects at once: This means that the behavior methods that are using You can then call Let me know if you give this a try. I can also take a try at this if you wish! |
Ok, thanks, I'll try this. |
I committed to save a version before using |
I updated the 2 examples to use the new conditions. |
These new conditions look great! I'm not sure what's the best solution, surely to hook again like you did in the other extension to a gdjs callback to listen to object destruction? This might be a pain because you have to inspect all the stacks... or keep a map of "object id to their stack object" - so that whenever an object is deleted, you can quickly get its stack (if any) and remove it? If this kind of behavior is becoming more common, we could introduce something in GDJS directly, something like a "RuntimeObjectWeakReferenceMap" (sorry for the long name, but you get the idea), that would help doing this for an extension (storing references to objects, and automatically dropping them if the object is deleted). |
An inverted index will allow to make the conditions more efficient anyway. It will also allow to ensure uniqueness too, because I think allowing to add the same instance in one stack twice is probably not a good idea. |
I wanted to make a semi-related feature request for a long while now actually. A very common pattern with behaviors is to use some of them to "Mark" some objects to be used by another one, similarly to an object group. See for example the LightObstacle, Platform, and Pathfinding obstacle behaviors. Making those with events is possible via the usage of scene variables, but is quite tricky and limited. To make that easier, my idea is to expose to all functions in extensions for each behavior of the extension a group that references all objects on the scene with that behavior. They would work like normal object groups, except that instead of using
The type of that object group would be the same as the Object parameter in the Behavior functions. This would allow for many powerful things:
|
Sounds good!
Let's talk about this in 4ian/GDevelop#3001 My main concern is that naively iterating on behaviors will lead to bad performances as soon as the number of objects grow. |
Actually, I only used sets because an inverted index would make it a lot more complicated for little value.
|
Is it a bot issue: |
This problem should be gone because the registry does not need to be rebuilt and pushed anyway now. |
❗ An internal error has occured. See logs at https://github.com/GDevelopApp/GDevelop-extensions/actions/runs/1334007416. |
Probably an issue due to the branch to merge from being on a fork, but actions run on the main repository. |
'RuntimeObject', | ||
], | ||
gdjsEvtToolsAllowedProperties: ['object'], | ||
runtimeSceneAllowedProperties: ['__allObjectStacks', '__allUsedObjects'], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry I just noticed this now.
__allUsedObjects
=> this is way way too generic, there is a risk for collision with another extension in the future or user code that would want to use something like this for another purpose.
Instead, let's name it something like__objectStacks_allUsedObjects
.
Basically, any name that is on runtimeScene/gdjs is a hack and we should use names as long and precise as possible.
The best solution would be to provide an extension and runtimeScene specific storage for each extension, so that no clash can ever happen.
Extensions/ObjectStack.json
Outdated
"disabled": false, | ||
"folded": false, | ||
"type": "BuiltinCommonInstructions::JsCode", | ||
"inlineCode": "const object = objects[0];\nconst behaviorName = eventsFunctionContext.getBehaviorName(\"Behavior\");\nconst behavior = object.getBehavior(behaviorName);\n\nbehavior.objectStack = [];\n// Make contains(), remove() and unicity checks more efficients.\nbehavior.objectSet = new Set();\n\nif (!runtimeScene.__allObjectStacks) {\n runtimeScene.__allObjectStacks = new Set();\n // It's only use is to have a O(1) check for\n // deleted objects that have never been in a stack.\n runtimeScene.__allUsedObjects = new Set();\n // Remove from deleted objects from stacks.\n gdjs.registerObjectDeletedFromSceneCallback(function (runtimeScene, obj) {\n if (runtimeScene.__allUsedObjects.has(obj)) {\n runtimeScene.__allUsedObjects.delete(obj);\n for (const behavior of runtimeScene.__allObjectStacks) {\n /** @type {gdjs.RuntimeObject[]} */\n const stack = behavior.objectStack;\n /** @type {Map<gdjs.RuntimeObject> */\n const objectSet = behavior.objectSet;\n if (objectSet.has(obj)) {\n // There should be only one occurrence, but check the whole array just in case.\n for (let index = stack.indexOf(obj); index >= 0; index = stack.indexOf(obj, index)) {\n stack.splice(index, 1);\n }\n objectSet.delete(obj);\n }\n }\n }\n });\n}\nruntimeScene.__allObjectStacks.add(behavior);", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's only use is to have a O(1) check for
=> Its only use is to have a O(1) check for
Thank you! 👍👍 |
Description
An ordered list of objects and a shuffle action
It provides:
It can be helpful for:
Card System Example
Klondike example