Use messages or the API?

Hello,

I have been playing around with quite a lot of game engines trying to find a nice balance between code and something visual. So far I have been very impressed and am enjoying using Defold.

However there is something which I can’t seem to find the answer to, or guidance/best practice.

Basically when to use message passing (msg.post) and when to actually call the API. Obviously message passing is great for custom messages and things that don’t have an API. So this is more in the case when both a message and an API exist for the same thing.

My tendancy is to favour the API, however I am concerned that during initialization maybe other game objects aren’t present/race conditions. Where as I believe the message handling happens after this.

For example, should I use go.set_parent or msg.post(".", “set_parent”, {parent_id = something})?

Also a related question on properties - why would I use something like go.get(".", “position”) opposed to go.get_position()?

I can only see I would use the property for things which aren’t provided by default (eg “health”) or for go.animate.

Thanks

4 Likes

As far as I understand it in the end they are actually the same thing. When doing go.set_parent the engine is sending a message - the same same way if you did msg.post. I prefer to use direct function calls myself because they are easier to read and they are better supported by the code completion.

So in the end it all depends what you want. Because they are simply two different way to express the same thing.

4 Likes

Thats excellent news. I had hoped it was purely to provide for different programming styles.

I will carry on using the API and leave message passing for more “game” related messages.

Thanks

2 Likes

There is no difference here. Go.set_parent() will generate a message under the hood.

I would never use go.get() if there is a function that gives me what I need. In this case position. I would use go.get() to for instance get a script property of another game object defined using go.property() or some component property.

3 Likes

I have a similar question and this thread so far confused me even more:

My game has 10 enemies which are chasing the player.
What’s the better design in regards of decoupling vs. performance?

  • get the players position in the enemies update loop via go.get_position("/player")
    or
  • send in the players update loop a message like “player_pos_update” and all the enemies check for this message in their “on_message” function and update their position?

Thanks

For 10 enemies, I don’t think you’re even close to getting a measurable difference in performance.
And, as always, when in doubt, profile you code.

As for decoupling, your first suggestion only requires the agents to know about their target (an url, which you also can easily replace at the agent’s discretion)

Your second solution adds a connection between players and agents: knowing who to send the message to, and knowing about receiving the message.

I would go for the first option.

2 Likes

Thanks, got your points

So for real decoupling it would make more sense to use an event system where each game object can subscribe and listen to. I thought the message system would exactly provide this functionality.
So I don’t get the point of the message system. I read the idea behind it is decoupling game objects but it’s more “a bit decoupling system”

I have also another question regarding the this message / reference system: what if a game object is deleted? The existing threads confused me even more once again.
Use case: 10 enemies want to follow player, player gets killed (=deleted) by one enemy. What happens is that the reference to the player gets lost resulting obviously in errors.
I’ve read about a method “go.exists” but this method doesn’t exist.

Yeah, when a game object is deleted you will get errors if you try to use go.get_position(id) and other similar functions.

  • You can catch these errors using the Lua pcall() functionality.
  • You will get errors the next frame, not the same frame as the game object was deleted.
  • If the player in your example dies do you really have to delete the game object? You could just hide it. And you probably want to stop the game anyway. Or respawn. Or reload.
1 Like

I solved it this way (just for reference):
I’ve create a “broadcast” game object and script. Enemies will register when they are initialized at this broadcast GO. As soon as the player collides it sends a message to the broadcast and the broadcast sends a message to the enemies that the player is dead. Now the enemies will only follow the player if he is alive.

Regarding message system vs. references: I use references like go.get_position("/player") if I need on every frame such information, but use messages if only sometimes information has to spread.

By the way I wanna say thank you Defold team: I had so far only a few questions but get very quickly and helpful response! That’s really awesome

3 Likes

Thank you for sharing your solution. It sounds reasonable!

:heart:

What about doing:

pos_x = go.get(".", "position.x")

Instead of:

pos_x = go.get_position().x

Do I understand correctly that the first is more efficient since it doesn’t need to allocate a new vector3? Or is there some under-the-hood reason that makes them identical performance-wise?

1 Like