Dynamically change atlas with variable

I am working with a new project from the ‘Desktop Game’ template.
I have a main.collection with one game object that has a sprite component pointing to the main.atlas
I created a new atlas with a “player” animation

what i want to do is set the atlas of a game object using the go.property + resource.atlas()during the init(self) lifecycle function.

I put this in a script and attach it to the game object and it works fine.

local init(self)
    go.property("my_atlas", resource.atlas("/asset/atlas/player.atlas"))
    go.set("#sprite", "image", self.my_atlas)
    sprite.play_flipbook("#sprite", "player")
end

when I run it the game object that has the logo sprite configured displays the custom sprite I added in the player.atlas.

now if I do something like this it does not work, but what I am ultimately trying to get working:

local init(self)
    local atlas_path = "/asset/atlas/player.atlas"))
    go.property("my_atlas", resource.atlas(atlas_path))
    go.set("#sprite", "image", self.my_atlas)
    sprite.play_flipbook("#sprite", "player")
end

I know from the documentation that resource.atlas() takes a string and returns a hash, but it seems like it doesn’t support passing a string as a variable?

If I run the below code those debug statements print ‘nil’ for the actual value and the type, but printing the type of path shows as a string.

function init(self)
	local atlas_path = "/asset/atlas/player.atlas"
	print(type(atlas_path))
	go.set("#sprite", "size", vmath.vector3(64,128,0))
	go.property("my_atlas", resource.atlas(atlas_path))
	print("ENTITY DEBUG: self.my_atlas =", self.my_atlas)
	print("ENTITY DEBUG: self.my_atlas type =", type(self.my_atlas))
	go.set("#sprite", "image", self.my_atlas)
	sprite.play_flipbook("#sprite", "player")
	msg.post("@render:", "use_fixed_fit_projection", { near = -1, far = 1 })
end

I figured it might be related to this topic:

So I made a second game object and attached a sprite that did reference /asset/atlas/player.atlas directly as a way to try to force the game to load the atlas for further reference but it doesnt work either.

Is there something I am missing or is there a better way to do this? I saw that textures and atlases can be created at runtime but I figured I would run this down before I try that.

just wanted to add a little more digging. If I look at the resource profiler I can see there are two reference counts to /asset/atlas/player.a.texturesetc so i’m fairly confident that atlas is being included in the bundle

Also I noticed when stepping through the debugger it just skips the go.property() line with the resource.atlas() as a variable.

Not sure if this will help but creating the property before the init function and setting it in the function seems a more natural approach to me. As far as I know, you need to create the properties at top

go.property("my_atlas", "")

function init(self)
	local atlas_path = "/asset/atlas/player.atlas"
	print(type(atlas_path))
	go.set("#sprite", "size", vmath.vector3(64,128,0))
	self.my_atlas = resource.atlas(atlas_path)
	print("ENTITY DEBUG: self.my_atlas =", self.my_atlas)
	print("ENTITY DEBUG: self.my_atlas type =", type(self.my_atlas))
	go.set("#sprite", "image", self.my_atlas)
	sprite.play_flipbook("#sprite", "player")
	msg.post("@render:", "use_fixed_fit_projection", { near = -1, far = 1 })
end

the problem with this is that resource.atlas() can only be called from within a go.property() function call per the documentation.

I agree your suggestion seems more natural and I had tried something similar but running it like this results in:

ERROR:SCRIPT: main/main.script:7: attempt to call field 'atlas' (a nil value)
stack traceback:
  main/main.script:7: in function <main/main.script:3>

because it is not in a go.property call.

in my larger project I am trying to implement a generic GO that gets configured when it is created. To that end I have also tried passing the atlas path during the factory.create() call but it’s the same result, its something about passing in the atlas path as a variable to the resource.atlas() call

1 Like

The go.property() line will not be processed at runtime. It will be picked up when we build your project, the referenced resource will be added to the resource tree, and the line will be removed.

The go.property() should be added to the top of your file outside of the scope of the lifecycle functions. See this example:

1 Like

I think my problem was I thought I had to use go.property() to change the atlas dynamically based on all the examples, including the one you linked.

but I can just do something like this and no worry about go.property():

function init(self)
	local atlas_path = hash("/asset/atlas/player.a.texturesetc") -- /asset/atlas/player.atlas explicitly referenced by another GO
	go.set("#sprite", "image", atlas_path)
	sprite.play_flipbook("#sprite", "player")
end

with this I could pass atlas_path any way I need (message, lookup, etc.), but getting that path required me to look at the resource profiler. Is there a way to get the build path of the atlas?
something that I could pass in /asset/atlas/player.atlas and get back /asset/atlas/player.a.texturesetc? Or can I safely assume all atlas paths will look like /path/*.a.texturesetc and make some sort of helper function to return that given a pre-build path?

I think this is the case. If you don’t want to worry about go.properties, another way to make sure that you can change atlases safely is to:

1. create a new collection and name it “atlas_cache“ or something like that, in this collection add a single game object with multiple sprite components and assign each atlas to its own sprite component.

2. Somewhere in your project, perhaps in main scene add a game object with collection factory component, and set the prototype to atlas_cache.collection

If I recall correctly this should ensure that the atlases get included in your game, at least that’s what I’m doing.

You should then be able to use the path

local path = 'atlas/logo2.a.texturesetc'
go.set('#sprite', 'image', hash(path))