Set constant on GO created by factory

Hi
I’m creating a galaxy map with randomly placed stars and planets, and I want to set different planets to different colors. I create the planets using a factory, and I can’t figure out how to access the instance afterwards.

How do I use set_constant on a GO created by a factory?

The factory returns the hashed id but in order to set_constant on a component I need to specify the path to the component in the GO.
Combining the hash and a string id of the component does not work.
I could send a message to the GO and have a script on it that sets the constant, but it seems wasteful to send messages when I have a local reference already.

Or what is the intended way of handling interactions like this?

The way I understand it is that sending a message is indeed the preferred way to do this.

You can use msg.url() to put together URLs from the different pieces. You can give it three arguments: socket, path, fragment. factory.create gives you the ID or Path, you can generally give nil for the socket, unless you are dealing with collection proxies, and then give the string ID of your sprite.

Here is some code for spawning an object and setting a random color on its “sprite” component:

local id = factory.create("#factory")
local spriteUrl = msg.url(nil, id, "sprite") -- Note: do not put a # before "sprite". 
local col = vmath.vector4(math.random(), math.random(), math.random(), 1)
go.set(spriteUrl, "tint", col)
3 Likes

Thanks! I already refactored it into messages, but good to know how to work with urls!

1 Like

In general you want to keep the number of scripts to a minimum (at least scripts that run a lot of code all the time). Having a script that deals with these simple one-off messages is ok, but you should always be a bit cautious. The solution suggested by Ross does not require that you attach a script to each spawned game object.

Another option would be to have a script and a game object property (in this case color) that you pass to the game object in your factory.create() call:

local col = vmath.vector4(math.random(), math.random(), math.random(), 1)
factory.create("#factory", nil, nil, { color = col })

-- planet.script
go.property("color", vmath.vector4())
function init(self)
   go.set("#sprite", "tint", self.color)
end
1 Like

Thanks! That’s actually what I ended up doing. But in order to keep some encapsulation I pass the planet type, and let the planet set the correct color. I guess I can’t stop my OO instinct :smiley:

Have you considered adding context data to collection proxies too? It would be nice to have the same parameter pattern throughout, regardless of how you instantiate.
Of course the collection proxy can include several GO and scripts, but a general context for the entire collection still would make sense I think.

1 Like

Hmm, no, not sure this has been discussed. But you’re not instantiating a collection from a proxy, you’re loading it right? You can pass data to a collection when instantiated via a collection factory though. As for passing data when loading a collection via a proxy I think the best solution is to pass a message with the data.

I have another thread about passing data to GO:s loaded by Collection Proxies. I have not found a simple way of doing it.
Someone suggested I have the instantiated GO send it’s url to the main controller, and that works well. Kind of like an event listener.
But it would be easier to just have the option to include a context object that get passed to the collection GO:s.
I’m not sure what the difference is between “loading” and “instantiating”, apart from the reference counting that enables “unload”. In my case the loading of the collection results in it being instantiated, doesn’t it? I’m only using the Collection Proxy because I want to be able to Unload easily.

Wait, ok, so you have a collection proxy that you load and once it is loaded you want to send some data to the game objects inside it? Are we talking about game objects that are created immediately in the init function of some script in the loaded collection or is it a known set of game objects inside the collection that you need to share data with?

EDIT: this discussion probably belongs in my other thread, about collection proxies

My worldmap collection gets loaded when the player lands on a planet. In order to generate the correct type of world, for the planet type, I need to tell the worldmap script which kind of planet it is.

My current solution is for the worldmap script to send its URL to the main script in the init function. Basically saying (“worldmap_needs_params”). The main function meanwhile has stored the planettype and responds with a “create_world” message with the planet type included. This in turn makes the worldmap pick the correct generation table and generate the random world.

It works fine, but I feel it’s a little clumsy to have to hang on to the “planet type” data in main, waiting for the callback from the worldmap. It would be easier to just fire off the creating together with a context object.

Can’t you just pass the planet type to the collection once it is loaded?

local function land_on_planet(self, planet_type)
	self.planet_type = planet_type
	msg.post("#worldmapproxy", "async_load")
end

function on_message(self, message_id, message, sender)
	if message_id == hash("proxy_loaded") then
		msg.post(sender, "enable")
		msg.post("worldmap:/somego#worldmapscript", "params", { planet_type = self.planet_type })
	end
end

worldmap would be the name you’ve given your collection

1 Like

Hmm I have tried that, actually it was the first thing I tried. But my worldmapscript instance gets a prefix appended. So I don’t know in runtime what ID it has.
Maybe I did something wrong…

No, that shouldn’t happen if you load it via a proxy. If you spawn using a collection factory then yes, all game objects will get unique ids.

OMG, haha… My script is actually named “worldmap1” because there is already another thing called worldmap in the GO. I got confused by the fact that GO instances get number suffixes, and I thought this was the same thing.
But one thing I still don’t quite get. Usually I send messages to the GO, not the script, right? I didn’t even know you could send to the script directly. Or does Defold just assume you want to talk to the script when you send messages to the GO?

image

EDIT:
So, you actually already have an easy way of sending data to GO loaded via proxy :smiley: Fantastic!

2 Likes

If you send a message to the game object it will be received by all components on the game object. So if you send a “disable” message to a game object it will get passed along to all components and all of them will get disabled while a “disable” message sent to “go#weaponsprite” will only disable that specific component.

1 Like

I see, makes sense. Thanks for the explanation!