function draw() {
background(51);
balloon.show();
balloon.update();
score.time();
var state = 0;
if (frameCount % 100 == 0) {
blocks.push(new Blocks());
}
while (state != 1) {
for (var i = blocks.length-1; i >= 0; i--) {
blocks[i].show();
blocks[i].update();
if (blocks[i].offscreen()) {
blocks.splice(i, 1);
}
if (blocks[i].hits(balloon)) {
blocks = null
console.log("hitted");
state = 1;
}
}
}
}
So basically I just want the blocks array to stop drawing themselves on the screen once the "balloon" object hits one of them. I get no error after running it, and I get the text written on the console, so the collision is working as intended. Any ideas why the loop doesn't stop?
Related
Tried to make a script like this:
let intervals = [],
isClick = [],
isGameOver = false,
countElement = 3,
count = 0,
gameOver = function () {
if (isGameOver) {
return;
}
isGameOver = true;
if (countElement <= count) {
for (var i = 0; i < intervals.legth; ++i) {
clearInterval(intervals[i]);
}
intervals = [];
countElement = 0;
}
},
elm = function (index) {
return function () {
if (isGameOver || isClick[index]) {
return null;
}
isClick[index] = true;
clearInterval(intervals[index]);
intervals[index] = null;
if (!intervals.filter(a => a).length) {
count = countElement;
gameOver();
return;
}
};
};
for (let i = 0; i < 17; ++i) {
setTimeout(() => {
element.on('pointerup', elm(i));
intervals[i] = setInterval(() => {
if (countElement <= count) {
clearInterval(intervals[i]);
gameOver();
return;
}
if (-64 > element.position.y) {
clearInterval(intervals[i]);
intervals[i] = null;
++count;
} else {
element.position.y -= 30;
}
}, pos.speed);
}, pos.startTime * i);
}
It actually works, but for some reason it doesn't always work as it should.
Perhaps I'll tell you right away what is required ..
It is necessary to generate the required number of elements and move along the axis Y.
They must have different speeds.
I tried to solve it like this:
let rnd = function (min, max) {
return Math.floor(Math.random() * (max - min) + min);
}, pos = {
speed: Math.floor(rnd(100, rnd(370, 470))),
startTime: Math.floor(rnd(rnd(370, 470), rnd(700, 1000)))
}
In general, I would like to see the elements start flying with different departure and flight speeds, there were attempts that can be missed if the element flew away, when you click on the element, it stops.
Well, in fact, if all the elements flew out - it doesn't matter if they flew away or stopped, the main thing is that all intervals should stop and there would be a kind of exit ...
Connoisseurs help out how this can be done without third-party libraries?
How to do it correctly, please show.
Problem solved...
There was no need to zero out countElement and intervals in gameOver.
Because of this, subsequent intervals were looped, as the indices were violated.
I have the following class:
// Population class
class Population {
constructor(size, target, mutationRate, minFitness) {
this.target = target;
this.size = size;
this.individuals = [];
// contains phrases (strings)
this.matePool = [];
this.mutationRate = mutationRate;
this.bestFitness = 0;
this.bestPhrase = "";
this.totalGenerations = 0;
// Stopping criterion
this.minFitness = minFitness;
}
clearMatePool () {
this.matePool = [];
}
addIndividual(newIndividual) {
this.individuals.push(newIndividual);
}
evaluate() {
let fitness = 0;
for (let i = 0; i < this.size; i++) {
fitness = this.individuals[i].fitnessFunct(this.target);
// Update best fitness and best phrase
if (fitness > this.bestFitness) {
this.bestFitness = fitness;
this.bestPhrase = this.individuals[i].getGenotype();
}
}
// Stopping criterion
if (this.bestFitness < this.minFitness) {
return true;
}
else {
return false;
}
}
buildMatePool() {
for (let i = 0; i < this.size; i++) {
let n = Math.round(this.individuals[i].getFitness() * 100);
for (let j = 0; j < n; j++) {
this.matePool.push(this.individuals[i].phrase);
}
}
}
reproduce() {
// Create new generation
for (let i = 0; i < this.size; i++) {
// Pick 2 parents
let a, b, child, midpoint;
while (true) {
// Index of parentA
a = getRandomIntInclusive(0, this.matePool.length - 1);
// Index of parentB
b = getRandomIntInclusive(0, this.matePool.length - 1);
// Be sure you have picked two unique parents (phrases)
if (this.matePool[a] === this.matePool[b]) {
continue;
}
else {
break;
}
}
// Crossover
child = this.crossover(a, b);
// Mutation
this.mutation(child);
// The new child is part of the new population
this.individuals[i] = child;
}
this.totalGenerations += 1;
}
crossover(a, b) {
let child = new Individual(this.target.length);
child.setGenotype("");
let midpoint = getRandomIntInclusive(0, this.target.length-1);
for (let i = 0; i < this.target.length; i++) {
if (i < midpoint) {
child.phrase = child.phrase + this.matePool[a].charAt(i);
}
else {
child.phrase = child.phrase + this.matePool[b].charAt(i);
}
}
return child;
}
mutation(individual) {
for (let i = 0; i < this.target.length; i++) {
// The block inside the conditional statement would be executed 1% of the time.
if(Math.random() < this.mutationRate) {
// replace char with a new random character
individual.phrase = individual.phrase.substr(0, i) + String.fromCharCode(getRandomIntInclusive(32, 128)) + individual.phrase.substr(i + 1);
}
}
}
}
I have the following DOM elements:
// Shows the current generation
var totalGenerationsHTML = $('#total-generations');
// Shows the best phrase so far
var bestPhraseHTML = $('#best-phrase');
// Shows the best fitness so far
var bestFitnessHTML = $('#best-fitness');
// Shows the result of the reproduction process (child)
var processingHTML = $('#processing');
And the following code section:
var condition = population.evaluate();
while (condition) {
// Processing
population.buildMatePool();
population.reproduce();
population.clearMatePool();
condition = population.evaluate();
}
I need to update the value of the DOM elements in each iteration. I've tried different loop implementations using setInterval() and setTimeout() but the output was out of sync. Any help will be useful.
P.S. I am new to JavaScript. Please excuse any mistakes.
while (condition) {}
Is so called blocking code, since that loop runs without giving the browser a chance to actually render the result, you wont see anything and probably the UI freezes.
You need to use either setTimeout, setInterval or as recommended for animation requestAnimationFrame
but the output was out of sync
Changing the DOM and so visible things on a page is an asynchronous Process and while Js is running, the browser does not repaint. A while loop is running JS.
For more detailed information have a look here
#philipp was right. requestAnimationFrame() is the solution.
Basically requestAnimationFrame() is:
Synchronized with browser repaint (setTimeout and setInterval are not)
Optimized for animation
Runs as fast as the screen will allow
Battery saver
The following HTML/JS/CSS snippet demonstrates the use of requestAnimationFrame().
var currentPos = -200;
var element = document.getElementById("heading");
// Animation loop
function moveRight() {
currentPos += 5;
heading.style.left = currentPos + "px";
window.requestAnimationFrame(moveRight);
if(currentPos >= 800) {
currentPos = -200;
}
}
moveRight();
h1 {
position: relative;
border: 5px solid black;
text-align: center;
width: 200px;
}
div {
width: 800px;
height: 400px;
background-color: lime;
margin: 0 auto;
overflow: hidden;
}
<!DOCTYPE html>
<html>
<head>
<title>Animation Loops</title>
</head>
<body>
<div>
<p>requestAnimationFrame()</p>
<ol>
<li>Synchronized with browser repaint</li>
<li>Optimized for animation</li>
<li>Runs as fast as the screen will allow</li>
<li>Battery saver</li>
</ol>
<p>
Traditionally to create an animation in JavaScript, we relied
on setTimeout() called recursively or setInterval() to
repeatedly execute some code to make changes to an element
frame by frame, such as once every 50 milliseconds. What we
specify as the delay (ie: 50 milliseconds) inside these
functions are often times not honoured due to changes in user
system resources at the time, leading to inconsistent delay
intervals between animation frames.
</p>
<p>
<strong>DO NOT</strong> use setInterval() or setTimeOut() for
animations, use requestAnimationFrame() instead.
</p>
<h1 id="heading">Animation Loops</h1>
</div>
</body>
</html>
I'm working on a project where I'm writing the game Tetris to be played in a browser using Javascript. I'm having a problem where the game simply stops working. It doesn't give any error messages. It's as if the game pauses itself after a random amount of time (usually around 30 seconds), after which nothing happens.
I'm using a game loop to manage the game. Here is the highest level section of code which manages the game.
var cycle = function GameTick(elapsed){
if(menuStatus != "game"){
renderMenu();
updateMenu();
}
if(menuStatus == "game"){
render();
update();
}
requestAnimationFrame(cycle);
}
My first instinct says that after running this for a while, constantly calling new iterations of the GameTick function, it runs out of stack space, but I have no idea if this is correct. Thanks for your help, and I'd be happy to clarify anything that doesn't make sense. I know I haven't given you much to go on, but I'm not sure what else would help besides posting the entire game code (which is very long).
EDIT: I'm posting my render and update functions as requested
function update(){
var currentTime = performance.now();
currentTime = Math.trunc(currentTime);
if((currentTime - beginTime) > dropTime)
{
pieceMoved = movePieces();
}
if(!pieceMoved && (currentTime - beginTime) > dropTime) //If no pieces can move
{
setPieceLocation();
handleFullRows();
spawnNewPiece();
console.log(pieceArray.length)
}
if(pieceArray.length > 0)
console.log(pieceArray[pieceArray.length - 1].color);
if((currentTime - beginTime) > dropTime)
beginTime = performance.now();
}
function render(){ //This function handles the rendering
ctx.fillStyle = "white";
ctx.fillRect(0, 0, canvas.width, canvas.height);
if(pieceArray.length > 0) //This renders the current moving piece
{
for(var j = 0; j < 4; j++)
{
drawColoredBlock(pieceArray[pieceArray.length - 1].color,
pieceArray[pieceArray.length - 1].xCoordinates[j],
pieceArray[pieceArray.length - 1].yCoordinates[j]);
}
}
for(var i = 0; i < 10; i++) //This renders all of the pieces that have been placed
{
for(var j = 0; j < 20; j++)
{
if(gameBoard[i][j] != 'white')
{
drawColoredBlock(gameBoard[i][j], i, j);
}
}
}
}
I am making a small music player for embedding into a blog. It has a playlist functionality. I am having an issue where I cannot skip from the first song in the playlist to the last if you press previous on the first song. Instead of showing the last value it just shows 'undefined'. Does anyone know what might be causing this?
Here is the JS Fiddle with my code in it, here are the two functions causing the issue:
function prevSong(){
if (counter === 0){
counter = songs.length;
}
else{
counter = counter - 1;
};
changeSong();
};
function nextSong(){
if (counter === songs.length){
counter = 0;
}
else{
counter = counter + 1;
};
changeSong();
};
You're trying to go beyond the end of your array. Your array has 3 elements (0, 1, 2). songs.length == 3. songs[3] is invalid.
Use the following instead of what you have:
function prevSong()
{
if (counter === 0)
{
counter = (songs.length-1);
}
else
{
counter = counter - 1;
}
changeSong();
}
function nextSong()
{
if (counter === (songs.length-1))
{
counter = 0;
}
else
{
counter = counter + 1;
}
changeSong();
}
I have a game I am making: http://www.taffatech.com/DarkOrbit.html
What I want to be able to do is when I kill a certain amount of enemies it will level up. This part is working, however I also want the spawn amount to increase when you level up.
In my loop function:
function Loop()
{
if (isPlaying == true)
{
updateLevel(); //this updates the level but it calls it every frame which might be bad.
Player1.draw();
drawAllEnemies();
updateStats();
requestAnimFrame(Loop);
}
The update level function:
function updateLevel()
{
if(Player1.enemiesKilled <3)
{
level = 1;
}
else if(Player1.enemiesKilled > 3 && Player1.enemiesKilled <= 9)
{
level = 2;
}
else if(Player1.enemiesKilled > 9 && Player1.enemiesKilled <=18)
{
level = 3;
}
else if(Player1.enemiesKilled > 18 && Player1.enemiesKilled <= 38)
{
level = 4;
}
else if(Player1.enemiesKilled > 38 && Player1.enemiesKilled <= 70)
{
level = 5;
s
}
else if (Player1.enemiesKilled > 120)
{
level = 6;
}
if(level == 1)
{
spawnAmount = 1;
}
else if(level == 2)
{
spawnAmount = 2;
}
else if(level == 3)
{
spawnAmount = 3;
}
else if(level == 4)
{
spawnAmount = 4;
}
else if(level == 5)
{
spawnAmount = 5;
}
else if(level == 6)
{
spawnAmount = 6;
}
}
spawn enemy function:
function spawnEnemy(number) //total enemies starts at 0 and every-time you add to array
{
for (var x = 0; x < number; x++)
{
enemies[enemies.length] = new Enemy();
}
}
My init:
function init()
{
spawnEnemy(spawnAmount);
drawMenu();
sndIntro.play();
document.addEventListener('click', mouseClicked ,false);
}
I tried adding: spawnEnemy(spawnAmount); to after I change the spawn amount global var in the updateLevel function but as this gets called every frame it makes hundreds of enemies fly at me. How can I solve this problem?
If I try something like this hundreds of enemies are genrated, I assume because it gets called every frame:
if(level == 1)
{
spawnAmount = 1;
spawnEnemy(spawnAmount);
}
If your intent is to increase the spawn rate on each level, I'd suggest you start off with a spawnRate first. Let spawnRate represent the number of game updates in between enemy spawns. Every time an enemy spawns, decrement another variable (say spawnRateCountdown), until that variable reaches 0, Then spawn an enemy. Reset the variable back up to the spawnRate. Rinse and repeat.
spawnRate = 60; /* one enemy every 60 updates. */
spawnRateCountdown = spawnRate;
function updateLoop() {
spawnRateCountdown--;
if (spawnRateCountdown == 0) {
spawnRateCountdown = spawnRate;
spawnEnemy();
}
}
After that, you can just update your spawnRate variable as you see fit.
The problem is that you don't create new enemies from time to time, but just shift the same enemy further away on its explosion (i.e. call recycleEnemy method). This single enemy is spawned only once: spawnEnemy function is called in init method only with argument 1.
You should rework enemy spawning algorithm. Below is a pretty good sample, which increases enemy spawning rate:
Remove number argument from spawnEnemy function: spawn only one enemy
Change Enemy.prototype.recycleEnemy function to remove this from enemies. Be cautious: if you'll call this function inside a loop by enemies, you will need to decrease counter to make sure that you won't skip next enemy: --i
Add a function getSpawnCooldown() { return 100 / spawnAmount; } (100 is approximate constant, you can variate it to change initial spawning rate)
Add a variable time which means the number of effective Loop calls from the beginning, defaults to 0
Add a variable lastSpawnTime, set it to 0
Inside if block of Loop function, add lines:
++time
if (time >= lastSpawnTime + getSpawnCooldown()) spawnEnemy();
In spawnEnemy function, add a line: lastSpawnTime += getSpawnCooldown()
Generally, you should better structurize your code. At least, split it to several js files which are responsible for different parts of your application.