Reputation: 10975
I have an object called Player
:
var Player = function(_id) {
var time = new Date().getTime();
this.act = function() {
if (new Date().getTime()-time>=5000)
this.destroy();
}
}
When I create this player object, I would like to destroy
this player after 5 seconds.
Now I can see two methods to approach this:
If I decide to use the act method (using setInterval
), it would probably look like:
Players = {};
for(var i=0;i<5;i++)
Players[i] = new Player(i);
setInterval(
function() {
for(var i=0;i<5;i++)
Players[i].act();
},
1
);
If I decide to use setTimeout
instead, such as:
var Player = function() {
setTimeout(
function() {
this.destroy();
},
5000
)
}
Using method #2 would mean that I wouldn't need to keep track of the time, or need a special ID.
However, suppose I had 200 players, it would spawn 200 timers, wouldn't that be inefficient?
However, if I used method #1, it would consistently run, the timer would just keep running every millisecond, which I feel is inefficient as well.
So, in terms of efficiency (accuracy would be nice to include as well), which method is better?
My final code looks like this:
function Player(_id) {
this.enqueue(_id);
}
Player.prototype = {
queue: [],
constructor: Player,
enqueue: function (_id) {
this.id = _id;
var length = this.queue.push({
expires: new Date().getTime() + 5000,
instance: this
});
if (length === 1) this.dequeue();
},
dequeue: function () {
var player = this.queue[0];
var delay = player.expires - new Date().getTime();
setTimeout(this.act.bind(player.instance,player.id), delay);
},
act: function () {
console.log(this.id);
this.queue.shift();
if (this.queue.length)
this.dequeue();
}
};
If I go into the developer's console within chrome and type in Player(4)
, and then wait 2 seconds and type Player(3)
, I get 4, and then two seconds later 3.
Works as expected and by looking at the code, I am only using one setTimeout only.
Upvotes: 4
Views: 1547
Reputation: 74244
How about the best of both worlds? You're creating Player
objects sequentially. Hence they will timeout in the same order as they were created. Keeping this in mind you only need one setTimeout
at a time (for the Player
object which will timeout next). This is how to implement it:
function Player() {
this.enqueue();
}
Player.prototype = {
queue: [],
constructor: Player,
enqueue: function () {
var length = this.queue.push({
expires: new Date().getTime() + 5000,
instance: this
});
if (length === 1) this.dequeue();
},
dequeue: function () {
var player = this.queue[0];
var delay = player.expires - new Date().getTime();
setTimeout(this.act.bind(player.instance), delay);
},
act: function () {
this.destroy();
this.queue.shift();
if (this.queue.length)
this.dequeue();
}
};
Now every time you create an instance of Player
it will add it to a queue (enqueue
). If there's only one instance in the queue then the queue starts moving (dequeue
). After the timeout the instance is destroyed and the queue shifts. If there are still instances in the queue then the queue keeps moving.
This way there's only one setTimeout
which is active at a time and the next one starts after the first one ends.
Upvotes: 3
Reputation:
Using the setTimer
(or setInterval
) doesn't spawn any timer per-SE but puts an event on an event queue which is parsed.
But yes, with many such events it's obvious the timing will become more and more inaccurate and make the browser quite busy trying to empty the queue of these and other events such as paint events.
What you can do is to have a self-container player object which contain it's own status. Just for example (consider this pseudo code as I haven't tested it):
function Player(name) {
var status = 0, //0=alive, 1=dying, 2=dead
timeOfDeath,
threshold = 5000; //ms, time of dying
/// get status of player
this.getStatus = function() {return status}
/// kill player
this.kill = function() {
if (status === 0) {
status = 1;
timeOfDeath = new Date().getTime();
}
}
function animateDeath(progress) { /* something here */ }
///MAIN, update player. This will be driven by a
///common loop
this.update = function() {
switch(status) {
case 0:
/// do whatever the player do when alive
break;
case 1:
///player is dying, animate death
///diff determines progress of death
var diff = new Date().getTime() - timeOfDeath;
animateDeath(diff);
if (diff > threshold) status = 2;
break;
}
}
}
Now we have a player object we can update from a common loop:
var p1 = new Player('Bob');
var p2 = new Player('Lee');
...
function loop() {
if (p1.getStatus < 2) p1.update();
if (p2.getStatus < 2) p2.update();
...
requestAnimationFrame(loop);
}
If you now kill one player by calling p1.kill()
this will be automatically handled by the object itself and as status changes the object won't be updated more.
Of course, this is a very basic example just showing the principle of it.
You would need to consider optimizations such as putting the player objects into an array instead that you iterate and remove dead players from as well as other components in the game, using prototypes if possible (objects are slower than arrays so if player info can be put in a simple array instead and have external functions do the same as those in the object this will perform better..) and so forth.
It's not sure you can drive everything at the same time and need to create "groups" etc. but that is too broad for this format.
Using requestAnimationFrame (rAF) allows you to synchronize updates to the monitor's VBLANK making the animation more smooth. As it is a more low-level mechanism it works better and faster too.
Upvotes: 2