-
-
Notifications
You must be signed in to change notification settings - Fork 21.3k
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
Resource.duplicate(true)
doesn't duplicate subresources stored in Array or Dictionary properties
#74918
Comments
Thank you! I believe I experienced this with 4.0-stable trying to duplicate a custom resource with a dictionary that contained additional dictionaries. I ended up writing a function to create a new resource and copy over the data piece-by-piece, but never figured out why duplicate(true) hadn’t worked. Some additional documentation would save the next person a lot of time. |
- Single points setter, single property, no setter overridding - Copy-pasted shapes are sharing geometry by default now (in line with the editor conventions) - Implement Make Unique toggle with undo support (workaround for godotengine/godot#74918) - SS2D_Point_Array.clone() - A confirmation dialog for "Make Unique" action to avoid accidental misclicks - Fix: Unable to clear SS2D_Point_Array resource in inspector - Fix: Opening a scene with a shape generates change in the scene file even if shape wasn't touched
It's troublesome that this isn't fixed yet, my game relies a lot on duplicating resources and arrays and I was really miffed when I just found out deep copying isn't functioning properly. This should be tracked and added to a milestone. |
This also occurs on all 3.x variants. |
I confim that |
I also met this situation in c#,I set the resourcelocaltoscene = true, and the resource members are not shared except the array and dictionary. |
Does anyone know if this is being looked at or at least planned to be included in a future release? |
I hit this bug during a game jam (v4.1.2). |
I'm guessing one of the issues with this (though perhaps not the only reason it was removed) is because resources can self-reference which would break this. Here is a pseudo-code thought on it: Variant Variant::recursive_duplicate(bool p_deep, int recursion_count, std::unordered_map<Ref<Resource>, Ref<Resource>> refs) const {
switch (type) {
case OBJECT: {
Ref<Resource> resource = _get_obj().ref;
if (p_deep && !resource.is_null() && resource.is_valid()) {
if (refs.contains(resource))
return refs[resource];
Ref<Resource> resource_new = resource->duplicate(true, refs);
refs[resource] = resource_new;
return resource_new;
}
return *this;
} break; This isn't in a working state but I am trying to look at what direction it might be able to go and help the conversation around this as I need this fixed myself. |
Oh my god, I was confused why all my enemy mobs die at the same time like they have shared HP. Had to make multiple loops to manually duplicate subresources in my ready function to work around this. This is rough for stat heavy games out there. |
I came across this issues when duplicating CollisionShape2D in the Scene... but i guess this is using the same function beneath |
Edit: This is not true for |
I've tested this recently in 4.2.1 and no, it wasn't fixed when using |
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
Please don't bump without contributing significant new information. Use the 👍 reaction button on the first post instead. |
Can this at least be updated in the documentation ASAP even if it won't be fixed any time soon? The Resource.duplicate docs confidently state:
Which is clearly not true and causing headaches for many users in this thread. It's now been over 1 year with no comments from the team. |
it's not fix now. has any way to fix this problem? I made a gdscript class, it's extend Resource to implement a function call _duplicate to fix this problem. But please fix this in the engine as soon as possible. the class code is :
|
Edit: The initial problem is that saving and loading subresources just does not work if you simply save and load using ResourceSaver and RexourceLoader IF any of your subresources are actually instanced. ResourceSaver will just save a reference to the project file instance. Now, the "FLAG_BUNDLE_RESOURCES " should fix it, but it is actually not working right now (#65393) If you want to save your instanced subresources you need to deep duplicate them using "BaseResource". It's also important du make deep duplications when you have a resource "template" saved that you later want to add to an array List multiple times. I am trying to use this, together with: #65393 (comment) It does not work unfortunately:
Has anyone managed to save and load resources that contain an Array of another resources? |
Any news on this? It's pretty crucial for a resource heavy game :/ |
Yeah this seems like a pretty major bug to me. It's workaroundable but I don't particuarly like the elegancy of any of the workarounds. |
I'm not sure if this is related or a new bug, but I'm finding nothing is duplicated in my custom resource. I've got a custom resource with a bunch of different variables including But essentially I have to manually copy over every value from one resource to another, which is far from ideal. |
Also stumbled upon this issue after a day of frustration. The docs should really be updated as "If subresources is true, a deep copy is returned; nested subresources will be duplicated and are not shared." is factually not correct. As nobody explicitly mentioned the simple (but dumb) workaround: after creating a duplicate using
|
@AdriaandeJongh |
Resource.duplicate(true)
doesn't duplicate subresources stored in Array or Dictionary properties
It seems to me to be a larger bug with duplication functionality. If I (on 4.2.2, OSX) duplicate an item, then change any of its properties, the properties of the original are also changed. Doesn't matter what kind of node it is or what property it is. Is there a memory reference issue going on maybe? |
Are you duplicating a node or a resource? How are you duplicating it? Duplicating a node does not automatically make its Resource-based properties (or their subresources) unique, with a handful of exceptions (properties marked with |
I would expect the behavior would be that creating a duplicate of a node would make a new copy that is a unique node unconnected to the original. E.g. let's say I have to make two identical decks in a blackjack game, one for the dealer and one for the player, but I want their positions to be different (or, maybe their sizes, etc). The current behavior I'm seeing is that changing such parameters on the duplicate also changes them on the original. |
This is being tracked in godotengine/godot-proposals#317 and godotengine/godot-proposals#4672. |
After thinking for a while why copying resources nested in arrays didn't work, I opened github and searched for this issue as expected. |
Here's the PR I made to add this exception to the docs: #94142 |
For anyone wondering how to circumvent this while it gets fixed: You can create a method in your resource that duplicates itself, then also duplicate each of the elements of the array in a for loop and reutrn the new resource. This should work in the mean time |
This has been mentioned several times with example code above, no need to repeat this suggestion again |
The method in resource that duplicates itself is kind of a pain in the ass if you have a million resources inside of resources isn't it? You would have to make the same method for a billion different resources. This really needs to be fixed |
This doesn't seem to be limited to arrays. I have a resource that contains a node, set on ready, and no matter what I do, all resources on my enemies seem to share that same node setting, even if it's set AFTER the resource is duplicated. func enemy_ready(enemy : Enemy):
var actions := enemy.actions
for i in actions.size():
var action_template : NPCActionResource = actions[i]
var action := action_template.duplicate(true) # Resources aren't unique by default.
if (action.type == NPCActionResource.ActionType.FIRE_PROJECTILE):
var projectile_action : FireProjectileActionResource = action
projectile_action.projectile_position_node = enemy.get_node(projectile_action.projectile_position_node_path)
if (action is MeleeActionResource): # Covers jump as well, since jump inherits from this.
action.melee_origin_node = enemy.get_node(action.melee_origin_node_path)
enemy.actions[i] = action # Overwrite the template resource with the unique one. The problem is specifically on the line All enemies fire projectiles from the same node (so enemy A may be shooting at me, but the projectile fires from enemy F). For reference, here's the fire projectile resource: @tool
extends NPCActionResource
class_name FireProjectileActionResource
@export var projectile_scene : PackedScene
@export var projectile_radius := 0.0
@export var projectile_speed := 20.0
@export var projectile_gravity := 9.8
@export var projectile_impact_scene : PackedScene
@export var projectile_impact_decal : PackedScene
@export var projectile_max_distance := 1000.0
@export var projectile_impulse := 10.0
@export var projectile_damage := 20.0
@export var projectile_splash_damage := 0.0
@export var projectile_splash_radius := 0.0
@export var projectile_type := ProjectileSystem.ProjectileType.BULLET
@export var projectile_delay := 0.3
@export var projectile_position_node_path : NodePath
var projectile_position_node : Node3D
func _init() -> void:
super._init()
type = ActionType.FIRE_PROJECTILE
state = NPCSystem.State.PROJECTILE_WINDUP
next_state = NPCSystem.State.PROJECTILE_FIRE Even if I set the resource as local to scene, it still has the issue. Am I doing a dumb or are non-export values within a resource shared? Edit: Ok, this does actually loop around to being relevant to the topic at hand. Seems the problem for me wasn't the duplicate() of the resource but, in fact, the array of resources on the enemy scene being shared. @export var actions : Array[NPCActionResource] So, after duplicating the array, it fixed the issue: func enemy_ready(enemy : Enemy):
var actions := enemy.actions.duplicate() # Duplicate the array -- seems this is shared for each enemy instance?
for i in actions.size():
var action_template : NPCActionResource = actions[i]
var action := action_template.duplicate() # Resources aren't unique by default.
if (action.type == NPCActionResource.ActionType.FIRE_PROJECTILE):
var projectile_action : FireProjectileActionResource = action
projectile_action.projectile_position_node = enemy.get_node(projectile_action.projectile_position_node_path)
if (action is MeleeActionResource): # Covers jump as well, since jump inherits from this.
action.melee_origin_node = enemy.get_node(action.melee_origin_node_path)
actions[i] = action # Overwrite the template resource with the unique one.
enemy.actions = actions |
Also added hitpoints, visuals, playing cards with effects.
I'm not using the duplicate method, or the duplication functionality in the editor, but this still seems to cause me pain. I have a scene with another sub-scene which has an array of custom resources. I create two separate instances of the first scene using the editor, and they still seem to share the custom resource array. I've marked the resources in the array as "local to scene" but that doesn't seem to do anything. I also tried using the editor's "make unique" feature on it, again to no avail. I'm totally stumped as to what to do. Should I forgo using the editor and create the nodes in code, with the above-mentioned custom duplicate method? No other way around this? Just in case, I've attached a minimal sample project. |
I had the same problem
Both were resources, the only thing that worked was @AdriaandeJongh solutions Otherwise the reference for the array was being kept the same, even with duplicate on the array itself, or duplicate(true) on main resource: |
We were discussing this issue on a discord with @monxa and Yuuto, and here are some key takes:
|
Bugsquad note: This issue has been confirmed several times already. No need to confirm it further.
Godot version
4.0.stable
System information
Manjaro Linux
Issue description
This behavior is poorly documented, and it breaks expectations. It is mentioned in the docs only under
Array.duplicate()
, but notDictionary
class, orResource.duplicate()
.This affects also "Make Unique" and "Make Unique (Recursive)" for custom resources.
Here's the problematic code:
godot/core/variant/variant_setget.cpp
Lines 1889 to 1900 in fc7adaa
The "breaks stuff :(" comment in the code was left in 2018 (d280b14).
Steps to reproduce
For simple code see reproduction project.
To reproduce on your own:
2a) "Make Unique" on parent resource in inspector.
2b) Or duplicate(true) from code.
Minimal reproduction project
res_bug_reproduction.zip
The text was updated successfully, but these errors were encountered: