Collection factory instances with collection proxies

Hi all, trying to wrap my head around something.

I have a many images for enemies in my game. I need to select which one to show for an enemy at runtime and set it on a sprite. I have read that collection proxies should be the way to go.

For each enemy image, I have created an atlas that contains just the single image, and a collection with a sprite component set to the enemy image. I call these ‘proxy_<insert_enemy_name>.collection’.

In my game world, I then have a collection factory which spawns instances of ‘battle_character.collection’.

The ‘battle_character.collection’ has some scripts and sprites and a collection proxy for each of the possible enemy images. Each collection proxy has a different id and is assigned the relevant ‘proxy_<insert_enemy_name>.collection’.

When I run the game, I spawn instances of ‘battle_character.collection’ and once spawned, I send a message to the new instance telling it which proxy collection to load. If I use msg.post("#proxy_<insert_enemy_name>", “load”) from within ‘battle_character.collection’ I get a warning:

WARNING:GAMEOBJECT: Instance is initialized twice, this may lead to undefined behaviour

… and the image does not appear.

If I try and use “async_load” I get an error stating that the socket is already in use.

I do listen to the “loaded” message and then “init” and “enable” the proxy, but no luck actually seeing anything. I see that the sender has a unique url each time, so I don’t understand the warning either - nothing should be initialized twice?

So, my question is, is the warning wrong? And should this work? How else can I set this up? I really don’t want to use gui nodes to dynamically load the images if I can help it.

Ok so renamed a few things, made a little progress.

Using “load” on the collection proxy seems to work and I get the “proxy_loaded” message. I then call “init” on the collection proxy and that triggers the warning about “Instance is initialized twice.”

After “init” I then call “enable” and despite being able to see the image briefly (the game appears to have frozen for a second or two), the game then silently crashes with nothing in the logs :frowning:

Ok last post for now. I don’t think my approach would work anyway due to the image being in its own world.

I think I can create a battle_character.collection for each enemy, and move all the common stuff into it’s own collection file so won’t be too much duplication.

edit - I think I have a solution by using the dynamic prototype feature of collection factories. Collection factory manual

I can create X amount of collection factories where X is the upper-limit of different enemy types in my levels and then load them at will. Seems like a lot of hoops to jump through for the simple use-case of setting a sprite dynamically, but I can make it work.

1 Like

This solution sounds a bit complicated. If I needed to create multiple images/animations for an enemy, I’d put them all in the same atlas, and use a game object with a property that controlled the index of the image to choose.

You can set properties when spawning game objects via factory.create, and then use that property to select an animation to play in sprite.play_flipbook.

An example project I found that uses this is here: https://github.com/benjames-171/defold-games/blob/1c3ceb0f7872956df5f7f89eb79c4fb57c4ced1a/Defrog/game/object/move.script#L8
And the factory.create call is here: https://github.com/benjames-171/defold-games/blob/1c3ceb0f7872956df5f7f89eb79c4fb57c4ced1a/Defrog/game/core/level.script#L28

Thanks but using a single atlas means they’ll all be loaded into memory at once which isn’t an option due to quantity and sizes.

I see. I believe the approach would still work with multiple atlases though, just instead of sprite.play_flipbook to change images within a single atlas, you would use go.set("#sprite", "image", self.atlas) to switch the sprite to another atlas.

See API reference (sprite) for reference.

2 Likes

Ah interesting. I assumed that would still require all the atlases loaded into memory, but maybe that is incorrect and they’ll be ref-counted and unloaded as needed? I can give that a try for sure!

I also came across the native image loader extension which may be another solution, but the github page doesn’t include my target platform.

Still no luck.

Using go.set("#sprite", "image", atlas) requires having a reference to the atlas, but I can’t figure out how to get a reference to an atlas without using a gameobject property go.property("my_atlas", resource.atlas("/my_atlas.atlas")

Using the go.property() would require it being set by passing it in as an argument with the collectionfactory.create() call, which also requires having a reference to the atlas already.

I guess creating a dynamic atlas is the next possible solution, or testing the native file loader extension on console.

Or creating a factory for each image and only loading the required one. I was going to get my hands dirty with some py scripting anyway so might as well go for that option.