I have just made a version of the "10 PRINT" to try to develop my JS skills.
In the end i thought it would be nice to add some sliders and let the viewers adjust some of the parameters to see what effect they have.
You can see what i came up with here: 10PRINT, by Boguz, on GitHub
One of the sliders i wanted to add changes the speed of the animation, but i can get it to work.
To trigger the animation i am using a setInterval like this:
setInterval(function(speed){
draw();
}, speed);
The variable speed is initialized when the document loads
let speed = 50;
and then i am trying to update it (when the viewer moves the slider) like this:
const speedControl = document.querySelector("#speed-slider");
speedControl.oninput = function() {
let speedVal = speedControl.value;
speed = Number(speedVal);
resetDraw();
}
When i log the speed variable i get the right values in the console, but somehow it is not having the desired effect.
If you need to see some more code, please ask me, or take a look at the GitHub Repository.
Any help is very welcome.
Thank you!
In your script store the setInterval value so you can clear it.
// SET INTERVAL
let drawInterval = setInterval(function(){
draw();
}, speed);
then in your resetDraw clear the interval and re set it (it will now use the new speed)
function resetDraw() {
context.clearRect(0, 0, canvas.width, canvas.height);
xpos = 0;
ypos = 0;
draw();
clearInterval(drawInterval);
drawInterval = setInterval(draw, speed);
}
You could also completely remove the initial setInterval and just call the resetDraw once to start.
Clear your old one first, then make a new one
function makeInterval(speed) {
const intervalId = setInterval(function(){
draw();
}, speed);
return intervalId;
}
let intervalId = makeInterval(speed);
...oninput = function() {
clearInterval(intervalId);
let speedVal = speedControl.value;
speed = Number(speedVal);
intervalId = makeInterval(speedVal);
}
The interval time passed to setInterval can't be live updated, it's baked in to the call when you make it.
Related
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
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?
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.
I want to change the speed of steinterval() while running I tried something like this:
inteval = 1;
setInterval(function(){
}, interval);
interval = 2;
Then I tried this which doesn't use intervals, but seems to work fine:
// Time
inteval = 1;
function refresh() {
setTimeout(function() {refresh()},interval);
}
refresh();
interval = 2;
I would rather have used setinterval() but this approach works fine. However in another similar question there is a longer approach: Changing the interval of SetInterval while it's running
If my snippet good in terms of efficiency? Is there a more efficient approach?
How about something like this:
var interval = 1000;
var times = 0;
function runInterval() {
times ++;
if(times == 5) {
interval = 3000;
clearInterval(s);
s = setInterval(runInterval, interval);
}
console.log(times);
}
var s = setInterval(runInterval, interval);
If you want to use setInterval, this is a way to do it. I hope it can help you
So I have this simple HTML:
<span id="badge">0</span>
I want the number 0 to increase by 1 every x milliseconds. How do I do that with Javascript (with or without jQuery)?
Thanks a bunch - I'm new to this :)
You should do this:
<script>
var $badge = $('#badge'); // cache
setInterval(function () {
var value = parseInt($badge.html());
value++;
$badge.html(value);
}, 1000);
</script>
Assuming 1000 milliseconds.
function increment() {
document.getElementById("badge").value = Number(document.getElementById("badge").value) + 1;
setTimeout("increment()",3000);
}
increment()
Every of the answers I see here has the same drawbacks:
performance issue because of selecting the DOM element every ms cycle. Especially when using a heavy library as jQuery.
setInterval() is probably the tool designed for that functionality, but not reliable. It can diverge a lot from the real time, especially when using a small interval. If you want exactly x executions per second, you may google for some timing libraries.
I would code:
var textNode = document.getElementById(badge).firstChild;
var start = Date.now();
window.setInterval(function update() {
textNode.data = Math.round((new Date()-start)/ms);
}, ms);
If you don't want to start at 0, it will be trivial to add an offset (determined before the loop begins), eg.
var start = Date.now() - (textNode.data * ms || 0); // NaN catching, implicit number cast
Something like this?
var millisecs = 10;
setInterval(function() {
var $badge = $('#badge');
$badge.text(parseInt($badge.text())++);
}, millisecs);
http://jsfiddle.net/iambriansreed/MPP8n/3/
Check this http://www.w3schools.com/js/js_timing.asp
A little more about timers:
// setting a variable for your timer will allow you the ability to "turn it on and off"
var tmrChangeI;
// setTimeout is a function to initiate a function once after given amount of milisecs
// whereas setInterval will continue a function until cancled every so many milisecs
// the following wil "turn on" your timer
tmrChangeI = setInterval(function() {
var $badge = $('#badge');
$badge.html($badge.html() + 1);
}, 500); // 500 will = every half of a second
// to "turn off" timer
clearInterval(tmrChangeI);
// or set a bool to test and use timeout to repeat till bool is false
var tmrBool = true;
// establish function to execute
function tmrFunc() {
var $badge = $('#badge');
$badge.html($badge.html() + 1);
if (tmrBool) tmrChangeI = setTimeout(function() { tmrFunc(); }, 500); // 500 will = every half of a second
};
// execute function, begin timer
tmrChangeI = setTimeout(function() { tmrFunc(); }, 500);
// clear via bool allowing one more execution
tmrBool = false;
// clear by force possibly stoping next execution,
// tho in this manner it may be too late if timer is very short
// and maybe overriden by bool still being true, this is not safest
// but is example of how to use setTimeout
clearTimeout(tmrChangeI);
You can use setInterval.
var $badge = $('#badge');
setInterval(function () {
$badge.html(parseInt($badge.html()) + 1);
}, 1);//Specify the milliseconds here, right it will update the value every 1 millisecond
Working demo - http://jsfiddle.net/8FMZh/
You could create a Jquery plugin, so you can reuse whenever you need.
$.fn.increment= function(options) {
var $this = $(this);
var coef = options.coef;
var speed = options.speed;
var value = 0;
setInterval(function(){
value = value + coef ;
$this.html(value);
}, speed);
};
And in your main javascript file :
$("#badge").increment({coef: 1, speed:1000});
working demo : http://jsfiddle.net/8FMZh/102/