If i change one duplicated nodes all the others change: help

Godot Version

4.3

Question

So I created an inventory scene with a gridcontainer filled with TextureRects. Works great.
But when I make a scene out of TextureRect and instance that in the Inventory scene, now everytime I change one of them, they all become the same inventory item. Here’s the code for the ItemNode

extends TextureRect

var id: int = 0
var item_name: String = ""
var item_region: Vector2 = Vector2(0,0)

var item_region_dict = {
	"apple": Vector2(512,288), "sword": Vector2(544,448), "armor": Vector2(554,336), 
	"cheese": Vector2(576,288), "helmet": Vector2(544,320), "shield": Vector2(528,352),
	"bow": Vector2(656,368), "staff": Vector2(608,384),
	}

func define(new_id, new_name):
	id = new_id
	item_name = new_name
	self.texture.region = Rect2(item_region_dict[item_name], Vector2(16, 16))
	

In my inventory scene, if I call $Slot1.define(1, “apple”) and then $Slot2.define(2, “sword”), all the slots become swords.

I’ve looked online and some people say you would make the instanced scene local, but that doesn’t help. Others say you should check editable children, but that doesn’t work either.

I’m very confused. I’ve make a small monster game before and every time I instanced a monster, they were unique and all the monsters didn’t become the most recently instanced monster.

I assume the scene you’re instancing is using an AtlasTexture resource to select the region.

When you instance your TextureRect scene, each instance will be referencing this same texture resource, not a copy. So when you change the texture.region in one instance, all of them see the new value, because there’s only one texture shared between them.

The way to fix this is to go into your texture resource and check the “Local to Scene” checkbox. This makes each instance of the scene get a copy of the texture resource, and your problem should get fixed.

image

2 Likes

I want to extend @apples advice and recommend to create separate AtlasTexture(s) for every region, this will make easer to manage atlas

	var textures = {}
	var origin_texture:=preload('uid://dyln7ttsrevjb') #texture_atlas.png
	var apple:=AtlasTexture.new()
	apple.atlas=origin_texture
	apple.region=Rect2(Vector2(512,288), Vector2(16, 16))
	item_region_dict["apple"]=apple
	#do similar for other textures in *for* cycle
	#then just set texture from 'textures' for slot
	$slot0.texture=textures[apple]
	$slot1.texture=textures[sword]

there some example from my project for runtime textures

#create textures
	var svp:=$icons_atlas
	var image:Image=svp.get_texture().get_image()
	if compress_ultra:
		image.compress(Image.COMPRESS_ASTC,Image.COMPRESS_SOURCE_GENERIC,Image.ASTC_FORMAT_8x8)
	else:image.compress(Image.COMPRESS_BPTC)
	var atlas:=ImageTexture.create_from_image(image)
	var fs:=Vector2i(64,64) #frame size
	list['star']={&'rect':Rect2i(Vector2i.ZERO,fs)}
	list['gates']={&'rect':Rect2i(Vector2i.RIGHT*64,fs)}
	list['wilderness']={&'rect':Rect2i(Vector2i.DOWN*64,fs)}
	list['road']={&'rect':Rect2i(Vector2i.ONE*64,fs)}
	list['street_road']={&'rect':Rect2i(Vector2i.RIGHT*128,fs)}
	list['housing']={&'rect':Rect2i(Vector2i(2,1)*64,fs)}
	list['up']={&'rect':Rect2i(Vector2i(0,2)*64,fs)}
	list['down']={&'rect':Rect2i(Vector2i(1,2)*64,fs)}
	list['tavern']={&'rect':Rect2i(Vector2i(2,2)*64,fs)}
	list['bed']={&'rect':Rect2i(Vector2i(3,0)*64,fs)}
	list['market']={&'rect':Rect2i(Vector2i(3,1)*64,fs)}
	list['chest']={&'rect':Rect2i(Vector2i(3,2)*64,fs)}
	list['chest_empty']={&'rect':Rect2i(Vector2i(3,3)*64,fs)}
	list['guild']={&'rect':Rect2i(Vector2i(2,3)*64,fs)}
	list['council']={&'rect':Rect2i(Vector2i(1,3)*64,fs)}
	list['workhouse']={&'rect':Rect2i(Vector2i(0,3)*64,fs)}
	list['mansion']={&'rect':Rect2i(Vector2i(4,0)*64,fs)}
	for key in list:
		var texture:=AtlasTexture.new();texture.atlas=atlas
		texture.region=list[key][&'rect']
		list[key]=texture
	svp.queue_free()

Wow. Great advice. I found the resource area you mentioned. As for setting up the AtlasTexture, in ItemNode which is a TextureRect, I added another TextureRect and am trying to define it in the editor as an AtlasTexture. See attached image. But everytime I refer to it in code, it says it’s a TextureRect. Any ideas on how to define and refer to an AtlasTexture?

If not, I can always define it via code at runtime, but it seems like it should be possible in the editor.

TextureRect doesn’t contains property atlas
it should be
item_atlas.atlas=Your created atlas texture with icons
item_atlas.region=Rect2

AtlasTexture could look weird in TextureRect, because of sizing flags
to pick just one frame from atlas, click button Edit Region, you may see it on screenshot

You may even
@export var textures:Dictionary
if typed dictionaries avaliable in 4.3:
@export var textures:Dictionary[String,AtlasTexture]=[]
and fill in with textures from editor

Hi,

You two are great. For anyone discovering this forum post, it’s all about AtlasTexture more than anything else. TextureRect gives you the option to choose New AtlasTexture when you add the node, but then it’s unclear how to refer to the atlas portion of the TextureRect after that.

So much of programming Godot is just learning Godot’s syntax on how to refer to things which can be frustrating. I’ll post my solution when I work on my code later this week.

John.

Here it is. If you have a tilesheet like Kenny’s 1-bit pack:

https://kenney.nl/assets/1-bit-pack

Then you can you it as an atlas in TextureRect. Choose New AtlasTexture and then add it to the atlas option below that.

To change your TextureRect to be a portion of the tilesheet, ignore all the .atlas code and just directly call self.texture.region = Rect2

extends TextureRect

var id: int = 0
var item_name: String = ""
var item_region: Vector2 = Vector2(0,0)

var item_region_dict = {"empty": Vector2(0,416),
	"apple": Vector2(512,288), "sword": Vector2(544,448), "armor": Vector2(554,336), 
	"cheese": Vector2(576,288), "helmet": Vector2(544,320), "shield": Vector2(528,352),
	"bow": Vector2(656,368), "staff": Vector2(608,384),
	}

func define(new_id, new_name):
	id = new_id
	item_name = new_name
	self.texture.region = Rect2(item_region_dict[item_name], Vector2(16, 16))


well as you progress you may find a way just to use extended version of Control and make use of
func _draw(): draw_texture()