Coordinating entities, events and timing

Hi,
Thanks for reading. Looked everywhere for some kind of guide to this but couldn’t find anything.

Say I want to create say 2 actors(entities) cube1 and cube2 and a director (script) which tells the actors when to appear, what to do and when to disappear.

I am guessing that you use the event model and tell all the actors (cubes) to listen out for director events but where do you put the director script and how does it know what the world time is ie. 10 seconds in fire this event, 20 seconds in fire that event etc.

Sorry if this question is a bit basic but I am trying to use playcanvas for presentation work rather than games so it needs to run in a scripted fashion father than reactive , if that makes sense :slight_smile:

Don’t apologise for asking a question like this. It’s an interesting talking point. :smile:

So there are various ways can you handle the scenario you describe. Let’s look at some of the things that you can use to do it:

Calling a Function on a Script

You can implement inter-entity communication with function calling. So imagine we have a script that handles keyboard input and another script that implements a player character controller. The input script (input.js) might do:

if (app,keyboard.wasPressed(pc.KEY_SPACE)) {
    var playerEntity = app.root.findByName('Player');
    playerEntity.script.player.jump();
}

In the player script (player.js) you would do:

jump: function () {
    this.entity.rigidbody.applyImpulse(0, 10, 0);
},

This is kind of OK - it works after all! But you have to fish out the target entity (‘playerEntity’) with a findByName call. You could cache that entity in the initialize function perhaps. But, to me, it’s a bit messy.

Events

You can use events to coordinate the behaviour of one or more entities. The benefits of events are:

  • Entities don’t have a function call between them that creates a code dependency. If script A fires an event handled by script B, you could delete either script from your app and your app should still run with no errors. This isn’t the case with function calling. This makes your code more modular and failure tolerant.
  • Multiple entities can handle a fired event. So if you have, say, 10 characters running around and you want them all to stop, you can do this with a single event, rather than having to call a ‘stop’ function on each character’s script.

A good way to route events is via the global ‘app’ object. So to fire an event you would do (in input.js):

if (app,keyboard.wasPressed(pc.KEY_SPACE)) {
    app.fire('player:jump');
}

And you handle the event as follows (in player.js):

app.on('player:jump', function () {
    this.entity.rigidbody.applyImpulse(0, 10, 0);
}, this);

You would insert the event handler above in the initialize function of player.js

Timing Things

There are a couple of ways you can time events or function calls. First, you can use setTimeout as follows:

// Fire 'someevent' in 1 second's time
setTimeout(function () {
    app.fire('someevent');
}, 1000);

This is nice and clean, but it has one major downside. The setTimeout (and the setInterval) function counts down whether your browser tab is active or not. Really, you would want the count down to pause when the tab is deactivated and restart when the tab becomes active again.

If I’m in a hurry and I’m not bothered about this, I use setTimeout. But if I want to do things ‘right’, I do:

initialize: function () {
    this.timer = 0;
},

fireEventIn(s) {
    this.timer = s;
},    

update: function (dt) {
    if (this.timer > 0) {
        this.timer -= dt;
        if (this.timer <= 0) {
            app.fire('someevent');
        }
    }
},

You could wrap up this timer approach in some helper functions. But you get the idea.

Anyway, I hope this goes some way to explaining some techniques you could use to solve your problem.

1 Like

flippin wonderful answer! cleared everything up

Many Many thanks.

1 Like