При нажатии на мышь срабатывают сразу несколько событий из-за анимации (canvas игра)

105
09 марта 2021, 14:00

Я уже задавал вопрос по этому проекту (В какой-то момент, отскакивающий от краёв канваса мяч, попадает в баг и ведет себя аномально), код тот же, только доработан.

В этой игре нужно сделать так, чтобы при нажатии на пузырь он лопался, ну а при промахе - создавался новый. Вот только, видимо из-за огромной частоты requestAnimationFrame, нажатие мыши интерпретируется несколько раз и происходит одновременно оба события. Часть кода с условиями событий:

document.addEventListener("mousedown", function() {
    for (let i=0; i<balls.length; i++) {
    if(dotInObj(Mouse.x, Mouse.y, balls[i].xPos, balls[i].yPos, balls[i].ballSize)){
      balls.splice(i, i);
      pop.play();
    }
    else {
      createBuble(Mouse.x-ri(30, 50)/2, Mouse.y-ri(30, 50)/2);
      hah.play();
    }
  }
});

При нажатии на пузырь, он может либо просто лопнуть, при этом будет воспроизведено оба звука, либо то же самое, только на его месте появляется новый, хотя новые должны появляться только при нажатии на пустоту (не появляются).

Кроме того, иногда удаляются сразу 2, а то и 3 несвязанных с собой пузырей. Что можно сделать?

Весь код:

function ri(min, max) {
  var rand = min - 0.5 + Math.random() * (max - min + 1)
  rand = Math.round(rand);
  return rand;
}
function dotInObj (dotX, dotY, objX, objY, objSize) {
    if((dotX>objX&&dotY>objY)&&(dotX<objX+objSize&&dotY<objY+objSize)) return true;
  else return false;
}
//alert(ri(-10, 10)/10);
var cvs = document.getElementById("canvas");
var ctx = cvs.getContext("2d");
var console = document.getElementById("console");
var speed = 1; // SPEED
var Mouse = {
    x: 0,
    y: 0
}
var hah = new Audio();
var pop = new Audio();
hah.src = "https://www.myinstants.com/media/sounds/combobreak-online-audio-converter_06FGwjv.mp3";
pop.src = "http://soundbible.com/mp3/Blop-Mark_DiAngelo-79054334.mp3";
//hah.play();
cvs.onmousemove = function (event) { 
    Mouse = {
        x: event.pageX - this.offsetLeft,
        y: event.pageY - this.offsetTop
    }
    console.value = "X: "+Mouse.x+"Y: "+Mouse.y;
}
var balls = [];
balls.push(0);
function createBuble(XX, YY) {
    let ballSZ = ri(30, 50);
    balls.push({
      ballSize: ballSZ,
      xPos:         XX,
      yPos:         YY,
      xKof:         ri(1, 10)/10,
      yKof:         ri(1, 10)/10,
      ball:         new Image(),
    });
    balls[balls.length-1].ball.src = "http://pngimg.com/uploads/soap_bubbles/soap_bubbles_PNG37.png";
}
function iter(i) {
    //alert(i);
    createBuble(cvs.width/2, cvs.height/2);
    if (--i > 0) {
      setTimeout(function () { iter(i); }, 1000);
    }
}
iter(5);    
function draw() {
 ctx.clearRect(0,0,cvs.width,cvs.height);
 for (let i=1; i<balls.length; i++) {
   ctx.drawImage(balls[i].ball, balls[i].xPos, balls[i].yPos, balls[i].ballSize, balls[i].ballSize);
   balls[i].xPos+=balls[i].xKof*speed;
   balls[i].yPos+=balls[i].yKof*speed;
   if (balls[i].yPos+balls[i].ballSize>=cvs.height||balls[i].yPos<=0) {
    balls[i].yKof += Math.random()/10;
    balls[i].yKof =  Math.abs(balls[i].yKof) * Math.sign(-balls[i].yPos);
   }
   if (balls[i].xPos+balls[i].ballSize>=cvs.width||balls[i].xPos<=0) {
    balls[i].xKof += Math.random()/10;
    balls[i].xKof =  Math.abs(balls[i].xKof) * Math.sign(-balls[i].xPos); 
   }
 }
 requestAnimationFrame(draw); 
 //console.value = "kek";
}
document.addEventListener("mousedown", function() {
    for (let i=0; i<balls.length; i++) {
    if(dotInObj(Mouse.x, Mouse.y, balls[i].xPos, balls[i].yPos, balls[i].ballSize)){
      balls.splice(i, i);
      pop.play();
    }
    else {
      createBuble(Mouse.x-ri(30, 50)/2, Mouse.y-ri(30, 50)/2);
      hah.play();
    }
  }
});
balls[1].ball.onload = draw;

JSFiddle: https://jsfiddle.net/sazdan/60pqsLxv/267/

Answer 1

Ваш алгоритм сначала должен проверить все пузыри в цикле не попали ли они под клик. А затем уже выйдя из цикла по условию создавать новый пузырь.

Удаление нескольких пузырей происходило из за того что вы использовали splice(i, i); а нужно splice(i, 1);

Переделал перебор от хвоста в начало, потому что при удалении хвост сдвигается и перебор в прямом направлении будет некорректным.

Вот исправленный кусок

document.addEventListener("mousedown", function() {
  t = true;
    for (let i=balls.length-1; i>0; i--) {
    if(dotInObj(Mouse.x, Mouse.y, balls[i].xPos, balls[i].yPos, balls[i].ballSize)){
      balls.splice(i, 1);
      pop.play();
      t = false;
    }
  }
  if (t==true) {
      createBuble(Mouse.x-ri(30, 50)/2, Mouse.y-ri(30, 50)/2);
      hah.play();
}
});

Если хотите чтобы пузыри удалялись по одному даже если под мышкой было несколько то вот

document.addEventListener("mousedown", function() {
  t = true;
    for (let i=balls.length-1; i>0; i--) {
    if(dotInObj(Mouse.x, Mouse.y, balls[i].xPos, balls[i].yPos, balls[i].ballSize)){
      balls.splice(i, 1);
      pop.play();
      t = false;
      break;
    }
  }
  if (t==true) {
      createBuble(Mouse.x-ri(30, 50)/2, Mouse.y-ri(30, 50)/2);
      hah.play();
}
});
READ ALSO
Не срабатывает window.open

Не срабатывает window.open

Простая функция, которой передаётся объект blob:

92
Не работает форма для регистрации

Не работает форма для регистрации

Есть форма для регистрации, но про нажатии на кнопку ничего не происходитКод формы:

81
Создание CSS3 эффекта мигающего века

Создание CSS3 эффекта мигающего века

Я пытаюсь создать экран ожидания c цифрами обратного отсчета, который показывает глаз вместе с веком, и глазное яблоко с эффектом радужной...

100
Простая смена классов JS

Простая смена классов JS

У меня есть валидация с помощью регулярки, где я проверяю совпадают ли условия с введенными даннымиСейчас показан пример где нужно ввести...

100