What would you use and why for communicating between scripts

If I had this kind of method:

...prototype.initialize = function() {
    this.app.mouse.on(pc.EVENT_MOUSEMOVE, this.movePlayer, this);
};

...prototype.movePlayer = function(event) {
    // Raycast code (won't write it....)

    // Option 1 - with events
    this.entity.fire("...:movePlayer", raycast.point);

    // Option 2 - entity bound
    this.entity.targetPoint = raycast.point;

    // Option 3 - script bound?
    this.entity.script.movePlayer.moveTowards(raycast.point);
};

I am not good at this but my opinions are:
Option 1 - Events are nice because they remove coupling but more cost heavy than direct bind on entity. Also, many entities can listen to the said event.

Option 2 - Best, performance wise, but controller and movement class need to be on the same object which makes them tightly coupled (since we are directly setting the position of the said entity. Value is entity bound, not script bound (if that makes sense)).

Option 3 - Think it’s the worst of the 3 (in this situation) because not only are they coupled (both scripts need to be on the same entity), it needs to look for the script on the entity and then find it’s method and pass the value every tick.

My general rule of thumb is:

  • If a script is reusable and self contained, I have it post events via the entity object. Eg something that loads and process an asset (option 1)
  • If it’s a script that manages the entity and depends on other scripts, then I would more than likely access the other scripts directly via the script component as it makes no sense for it to use a decoupling pattern (option 3)

Worth mentioning that option 3 is a O(1) lookup in PlayCanvas so it’s super cheap

I use the event pattern here as the scripts are mostly self contained and communicate out and the Bitmoji3d.js script is effectively the manager of the entity: https://playcanvas.com/project/721733/overview/3d-bitmoji-library

3 Likes

Hi @JustVlaxx! I have never managed to use option 1 in my project. Perhaps because my project is less suitable for it. I think I use a combination of option 2 and 3 myself.

2 Likes

Cool, I am too used to Interface interaction with other scripts (controller is fixed and has a reference to every other component attached to the entity and because it’s an interface I can switch the ‘PlayerTranslateMove’ class to ‘PlayerVelocityMove’ with no problem).

Here, I didn’t find interface equivalent (very new still) but the closest thing would be the event system since my ‘PlayerTranslateMove’ and ‘PlayerVelocityMove’ could subscribe to the same event and I could just switch the class when I want/need.

Is it O(1) because it’s on the same entity so it just has to search that entity’s scripts to find it? Hm, I thouht that even if it is on the same entity it would still have to find the entity first and then look for the script. I am weird.

Thanks for the explanation though. Very useful to know.

I am using all 3 because I want to make the layer of abstraction between logic and UI. Like, Player can live without the UI but the UI can’t live without the Player sort of thing.

Just trying to figure out which one is best used for which scenario. Yaustar made it much more clear :slight_smile:

1 Like

To get any component on an entity is an O(1) operation, to get any scriptType on a script component is an O(1) look up.

This is because it’s part of the JS Object which is effectively a dictionary. It is also why you can’t have multiple instances of the same component or same scriptType on a single entity.

In terms of find Entities, using findByName is a O(n) lookup, whereas findByTag is much quicker. Not sure what the operational cost is though but it’s better than O(n)

You can use script attributes to store a reference to the entity at Editor time or you can cache the object reference on initialize so you don’t take the cost each time.

1 Like

You could do a subscription like method where the manager can fire an event with a callback for other scripts to call to register themselves. That way it is still decoupled and extendable.

1 Like

Will do some research on this because I don’t understand how that would go. Probably like an event system that objects can subscribe to and unsubscripe on runtime but this is only me guessing.

Thank you for making it quite clear and explaining to greater detail than I expected!

Here’s an example of what I mean: https://playcanvas.com/project/943495/overview/subscribe-pattern

It’s decoupled so none of the scripts really know about each other and removing any of them won’t break the code/entity.

The order of the scriptTypes matter as the updates, initialize are called in the order on the script component:

Edit: Actually thinking about it, I’ve just reimplemented the existing event system just without the initial cost firing the event so I’m not sure about recommending this now :person_facepalming:

1 Like

:smiley: Will still look at the principle behind it. Althought I am not in country for a few days (friends wedding) so will take a look when I come back!

Thanks a lot!