Calling functions in other script components

Hey!
Is there any way to smoothly do something like this, without having to send messages back and forth?

local myScript = someObject.get("#my_script_component") if myScript.some_bool_function() then do_something() end

Cheers!

1 Like

Welcome to the forum!

No, message passing is the designed mechanism to use for communication between objects.

Functions defined in scripts are put in the same scope so you can cross call them, but there is no way to set up “member functions” like in your example. (They will be shared across the scope and the “self” variable in init(), update() etc refers to an internal object that you can’t access any other way. That said, Lua do allow serious trickery so I won’t say it’s impossible to build something functionally similar to what you’re asking but we strongly recommend against going that route.)

If the boolean you store in the component doesn’t need to be computed on access, you can also use script properties.

Alright, thanks!

So, in that case - I’m trying to make a system where one script contains all the level/grid/state information.
How should I set up a system where every object moving in that grid, needs to call a “can_move_up_from_position(x,y)” with a messaging system?

I may just be used to another way of thinking when coding, but, I’m not sure how to do this without making a ton of things inside the on_message function

Yeah, it takes a little while to get into the right mindset when coding with message passing and how to arrange things in an efficient way in Defold. :smile:

There are several ways to do that. One way is to send:

msg.post(“board#script”, “request_can_move_to”, { x = 10, y = 27 })

and then just respond when a “response_can_move_to” arrive. That style can quickly become hairy though.

Another way is to turn it around and have the level script keep track of all the objects in a 2d table and then manipulate that, animate the objects etc (either directly or via messages). Most of the logic would then sit in the level script and the objects are just visual things that are shuffled around.

(I recently built a small example game with bricks on a board for a tutorial where I set things up that way. The size for init(), on_message() and on_input() landed on ~120 LOC. Most of the action takes place in local functions called from those though.)

What about putting all global state/grid etc in a lua-module that is required by the scripts that want to access it?

In that case, do they all have unique instances of each variable/state, or are they shared?

They are shared (unless declared “local”). The instance data is accessible through “self” for each game object instance, in the init(), update() etc functions.

EDIT: Depending on the “shared_state” setting in game.project, separate contexts are used for scripts and gui_scripts. If it’s set, all scripts share the same context.

So, theoretically, I could have a level_data lua module, that contains object and level information, which I then use in the level_spawner script to populate/spawn objects, which also includes the level_data module?

Yes, that would definitely be possible.

Thanks, using a lua module worked quite well!

I’ve got another question though - I want to spawn a set of objects, and then keep a reference to them in an array.
I then want to look if any of those objects are at a specific location in the world, but I can’t seem to find how to access information in those objects.

factory.create returns a hash, which I can use to send messages, but I haven’t seen a go.find( hash ) function returning a reference to the game object itself, in order to access the position of that object.
How would I do that?

This should work:

self.object=factory.create(…)
pos=go.get_world_position(self.object)

Ah, thanks!
So that works in that specific case, when I just want the position. Is there a more generic version where I can then access a script component of that object?

Something like this:

self.object=factory.create(…)
url=msg.url(self.object)
url.fragment=“script”
value=go.get(url, “property_name”)

Thanks!
Although in that case it has to be a property - would there be a way to access non-properties, like arrays?

No, you would need to do it with some sort of message passing

This is probably my biggest gripe with Defold at the moment. Not being able to call functions or grab any variable from another script is painfully limiting.

I may simply be coding the wrong way, but I keep finding myself getting stuck simply because I can’t access any data without passing loads of messages back and forth.

Is there any specific reason it’s prohibited?

The engine is constructed around this type of asynchronous communication model between objects and has an object model where data and code are encapsulated (see http://en.wikipedia.org/wiki/Message_passing). Yes, it does require a different way of thinking about the code. For instance, messages are often best used to invoke behavior that you encapsulate in objects. If you use messages to pass data around a lot you might want to look at alternative ways of modeling your data. Perhaps it’s also possible to store it closer to the user?

Hm, alright.
To me, it feels like I’m either unable to access and run code that I need to run, or that I have to structure things in a way so that I skip using multiple scripts entirely, and just use one massive script that controls everything, rather than properly separated scripts.

(For the record - my background lies in Unity and C# almost entirely, and again, might just be that I’m just not very used to this kind of system)

I’d say if you want to appeal to users who are familiar with Unity, I strongly recommend allowing direct access to objects and its members.
The advantages of message passing seems to be very few compared to its heavily restrictive nature, and has so far led us to cut features, even in the very simple game we’re making right now, simply because script communication has been so much more difficult than what we expected.

As it stands right now, I see no reason why async message passing would be better than directly accessing objects and calling their functions. At least allowing both would be great, they don’t have to be mutually exclusive.

Might be my inexperience talking, but that’s my 2 cents so far anyhow!

You can still use Luas tools for modular programming to structure your code into smaller (or sharable) chunks if you want.

It’s a different approach to object orientation and yes, it promotes a different way of structuring the code. Once you get into it you won’t feel restricted at all, on the contrary. My experience (coming from a mix of languages and paradigms) was that it took a little while to wrap my head around the way to code in Defold. Once I got into it, however, I realized I can move forwards very fast and the results are solid.

As it stands right now, I see no reason why async message passing would be better than directly accessing objects and calling their functions. At least allowing both would be great, they don’t have to be mutually exclusive.

Defold is carefully designed with message passing as the primary way of communicating between objects. I don’t know the internals of the engine but as I understand it, changing this model would require tearing up the fundamentals of the engine and rebuilding it from the ground up.

The advantages of message passing seems to be very few compared to its heavily restrictive nature, and has so far led us to cut features, even in the very simple game we’re making right now, simply because script communication has been so much more difficult than what we expected.

That’s very unfortunate. We’re working on more elaborate tutorials that will be put up on the site in the near future. Is there anything specific you believe we can do to make getting into Defold easier and faster?

If you really want to be able to access all kinds of properties between gameobjects you could build some sort of property store as a lua module. Something like (code written of the top of my head here, so not tested):

property_store.lua:

local property_store={
values={}
}

function property_store.set(id, property, value)
property_store.values[id][property]=value
end

function property_store.get(id, property)
return property_store.values[id][property]
end

return property_store

some_script.script:

local ps=require(“property_store.lua”)
function init(self)
ps.set(go.get_id(), “name”, “test”)
end

function on_message(self, message_id, message, sender)
if message_id==hash(“collision_response”) then
ps.get(sender.other_id, “name”)
end
end

Since I don’t know the inner workings of the engine I have no idea if this is a highly unrecommended coding standard, but it would work.