I’m none the wiser as to how to do it. You’ve only explained WHAT it can do, not how, nor why, when or where.
eg. Mail messaging, to send a message to your friend, via post:
Write his name, and address, on the front of an envelope, and your address on the back. Place your message inside the envelope. The send address should be of the format:
firstName - Surname
House Number on Street
of Suburb…
in the City…
Belonging to the State of…
Of PostCode…
In the Country of…
Be sure to fill out your return address in the same format, on the back.
What, where, when, how, why would be at least five sets of three sentences each minimum wouldn’t it?
What: Messages are a way of sending information in a way that is loosely coupled. Messages can have a name/id and a body. The body can be any kind of Lua table, but has a max size in bytes.
How: Most scripts can send messages with the msg.post(address, message_id, message_lua_table_data) syntax. Game Object, GUI, and Render scripts can check their inboxes based on the on_message(self, message_id, message, sender) function. You can place conditional statements in your on_message functions to select for message id, as well as the contents of a message, to decide how to act on them.
Why: Defold is designed in a loosely coupled way in an attempt to avoid the occurrence of bugs caused by tightly coupled dependencies. Instead of acting directly, you can send a message, and then other entities can choose to act on the message or not. Communicating with the system is often done through message passing, communicating with child / parent objects, such as children telling their managers that they were created, or parents telling their children to die.
When: Messages are processed every frame multiple times. Unless you are generating a very large number of messages most which are generated on a frame will be processed. When a thing gets a message it will also get the path to its sender in case it should want to reply.
Where: You do not want to use message passing for everything. Some kinds of data you should pass through shared imported modules. Some kinds of entities you want to control via a master object with a single controller script, and to not have each thing its controlling having its own logic.
Better?
Only if message sending is an inelegant mess.
You are asking for five things to be explained in three sentences…
No, just message sending.
Ok, I feel I’ve explained that simply above already with the how section. Explaining 5 things (what, where, when, how, why) is still a challenge in three sentences, but I could cheat and make them long sentences?
Can you explain exactly what you don’t like about Defold message sending system? Is it the address pathing you are having trouble with understanding?
Can you explain how Defold’s system could be changed to look more like an elegant solution? Is it more tight coupling, direct modification that you’re after?
This tangent would have been better in its own thread…
Objective-C
[object message:parameter]
Once you accept that there’s NOTHING elegant about how Defold handles messaging, you’ll begin to see the problem.
JavaScript
Object.subObject.function(parameter, parameter02)
Swift
typeName.subTypeName.function(argumentLabel: parameterValue)
I can’t think of a single language or framework that does it as horribly as Defold. Can you?
of this: msg.post(address, message_id, message_lua_table_data)
What does an address look like?
What does a message ID look like?
How is a message lua table data “packet” actually structured?
I ask, because my above examples are exactly how their code looks, in all those languages, bar the horrible names I’ve used.
However you’ve not even used placed holders. You’ve used descriptors to hide how inelegant actual use of Defold messaging is. Show a REAL example, with full address, and a parameter, and a function name.
What does an address look like, and what is it relative to in Defold?
How does it trawl the system to find this address?
I explained things the way I did only because I was trying to answer your questions based on what I thought you were after.
Those examples are tightly coupled, are they not? Defold is intentionally designed in a loosely coupled way, like Erlang, for the benefits that this gives in designing your systems.
You do not call functions directly in message passing, but you can setup the recipients to act on messages as if they had functions called directly on them.
You can call functions directly with required modules. This may be more closely to what you are after depending on what you are trying to do.
Here is a basic example to compare to yours.
msg.post(“object_collection/sub_object#component”, “function”, { “parameter”, “parameter_02” })
Defold would expand the above relative path in a way that is relative to where the script calling it lives. The message labeled “function” wouldn’t actually be a direct function call, it’s just a label for the recipient to filter based on.
And here is maybe an example that may happen if you have a script in a different socket that wishes to send a message to something in the main socket world in a hard coded way.
msg.post(“main:/sub_collection/player#logic”, “increase_score”, {score_to_add = 200})
Do you know what a Lua table is? The data is just a Lua table. You can make it any way you want mostly. An exception is you can’t pass anonymous functions.
Pathing is a different subject. You can define paths into variables to be reusable. The paths are based on a scene hierarchy from the socket (the world the recipient is in) through a path leading through possible collections until you reach a game object / its component you wish to communicate with. You can define an address in a relative way or an absolute way. If you define it in a relative way then Defold will build the rest for you based on where the entity defining the path is at.
How does it trawl the system to find this address?
Again, if you define the path in a relative way, it will look at the position of the current script calling msg.post and try to construct the path dynamically.
Did you read the official docs on message passing already? http://www.defold.com/manuals/message-passing/
This looks more like calling a specific function on another object. Does this mean that you prefer a system where you can call any function on any other object without limitations? Also, are you not omitting some things from your examples above? Take objective-c for instance:
[object message:parameter]
What is object here and how do I get hold of a reference to it? Another thing that comes to mind is that you are comparing language features in Objective-C, Javascript and Swift with those of a game engine framework (in this case Defold). Wouldn’t it be more fair to compare the three programming languages Objective-C, Javascript and Swift with the Lua programming language and not with the game engine Defold?
Also, what we are doing here is theoretical. It would be better if we could come up with a real world example of when to use message passing and discuss around that specific example.
How does it trawl the system to find this address?
Addresses are static.
Our message passing and URLs is modeled after the internet and HTTP, because the requirements we designed after were quite similar to those systems. A programming language is something completely different, and solves different problems.
Deeeds, I am confused as to what your confused about because the messaging system seems incredibly straightfoward. Ignoring your opinion on the “elegance” of the system, I have a few questions for you that will help us understand better.
-
What specifically do you not like about the messaging? Vague generalizations aren’t helpful. If you don’t have any specifics, do you just not understand how the messaging works at a high level? And did the documentation not help with your understanding?
-
What are you trying to do that you can’t accomplish with this messaging system?
-
What future or hypothetical situatioms do you believe can’t be accomplished with this messaging system?
-
Do you actually understand how the system works in general and don’t have any specific problems that need to be solve and are mostly hoping to get a peak at the implementation details of the messaging system?
I personally just think that using messages for what Defold is using them is a pointless complication. Why not just use functions and hooks for everything that is done with messages now?
Message-heavy architecture could make sense if we’d use them to communicate between runtimes or different machines. But now everything is sitting in same runtime, can give each other direct access to self through global variables and functions etc.
There is nothing preventing you from setting up a system like that for your own code through Lua modules. But for Defold as a whole the design decision was made a long time ago to use message passing as the way to communicate between game objects and components (and as you probably can understand it is nothing that can be changed now).
Why? You can have both
msg.post(".", “set_parent”, {parent_id = self.master})
and
go.set_parent(self.master)
with one of them being a (possibly deprecated) wrapping around another.
That has been done for some messages, like physics.ray_cast(). It might make sense to do this in more places too.
An important reason for message passing to exist for me is for building parent / child relationships with dynamically generated systems. It is the primary use I have for them.
I agree that more of the general system functions could be converted to simple function call style syntax like others already have. It seems like it’s already planned and just a matter of priorities and urgency of other work needing to be done first for it to happen.
I am a bit of an architecture nut, and I love this sort of topic, so i’ve accidentally written a lot .
TL;DR: I’m still getting used to messaging, I don’t like that I need to write around it so often.
Messaging has its benefits, and i’m sure i’ll get used to it over time, but so far i’ve found it to be quite a hindrance.
For instance, I’m really missing the simplicity of function callbacks. It seems that, when I want something similar, I need to jump through extra hoops.
If I had a pure reference, I could just pass in a callback, but in the messaging world I need to have both objects agree on a form of communication, which has no guarantees of consistency between object pairs, for instance:
Lets say I want A to use a B, firing off some long running process, and then Fire off some completing behavior in A, Issues that could arise:
- Either URL might be incorrect, and this is Lua, so there will be no warning until runtime.
- Either message id might be incorrect, and this is Lua, so there will be no warning until runtime.
- The callback itself does not explicitly run in the context of object A or B
- One implementation might have the code run in A, one might in B, the objects available will be different.
- In both the aforementioned cases you may need to ask A or B for something during the “callback”, in which case execution gets even more complicated:
- If your behavior in the nested case is synchronous, you must now make it asynchronous - I can’t send a message and rely on things actually happening before my next line of code.
- Your function can’t be anonymous, it needs to be accessible through this response msg route. Which means that any other misbehaving object can run this again, there is less of a concept of fire and forget - these are permanent fixtures.
- If I do this again with objects C and D, I could structure it completely differently.
Of course, there are solutions to all of these. It’s up to you as a programmer to be consistent in your approaches in dynamic languages like lua, You can write global Lua modules that allow you to set up regular old callbacks in some cases, you can nil-out a function once a message has fired, but these are still hurdles to overcome and, for example if you’re not careful about your call context when working with GUI nodes you’ll end up with errors like: Node used in the wrong scene
I think loose coupling is good thing, a great thing in fact, but I have struggled to find (practically speaking) many locations where messaging is more sensible than just calling a function. Most of the time, when you are giving a specific object a specific message, it may as well just be a function call, it’s not like writing
msg.post("col:object_name#component", "foo")
is practically any less coupling than
object_name[component].foo()
There is still a dependency there, and not needing to nil
check component
in the messaging case really isn’t much of an advantage considering that you need to define a bunch of other stuff for it to work.
I should reiterate that, I do think there are some great arguments for messaging, but I don’t think it should be used in the common case, and I think Defold is built in a way which pushes architectures towards using it as the common case.
All that said, I’m still new to Defold and i’m sure i’ll find more elegant ways to get the behaviors I want as I adjust my brain to the paradigm.