Resources Shared Despite Being Local to Scene

Godot Version

4.3

Question

I have an array of Resources (Supply). The Resources have been created via the editor. local_to_scene is ticked.
image

The problem is that both Actors on the right are being damaged, despite the damage only being applied to the rightmost one (see the fire). As it isnt applied to the other actor, is it something to do with affecting every instance of the scene, rather than just a specific instance? If so, how would I have the resource be specific to the instance?
Godot_v4.3-stable_win64_jYt92cQKYh

Here’s the relevant Actor’s Scene, showing the local_to_scene still ticked.
image

The Resource is a Supply class. Here’s the (slightly reduced) code:

## info regarding a changeable value, such as health or mana.
@icon("res://assets/node_icons/supply.png")
class_name SupplyComponent
extends Resource

#region SIGNALS
signal value_changed() ## the resource value has changed
signal value_decreased(amount: float) ## the resource value has decreased
signal emptied() ##  there is none of the resource left
signal max_value_changed() ## the resource's max value has changed
#endregion


#region EXPORTS
@export_group("Details")
@export var type: Constants.SUPPLY_TYPE  ## @REQUIRED.
@export var max_value: int = 999:  ## @REQUIRED.
	set(value):
		max_value = clamp(value, 1, INF)
		if max_value < value:
			set_value(max_value)
		value_changed.emit()
@export var regeneration_per_second: float = 0
#endregion


#region VARS
var value: int:
	set(value):
		push_warning("SupplyComponent: Can't set value directly. Use funcs.")
	get:
		return _value
var _value: int = 999:
	set(value):
		if value > max_value:
			_value = max_value
		else:
			_value = value
		value_changed.emit()
		# Signal out when health is at 0
		if value <= 0:
			emptied.emit()

#endregion

The full repo is here, if that helps.

I need more info because in the gif that you have uploaded, it seems like both units are being hit with only one projectile which would make me want to place a Breakpoint at the point of the projectile hitting the enemy.

Does it send a signal? maybe every enemy picks up on the value_decreased() signal even though it should only be affecting the one enemy being hit.

1 Like

No the resources are not shared. The problem seems to be elsewhere, since the visual effects are also applied, except for the fire. They have their own health, but they are hurt when one of them is hurt, which leads to them dying at the same time

1 Like

I think whats happening here is, that the chain-effect is applied veryfast back and forth between the targets. Thats why they both get damaged and die.

BTW i love the little icons you put to the nodes

2 Likes

I just checked out the code and I believe the crux of the problem is in your deal_damage_effect.gd line41: supply.decrease(damage)
This will then decrease its value and then emit the signal.
Who is listening to the signal? I believe it is every single Combat Actor that has this component installed.
combat_actor.gd line58 and line60

Don’t take my word for it though, I just briefly skimmed through the code

1 Like

Thanks very much both.

it seems like both units are being hit with only one projectile which would make me want to place a Breakpoint at the point of the projectile hitting the enem

I think that is just how it looks, though I’ll check. The collision stuff was working fine until this move to Resources. I’ll check!

they are [both] hurt when one of them is hurt

You could well be right. I thought it was because they are sharing an instance of Health (the Supply), but absolutely could be something else.

I think whats happening here is, that the chain-effect is applied veryfast back and forth between the targets.

Hmm. :thinking: I was using Nodes for the Health and that didnt happen, so with the only thing changing being moving to inherit from Resource rather than Node I didnt think it was that. I’ll double check, though.

i love the little icons you put to the nodes

Me too! They’re from this: Justin's 16x16 Icon Pack by Justin Arnold. If any of the icons look jankier than the others it is because I amended them. :stuck_out_tongue:

I just checked out the code and I believe the crux of the problem is in your deal_damage_effect.gd line41: supply.decrease(damage)
This will then decrease its value and then emit the signal.
Who is listening to the signal? I believe it is every single Combat Actor that has this component installed.

That should be easy enough for me to confirm, as in that case the Health would only change for 1 of them.

Will report back asap.

Despite only 1 being hit, both are effected. Not collision issue.
Godot_v4.3-stable_win64_rHLqhPshWW


When applying damage (i.e. reducing health) we use a specific CombatActor, so doesnt seem to be accidentally affecting all of them.

class_name DealDamageEffect
[...]
## reduce health of target
func apply(target: CombatActor) -> void:
	var supplies: SupplyContainerComponent = target.get_node_or_null("SupplyContainer")
	if supplies is SupplyContainerComponent:
		var supply = supplies.get_supply(target_supply)
		var damage = _calculate_damage(target)
		supply.decrease(damage)

I find the Resource bit really confusing. The object ref in the debugger doesnt match the one in the Remote Scene Tab.

supply variable:
image
Click to inspect and I get:
image
If I go through the Remote Scene Tree and hover over what would be Health in the SupplyContainer, we see the same ref as the debugger:


If I click on that object in the Scene Tree, I get the object ref we saw earlier, but not the same one shown in the SupplyContainer or debugger.
image


HorseyRider.SupplyContainer[0] and HorseryRider2.SupplyContainer[1] share the same object reference.

So, it has to be that it is still a shared resource, doesnt it? :confused:

Okay yes they should have different object-ids, so your first assumption appears to be correct. This might be a caching-error, you can try to delete the cache and see if it still happens.
I once had a problem where the local_to_scene was working but sadly i dont remember how i fixed it

1 Like

Cleared cache (deleted .godot folder). No change. Resources all still listed as local to scene = true. :confused:

Any other ideas? I am well lost on this one. I think this is the same reason I did everything as hardcoded .gd scripts last time I tried.

Removed the Supply Container Node and readded it. Now all the actors are affected, instead of just the ones sharing the HorseyRider scene. :person_facepalming:
Godot_v4.3-stable_win64_uQ7KFff6by

Combat Actor:
image
Wolf Rider:
image
Horsey Rider:

So lost now. :face_with_spiral_eyes:

I still believe when you emit the signal of value_decreased() signal, that you should also include an id of the combat actor that you want to address or else every combat actor will receive the signal and then say “Okay, this signal is for me. I need to take damage.”

I haven’t implemented Components yet but I have the firm belief that every actor should know which component it uses, and also every component should know who it belongs to.


How about every component have a variable called owner and whenever a combat_actor initializes their components, they also pass themselves along as a parameter so the component saves it as the owner.

Whenever the component receives a signal, it also checks the id if it’s the same as its owner. If not the same, ignore the signal and return

1 Like

in your combat actor can you print out the health?

if _supply_container is SupplyContainerComponent:
		var health = _supply_container.get_supply(Constants.SUPPLY_TYPE.health)
        print("My health: ", health)
		health.value_decreased.connect(_on_hit_flash.activate.unbind(1))  # activate flash on hit
		health.emptied.connect(func(): died.emit())  # inform of death when empty
		health.value_decreased.connect(_damage_numbers.display_number)

they should technically all have their own object-id. if they all print the same object-id, they share the same health. That would mean that this is a bug and you should probably open up an issue on github

1 Like

@Locher very happy to give that a try when back at the computer later, though I’m unsure why that would only be an issue after moving to inheriting from Resource instead of Node (unless some other change I am forgetting caused it! :wink: )

@herrspaten will check when back.

Thanks again, both!

1 Like

For the start, you could print out the name of the parent_node when it goes into the handler of whenever the damage number is displayed. Our goal is that when the projectile hits one enemy, only the affected enemy should receive damage and show the damage number.

If the damage number is displayed more than one time, then we will slowly walk back the code procedure to find out if it’s a resource-sharing problem or a signal-oversight.

1 Like

This is a bug. Looks like there have been a few patches to fix related problems, but they didn’t solve this specific issue. Don’t forget to give it a thumbs-up vote on GitHub. For now, you would have to work around this by duplicating the resource in your script.

2 Likes

Actually, I forgot that exported arrays instantiated at runtime are never duplicated. Each instance is using the same array. :sweat_smile:

2 Likes

Added print on CombatActor as follows.

class_name CombatActor
[...]
func _ready() -> void:
	if _supply_container is SupplyContainerComponent:
		var health = _supply_container.get_supply(Constants.SUPPLY_TYPE.health)
		print("CombatActor._ready:", self, " has this health component: ", health)

Which resulted in this:

CombatActor._ready:WolfRider:<RigidBody2D#55448700198> has this health component: <Resource#-9223371979996789257>
CombatActor._ready:HorseyRider:<RigidBody2D#56035902797> has this health component: <Resource#-9223371979996789257>
CombatActor._ready:HorseyRider2:<RigidBody2D#56472110477> has this health component: <Resource#-9223371979996789257>

Also added print to pop up numbers, which returned this from a single instance of damage:

PopUpNumbers.display_number: called on WolfRider:<RigidBody2D#55448700198>
PopUpNumbers.display_number: called on HorseyRider:<RigidBody2D#56035902797>
PopUpNumbers.display_number: called on HorseyRider2:<RigidBody2D#56472110477>

So does that confirm that they’re all sharing the Resource?

@trickster721 ah, I saw the fix listed for 4.3 and assumed it was sorted. :man_facepalming: Found this workaround suggested. All seems to be working now!

Result:
Godot_v4.3-stable_win64_94ZM9N0XgN
Code:

class_name SupplyContainerComponent
[...]
@export var _editor_supplies: Array[SupplyComponent] = []  ## this is a wrapper for _supplies, due to godot's issue with arrays always sharing resources.
var _supplies: Array[SupplyComponent]  ## all supplies. copied from _editor_supplies on _ready.

func _ready() -> void:
	_duplicate_supplies_array()

## duplicate all supplies in _editor_supplies to _supplies
func _duplicate_supplies_array() -> void:
	for supply in _editor_supplies:
		_supplies.append(supply.duplicate(true))

Thank you to everyone for all of your help!

P.S. can I do something to make this easier to help others with the same issue?

2 Likes

Yes they share the same health-resource as they have the same object-id.

I think the marked solution is automatically linked to the original help-message, which should help people to just click on it and see the solution. So you dont need to do anything

1 Like

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.