I'm trying to create a sort of ecosystem where objects spawn over time. However, when I try using setInterval to increase the amount it doesn't work. It works when I call the function on its own, but not when I use setInterval.
var plantSpawn = 5;
function createPlants() {
setInterval(reproducePlants, 5000);
for(var i=0; i<plantSpawn; i++){
var plant = new Object();
plant.x = Math.random() * canvas.width;
plant.y = Math.random() * canvas.height;
plant.rad = 2;
plant.skin = 'green';
myPlants[i] = plant;
}
}
function reproducePlants() {
plantSpawn += 5;
}
My goal for this is for every 5 seconds, 5 new plants appear. However, when I use the reproducePlants function with setInterval it does not work.
Note: I am calling createPlants() later in my code which makes the first 5 plants show up, but the next 5 won't show up. I am just showing the code that I'm trying to fix
The creation code must be moved inside the function that is repeatedly called.
NOTE: This is not an efficient way if you are going to call reproducePlants infinitely many times, since the myPlants array is reconstructed every time.
// Init with 0, because we increment it inside reproduce Plants
var plantSpawn = 0;
var myPlants = [];
function createPlants() {
reproducePlants();
setInterval(reproducePlants, 5000);
}
function reproducePlants() {
const canvas = document.getElementsByTagName('canvas')[0];
plantSpawn += 5;
for(var i = 0; i < plantSpawn; i++) {
var plant = new Object();
plant.x = Math.random() * canvas.width;
plant.y = Math.random() * canvas.height;
plant.rad = 2;
plant.skin = 'green';
myPlants[i] = plant;
}
}
You don't necessarily need to call the createPlants function from the reproducePlants function. You could add a call after both the functions. If I understand what you are trying to achieve that should do it.
You need to move the code that creates the plants (the for() chunck) inside the function that is called every 5 seconds (reproduce plants). So each time the function is called it will create the plants.
If you are trying to add only the 5 new plants every 5 seconds to your plants array you shouldn't recreate the array each time. It's better to keep track of the last index you have added and then continue right after that.
I created a variable called lastCreatedIndex so you can understand better what is going on. So the first time the code will run plants[i] from 0 to 4, the second 5 to 9...
var myPlants = [];
var lastCreatedIndex;
var plantSpawn;
function createPlants() {
plantSpawn = 5; //Initialize with 5 plants
lastCreatedIndex = 0; // Starts from index 0
reproducePlants();
setInterval(reproducePlants, 5000);
}
function reproducePlants() {
for(var i = 0 + lastCreatedIndex; i < plantSpawn; i++) {
var plant = new Object();
plant.x = Math.random() * canvas.width;
plant.y = Math.random() * canvas.height;
plant.rad = 2;
plant.skin = 'green';
myPlants[i] = plant;
console.log(i); // Output the number of the plant that has been added
}
lastCreatedIndex = i; //Update the last index value
plantSpawn += 5;
}
Related
Im trying create some type of number generator on webpage. I want to show like five numbers before the generated number show. For better imagine, you can look to google generator. When you click generate, it shows like 3-4 numbers before generated number. I use setInterval or setTimeout but i dont know how it works. My js code:
var button = document.querySelector("button");
button.addEventListener("click",function() {
for (var i = 0; i < 8; i++) {
setInterval(textC,5);
}
});
function textC(){
number.textContent = Math.floor(Math.random() * 1000) + 1;
}
Thanks for every help!
The issue with setInterval() is that it will continue forever unless cleared, causing you to keep generating random numbers. Instead you can use setTimeout(), but set the timeout to change based on the value of i in the for loop. That way, each interval will occur 50 m/s after the other.
See example below:
const button = document.querySelector("button");
const number = document.querySelector("#number");
button.addEventListener("click", function() {
for (let i = 0; i < 5; i++) {
setTimeout(textC, 50 * i);
}
});
function textC() {
number.textContent = Math.floor(Math.random() * 1000) + 1;
}
<p id="number"></p>
<button>Generate</button>
Don't use a loop (why not?). Just nest setTimeout and call it until a predefined threshold is reached. It gives you maximum control.
var button = document.querySelector("button");
var number = document.querySelector("#number");
const nRuns = 12;
const timeout = 100;
let iterator = 0;
button.addEventListener( "click", textC);
function textC(){
number.textContent = `${Math.floor(Math.random() * 1000) + 1}\n`;
iterator += 1;
if (iterator < nRuns) {
setTimeout(textC, timeout)
} else{
iterator = 0;
// you control the loop, so it's time for some extra text after it
number.textContent += ` ... and that concludes this series of random numbers`;
}
}
<p id="number"></p>
<button>Generate</button>
I'm creating a game where the computer tries to guess the user's number based on user feedback like too high or too low. I'm using a binary search. The functions work properly, however, every time the buttons are pressed, the code resets to make the original list from 1 to 100 making the guess 50 instead of remembering the new list and guess defined inside my functions.
var list = new Array();
for (i = 0; i <= 100; i++) {
list.push(i)
}
//console.log(list)
// List is intially an empty array (list). The
// for loop generates integers from
// 0 to 100 and pushes them into the array.
var guess = list[Math.floor((list.length / 2))];
console.log(guess);
var toolow = function(guess) {
while (list.includes(guess) == true) {
list.shift()
};
var guess = list[Math.floor((list.length / 2) - 1)];
console.log(list);
console.log(guess)
}
// toolow(guess)
var toohigh = function(guess) {
var last = parseInt(list.length);
while (list.includes(guess) == true) {
list.pop()
};
var guess = list[Math.round(list.length / 2)];
console.log(list);
console.log(guess)
}
// toohigh(guess)
<h1> Guess Your Number </h1>
<button id="TooLow" onclick="toolow(guess);"> Too Low</button>
<button id="TooHigh" onclick="toohigh(guess);">Too High</button>
your over use of the variable guess is causing all sorts of issues
no need to pass guess from onclick to the function
don't declare a var guess inside the functions
et voila - your code works now
var list = new Array();
for (i = 0; i <= 100; i++) {
list.push(i)
}
//console.log(list)
// List is intially an empty array (list). The
// for loop generates integers from
// 0 to 100 and pushes them into the array.
var guess = list[Math.floor((list.length / 2))];
console.log(guess);
var toolow = function() {
while (list.includes(guess) == true) {
list.shift()
};
guess = list[Math.floor((list.length / 2) - 1)];
console.log(list);
console.log(guess)
}
// toolow(guess)
var toohigh = function() {
var last = parseInt(list.length);
while (list.includes(guess) == true) {
list.pop()
};
guess = list[Math.round(list.length / 2)];
console.log(list);
console.log(guess)
}
// toohigh(guess)
<h1> Guess Your Number </h1>
<button id="TooLow" onclick="toolow();"> Too Low</button>
<button id="TooHigh" onclick="toohigh();">Too High</button>
I have an array which is taking values over the space of a second and calculating the average. The array is then permanently storing the values. I need them to remove the values after the average has been calculated. I have tried clearing the array at the end of the calculateAverage function using levelMeasurements = []; and levelMeasurements = 0; but that does not work.
Any help on this would be greatly appreciated, thanks!
My code:
var levelMeasurements = [];
function collectLevelMeasurements() {
levelMeasurements.push(averagingLevel);
}
var collectInterval = window.setInterval(collectLevelMeasurements, 0);
function calculateAverage() {
window.clearInterval(collectInterval);
var avg = 0;
for (counter = 0; counter < levelMeasurements.length; counter++) {
avg += levelMeasurements[counter] / levelMeasurements.length;
}
averageAbsoluteLevel = Math.abs(avg);
averageDbLevel = Tone.gainToDb(averageAbsoluteLevel) * scale + offset;
console.log("Measure for 5 minutes: average level is:" + averageDbLevel);
}
window.setInterval(calculateAverage, 1000);
Your collectInterval timeout is never reinstated after the first time calculateAverage is called, unless the issue is something else not visible from the code.
In your code calculateAverage is called every 1000 milliseconds, but the values stored in levelMeasurements are not updated after the first window.clearInterval.
I made a simplified snippet without Tone lib:
var levelMeasurements = [];
//added this as a easy source of values
let count = 0;
function collectLevelMeasurements() {
// levelMeasurements.push(averagingLevel);
levelMeasurements.push(count++);
}
var collectInterval = window.setInterval(collectLevelMeasurements, 0);
function calculateAverage() {
window.clearInterval(collectInterval);
var avg = 0;
for (counter = 0; counter < levelMeasurements.length; counter++) {
avg += levelMeasurements[counter] / levelMeasurements.length;
}
averageAbsoluteLevel = Math.abs(avg);
averageDbLevel = averageAbsoluteLevel;
//prolly dont need tone lib for this example
// averageDbLevel = Tone.gainToDb(averageAbsoluteLevel) * scale + offset;
console.log("Measure for 5 minutes: average level is:" + averageDbLevel);
//reset these variables
levelMeasurements = [];
count = 0;
console.log(levelMeasurements)//empty array at this point
//reset the collectInterval!
collectInterval = window.setInterval(collectLevelMeasurements, 0);
}
window.setInterval(calculateAverage, 1000);
You can try following:
levelMeasurements.length = 0.
Or refer more solution at here: How do I empty an array in JavaScript?
I want to create a 'random selector' behaviour where the function iterates through an array for a period of time (example: 3 seconds, 5 seconds) while displaying all of array elements fast throughout the iteration until the iteration ends. Just imagine seeing all the elements displayed in a label one after another until it finally stops at an element.
My code so far:
var places = ["Curry Leaf", "Subway", "Burger King"];
function execute_randomizer() {
var place_label = document.getElementById("place_label");
for (var i = 0; i < 100; i++) {
var selected_place = places[Math.floor(Math.random() * places.length)];
setTimeout(function () {
place_label.innerText = selected_place;
}, 400);
}
}
This runs through the iteration and displays an element when the loop is done but it doesn't show each iteration's element. How can I amend this?
EDIT
Even if there's 3 elements, the animations must re-iterate through the array until the duration is completed
Your for finishes iterating before the setTimeout runs, then the function passed to setTimeout runs 100 times using the last value of selected_place.
More about this behavior in setTimeout in for-loop does not print consecutive values
Another problem that I noticed is that your setTimeout will trigger after 400ms, since the for loop will finish in about 1 ms, the function passed to setTimeout will trigger 100 times one after another with no delay in between, for this, I replaced the delay argument of setTimeout from 400 to 400 * i.
var places = ["Curry Leaf", "Subway", "Burger King"];
function execute_randomizer() {
var place_label = document.getElementById("place_label");
for (var i = 0; i < 100; i++) {
var selected_place = places[Math.floor(Math.random() * places.length)];
(function(selected_place){
setTimeout(function () {
place_label.innerText = selected_place;
}, 400 * i);
})(selected_place);
}
}
execute_randomizer();
<span id="place_label"></span>
You could use a closure over the value and a different time for each timeout.
var places = ["Curry Leaf", "Subway", "Burger King"];
function execute_randomizer() {
var place_label = document.getElementById("place_label");
for (var i = 0; i < 10; i++) {
var selected_place = places[Math.floor(Math.random() * places.length)];
setTimeout(function (value) {
return function () {
place_label.innerText = value;
};
}(selected_place), i * 100);
}
}
execute_randomizer();
<div id="place_label"></div>
For a first run through, you could show each element and then take a random element at last value.
function execute_randomizer() {
function cb (value) {
return function () {
place_label.innerText = value;
};
}
var place_label = document.getElementById("place_label");
place_label.innerText = '';
for (var i = 0; i < places.length; i++) {
setTimeout(cb(places[i]), 200 + i * 200);
}
setTimeout(cb(places[Math.floor(Math.random() * places.length)]), 200 + places.length * 200);
}
var places = ["Curry Leaf", "Subway", "Burger King"];
execute_randomizer();
<div id="place_label"></div>
You should change your loop because right now you go in loop 100 times in maybe one millisecond and order 100 of change text but once again in the same time.
So better is wait for time out 400 ms and then make next iteration.
Please remember time out is async.
I think it might be better if you would put the whole function in a timeout. (I'm not sure and didn't test, but that's what I would try).
What I mean is that you just build a function that creates your randomizer and fills in your field. After that you put a timeout to call the same function again + keep a counter that you pass as parameter.
An example of what I mean below: (didn't test it)
var places = ["Curry Leaf", "Subway", "Burger King"];
execute_randomizer(0); /* Start for the first time */
function execute_randomizer(times) {
if(times == 100)
return; /* stop execution */
var place_label = document.getElementById("place_label");
var selected_place = places[Math.floor(Math.random() * places.length)];
place_label.innerText = selected_place;
setTimeout(function () {
execute_randomizer(times+1)
}, 400);
}
I am attempting to create an app in Javascript/HTML/CSS to run board games, so I can teacher my 11th and 12th grades students to do the same. Mostly I've got it working, including a dice roll, but I have a counter that determines whose turn it is that is returning NaN for one of the turns. It does return all other turns, but adds in the NaN as well.
I'm not using a for() loop because it is inside a given function that starts a player turn. All of the answers I've found online and here at StackOverflow refer to issues in counters using the for() loop.
Here's my code:
var p1="Name1";
var p2="Name2";
var p3="Name3";
var playerList=new Array(p1, p2, p3);
var pTurn=0;
var currentPlayer=playerList[pTurn];
function nextPlayer() {
pTurn++;
if(pTurn<playerList.length) {
pTurn=0;
}
currentPlayer=playerList[pTurn];
/* the rest of the function sends the data to the html page and works */
}
You need to reset pTurn when it is too large for the list. That is:
if (pTurn >= playerList.length) {
pTurn = 0;
}
var p1 = "Name1";
var p2 = "Name2";
var p3 = "Name3";
var playerList = new Array(p1, p2, p3);
var pTurn = 0;
var currentPlayer = playerList[pTurn];
function nextPlayer() {
pTurn++;
if (pTurn >= playerList.length) {
pTurn = 0;
}
currentPlayer = playerList[pTurn];
}
for (var i = 0; i < 10; ++i) {
console.log(currentPlayer);
nextPlayer();
}