SetTimeOut() in THREE JS - javascript

I realize a project for the master degree.
I must create a three js space invaders game.
The project is well underway but i have a problem. My aliens ( THREE.Mesh object ) must be able to fire randomly.
To carry out that, i've created a function which should draw a random number. This function works.
The problem comes the animate() function. In fact i can't put a SetTimeOut() the animate() function.
The SetTimeOut() works the first time animate() is called but after there is no timer. The code executing continually without waiting the timer.
Maybe the problem coming because animate is continually called by requestAnimationFrame();
My code :
Index.html =>
if (!init())animate();
function animate(){
requestAnimationFrame( animate );
level1.animate();
render();
}
Level.js =>
Level.prototype.animate = function()
{
//Timer doesn't work
var that = this;
//Just a test with a simple console.log test
setTimeout(function() { console.log("test"); },10000);*/
this.sky.rotation.x -=0.005;
this.spaceship.fire();
for (var i=0; i<this.ducks.length;i++)
{
this.ducks[i].move();
if (this.ducks[i].is_ready_to_fire())
this.ducks[i].fire_if_ready();
}
};
With this example the program will wait 10 seconds the first time before print "test" and after the first call, print "test" without waiting.
Have you any ideas ?
Thank you very much.
Sorry for my poor english.

The question if you need a timer for your purpose.
If I understand your problem correctly, you need the aliens to fire after a random amount of time.
If you don't care about the exact amount of time and only about the aliens shooting at random occasions, I'd use a counter on each alien to count the frames until it shoots.
So your code will look something like this:
var MAX_FRAMES_TO_WAIT = 600000;
Alien.prototype.init = function() {
this.framesUntilFire = Math.round(Math.random() * MAX_FRAMES_TO_WAIT);
}
Alien.prototype.fireWhenReady = function() {
if(--this.framesUntilFire === 0) {
this.fire();
this.framesUntilFire = Math.round(Math.random() * MAX_FRAMES_TO_WAIT);
}
}
function animate() {
requestAnimationFrame(animate);
/* ... */
for (var i=0; i<this.ducks.length;i++)
{
this.ducks[i].move();
this.ducks[i].fireWhenReady();
}
That should do the trick. Be aware that this will mean that the enemies fire quicker when the framerate is higher and slower when the framerate should drop.
You can counter that with counting the framerate as well and using it as a divider to level it out.
I hope that helped you a bit!

You can avoid setting a new timer every frame by simply resetting a new timer once the previous one has finished. A simple solution for this is recursive:
Level.prototype.fireDelayed = function() {
setTimeout(function() {
if (!this.currentLevel) {
return;
}
this.fireDelayed();
this.fire();
}.bind(this), Math.random() * 1000);
};
It simply stops firing if the level is no longer the currentLevel.
Does that make sense?

Related

setTimeout being called multiple times

I can't figure out why setTimeout is being called multiple times in my code.
Here's a snippet of the code with what I thought was irrelevant removed:
let dead;
setup()
{
dead = false;
}
draw()
{
if(fell == true)
{
dead = true;
}
mechanics();
}
function mechanics()
{
let triggerVar;
if(dead == true)
{
triggerVar = 1;
dead = false;
}
if(triggerVar == 1)
{
setTimeout(resetG, 1500);
triggerVar = 0;
}
}
function resetG()
{
lives -= 1;
position = 0;
}
I can't tell what I'm doing wrong because whenever the character dies and setTimeout is called, it is actually not only called after the delay but also for the exact same duration after it is triggered. So in this case it is triggered first after 1500 millis and then every frame for another 1500 millis.
I managed to find the problem, which was not with the code I posted. The problem was that the constructor code that makes the object that changes dead to true if certain conditions are met was being called every frame from the moment it triggered death until the first instance of setTimeout kicked in, which means setTimeout was called every frame for 1500 milliseconds.
Chances are that you mechanics() function is called multiple times, you may give a variable to the settimeout like:
let timeoutID= setTimeout(resetG, 1500);
And in the proper place to clear it, for example after lifecycle
clearTimeout(timeoutID);

How to I work with window.requestAnimationFrame and yet be able to change it's speed?

Is there a way to temporarily modify the speed of something with window.requestAnimationFrame in the middle of say, a classic Snake game?
See the following code:
class Game {
constructor(snake) {
this.snake = snake;
}
draw() {
this.snake.slither()
this.snake.draw()
}
start() {
document.addEventListener("keydown", keyDownEvent);
let x = 8;
interval = setInterval(this.draw.bind(this), 1000 / x);
}
}
In the above example, if I wanted to temporarily change the speed, I would just change the interval speed, and then after setTimeOut, return the speed back to normal.
Unfortunately, and for the best, you cannot change the speed of the requestAnimationFrame speed as it is calculated by the browser to ensure a good performance.
You can however, like you described yourself, change an interval speed. But setting setInterval doesn't allow the interval speed to be changed, unless being stopped and restarted. Alternatively you could use a function which uses setTimeout and calls itself after each timeout is being called.
The setTimeout should use a variable or property that is available in all function scopes to use and modify the interval rate.
let timeout = null;
let interval = 0;
function render() {
// Render your animation.
}
function draw() {
timeout = setTimeout(() => {
requestAnimationFrame(render);
draw();
}, interval);
}
function stop() {
if (timeout !== null) {
clearTimeout(timeout);
}
}
function changeDrawSpeed(times) {
interval = 100 * times;
}
draw();
setTimeout(changeDrawSpeed, 1000, 50);
setTimeout(changeDrawSpeed, 3000, 0);
Edit: I just forked and edited a bit a previous example of mine for you:
https://jsfiddle.net/ibowankenobi/2k81ya5q/
You can certainly write a routine to control your game frames, I have a small library for instance where I can do:
rafx.async({frame:0})
.animate(function(frames){
let frame = ++frames.frame;
if (!(frames % 3)) { //skip every third frame
return frames;
}
//game logic here
}).until(function(frames){
//return true to quit;
}).then(//whatever follows)
You can also pass additional variables that modify in-run-time how animation changes (frames or whatever).
https://github.com/IbrahimTanyalcin/RafX

javascript sprite animation in a game

I am working on a simple animation of a sprite in a little Javascript game, a mosquito that is changing state while flying towards right. These states correspond to two different images in my tile sheet.
So far I have been doing this, but the animation is very irregular:
for (var i = 0; i < mosquitos.length; i++) {
var mosquito = mosquitos[i];
setInterval (updateAnimation, 500);
mosquito.update();
// rest of code non-relevant to animation here...
and then, later:
function updateAnimation () {
next();
function next () {
mosquito.state = mosquito.FLYINGRIGHT1;
setTimeout (previous, 500);
function previous () {
mosquito.state = mosquito.FLYINGRIGHT;
}
}
}
The two states are of course FLYINGRIGHT and FLYINGRIGHT1...
problem is that mosquito starts animating very quickly and very irregularly. I would like it to change state, i.e. every half of a second. I tried with different time periods but it is always the same effect.
I can produce a jsfiddle of the whole thing, if what I am missing is not so obvious.
Thank you for any help and insights.
Here's the game in question, from my website:
http://www.retroinvaders.net/laurasworld/src/laurasTriviaLevels.html
I think you should rethink this part:
var mosquito = mosquitos[i];
setInterval (updateAnimation, 500);
I suppose you want every new 'updateAnimation' get a different mosquito (mosquitos[0], mosquitos[1] .. mosquitos[i]). But what really happens is something different. Every time you get the same mosquito (mosquitos[i]).
This is because of asynchronous behaviour of setInterval() function.
function updateAnimation () {
next();
function next() {
// every time, mosquito is equal to mosquitos[i]
// and NOT mosquitos[0], then mosquitos[1] ...
mosquito.state = mosquito.FLYINGRIGHT1;
setTimeout (previous, 500);
function previous()
{
mosquito.state = mosquito.FLYINGRIGHT;
}
}
}

Worn out getting animation to sequence

This is originally from (Pause execution in while loop locks browser (updated with fiddles))
I have been at this all day and I can't figure out how to keep javascript from advancing to the next line and in essence executing all lines at once. I have tried every combination of delay / setTimeout I can think of to no avail.
I just want the elements in the array to flash once then pause, then do it again for another element in the array till all elements have been removed and the array is empty.
But because javascript is executing all lines at once I end up with the appearance of all elements flashing at the same time.
Here is the fiddle:
http://jsfiddle.net/ramjet/xgz52/7/
and the relevant code:
FlashElement: function () {
while (elementArray.length) {
alert('a ' + elementArray.length);
var $el = elementArray.eq(Math.floor(Math.random() * elementArray.length));
PageLoadAnimation.FlashBlast($el);
alert('delay complete');
elementArray = elementArray.not($el);
alert('array popped');
alert('z ' + elementArray.length);
}
},
ANSWER FOR THIS SITUATION. Hopefully it will help others.
As Zach Saucier points out the loop was really my problem...but not the only problem. I was the other problem(s).
Me first.
Fool that I am I was really causing my own complications with two things I was doing wrong.
First using jsfiddle my javascript would error due to syntax or some such thing but fiddle doesn't tell you that (to my knowledge) so my fiddle wouldn't run but I took it in pride as MY CODE IS FINE stupid javascript isn't working.
Second I was passing my function to setTimeout incorrectly. I was adding the function parens () and that is not correct either which would bring me back to issue one above.
WRONG: intervalTimer = setInterval(MyFunction(), 1500);
RIGHT: intervalTimer = setInterval(MyFunction, 1500);
As for the code. As Zach pointed out and I read here (http://javascript.info/tutorial/settimeout-setinterval) while he was responding setting a timeout in a loop is bad. The loop will iterate rapidly and with the timeout one of the steps in the loop we get into a circular firing squad.
Here is my implementation:
I created a couple variables but didn't want them polluting the global scope so I created them within the custom domain. One to hold the array of elements the other the handle to the setInterval object.
var PageLoadAnimation =
{
elementArray: null,
intervalTimer: null,
....
}
In my onReady function (the one the page calls to kick things off) I set my domain array variable and set the interval saving the handle for use later. Note that the interval timer is how long I want between images flashes.
onReady: function ()
{
elementArray = $('#PartialsContainer').children();
//black everything out just to be sure
PageLoadAnimation.BlackOutElements();
//flash & show
intervalTimer = setInterval(PageLoadAnimation.FlashElement, 1500);
},
Now instead of looping through the array I am executing a function at certain intervals and just tracking how many elements are left in the array to be flashed. Once there are zero elements in the array I kill the interval execution.
FlashElement: function ()
{
if(elementArray.length > 0) //check how many elements left to be flashed
{
var $el = PageLoadAnimation.GrabElement(); //get random element
PageLoadAnimation.FlashBlast($el); //flash it
PageLoadAnimation.RemoveElement($el); //remove that element
}
else
{
//done clear timer
clearInterval(intervalTimer);
intervalTimer = null;
}
},
So the whole thing is:
var PageLoadAnimation =
{
elementArray: null,
intervalTimer: null,
onReady: function () {
elementArray = $('#PartialsContainer').children();
//black everything out just to be sure
PageLoadAnimation.BlackOutElements();
//flash & show
intervalTimer = setInterval(PageLoadAnimation.FlashElement, 1500);
//NOT this PageLoadAnimation.FlashElement()
},
BlackOutElements: function () {
$('#PartialsContainer').children().hide();
},
FlashElement: function ()
{
if(elementArray.length > 0)
{
var $el = PageLoadAnimation.GrabElement();
PageLoadAnimation.FlashBlast($el);
PageLoadAnimation.RemoveElement($el);
}
else
{
//done clear timer
clearInterval(intervalTimer);
intervalTimer = null;
}
},
GrabElement: function()
{
return elementArray.eq(Math.floor(Math.random() * elementArray.length));
},
RemoveElement: function($el)
{ elementArray = elementArray.not($el); },
FlashBlast: function ($el) {
//flash background
$el.fadeIn(100, function () { $el.fadeOut(100) });
}
}
Hope that help others understand the way to go about pausing execution in javascript.
The reason why you were having trouble is because setTimeout function is non-blocking and will return immediately. Therefore the loop will iterate very quickly, initiating each of the timeouts within milliseconds of each other instead of including the previous one's delay
As a result, you need to create a custom function that will wait on the setInterval to finish before running again
FlashElement: function () { // Call it where you had the function originally
myLoop();
},
...
function myLoop() {
setTimeout(function () { // call a setTimeout when the loop is called
var $el = elementArray.eq(Math.floor(Math.random() * elementArray.length));
PageLoadAnimation.FlashBlast($el);
elementArray = elementArray.not($el);
if (0 < elementArray.length) { // if the counter < length, call the loop function
myLoop();
}
}, 1000)
}
Feel free to change the delay to whatever value you wish (3000ms to let each fade finish before the last at the moment). If you want to start the fade in of the next before the previous ends and keep them in their original positions you would have to animate the opacity using .css instead of using fadeIn and fadeOut
My answer is based on this answer from another SO question

Accessing 'this' variable in function passed to setInterval/setTimeout

I'm creating a simple game with a character that can jump, move right, and move left.
I'm having trouble with the jump function, which uses a setInterval.
Here is the function:
jumpUp: function (speed) {
setInterval(function () {
this.yPos += 10;
this.playerElement.css("top", '-=' + 10);
alert("dude, im not moving....so stop trying"); //for some reson, this line works and other dont.
}, 100);
}
I should add that the code works without the setInterval. I really don't have any idea why it's not working when I add the setInterval.
My questions:
What is stopping this code from running?
Is setInterval a good way to make a character look like it jumping and landing? Or should i use different method?
EDIT 1:
fiddle
The problem is your use of this. When the function you pass to setInterval is called, this will be the global object (window in browsers). You need to preserve the this value from when you call setInterval. One way of doing this is to store the value of this into a variable, which will then be closed over by the anonymous function (which is a closure):
jumpUp: function (speed) {
var self = this;
setInterval(function () {
self.yPos += 10;
self.playerElement.css("top", '-=' + 10);
}, 100);
}
EDIT:
To answer your second question, a better approach to animating a sprite (like your character) is to store the character's velocity, and then have a single game loop that will calculate the next position of the sprite based on that information. A very simple example would look like:
// Somewhere else in the code:
function tick() {
// move player by player.velocity.x and player.velocity.y
// code to decelerate player gradually, stop player when they hit a wall/floor, etc...
// A very simple example:
if (player.velocity.y > 0) {
player.velocity.y -= 1
}
// call next tick() (setTimeout, or preferably requestAnimationFrame)
}
// In the player code:
velocity: {x: 0, y: 0},
jumpUp: function () {
this.velocity.y -= 10;
},
moveRight: function () {
this.velocity.x += 10;
}
As darma and danronmoon pointed out, you have a scoping problem with this.
Try the following code:
jumpUp: function (speed) {
var that = this;
setInterval(function () {
that.yPos += 10;
that.playerElement.css("top", '-=' + 10);
}, 100);
}
I added a variable, that, to maintain the reference to whatever this is supposed to be.
In addition to your closure problem, this is probably going to cause choppy jumping.
Here's another pattern that watches the clock to see how much time has elapsed between each call to the function (setInterval is not consistent):
jumpUp: function (speed) // speed in pixels per second
{
var last = +new Date();
var c = this;
var jumptick = function ()
{
var interval = +new Date() - last;
c.yPos += speed * interval;
c.playerElement.css("top", c.yPos);
if (/* condition for reaching maximum height */) speed = -speed;
if (! /* condition for reaching ground */) setTimeout(jumptick);
// could add an interval but this will lead to fastest frame rate
};
jumptick();
}
Setinterval is not a good way to achieve this because it will use a lot a ressources.
You also need to consider the framerate when you are moving your character, otherwise he will move fast on a fast machine/browser and slow on a slow machine.
A good method is using the requestAnimationFrame method. You can find a javascript file on google that will make it crossbrowser compatible.
Then, everytime your function is called, you will need to check the time elapsed between to frame and move your sprites accordingly. It's more work but that way, your game will run at the same pace on any machine.

Categories