Este foro ya no está activo, así que no puedes publicar nuevas preguntas ni responder a las preguntas existentes.

Detectar choque entre circulos canvas HTML5

28 de febrero de 2015

Quisiera saber cómo puedo hacer que, teniendo dos círculos, como objetos independientes, los cuales están rebotando dentro del canvas de forma libre, cuando se encuentren reboten entre ellos y cambien su dirección.

Adjunto mi código JS para mi canvas, para que sepan cómo lo tengo planteado, gracias:

// Función que crea un circulo.
function circle(x, y, r, color) {
   contexto.fillStyle = color;
   contexto.beginPath();
   contexto.arc(x, y, r, 0, Math.PI*2, true);
   contexto.closePath();
   contexto.fill();
}
 
// Función que crea un rectangulo. 
function rect(x, y, w, h, color) {
   contexto.fillStyle = color;
   contexto.fillRect(x, y, w, h);
}
 
// Función que crea un objeto pelota
function Pelota(x, y, r, color) {
   this.x = x;
   this.y = y;
   this.r = r;
   this.color = color;
   this.dx = 1;
   this.dy = 1.1;
   this.speed = 2;
   this.center = this.r/2;
 
   this.update = function() {   
      if (this.x+this.center > ancho_rectangulo || this.x-this.center < 0) {
         this.dx = -this.dx;
      }
 
      if (this.y+this.center > alto_rectangulo || this.y-this.center < 0) {
         this.dy = -this.dy;
      }
 
      this.x += this.dx * this.speed;
      this.y += this.dy * this.speed;
   }
 
   this.draw = function() {
      circle(this.x, this.y, this.r, this.color);
   }
}
 
//Función que actualiza y dibuja la pelota.
function gameLoop() {
   limpiar();
 
   // Si (pelota.x > width) entonces clearInterval(intervalId);
   pelota1.update();
   pelota1.draw();
 
   pelota2.update();
   pelota2.draw();
}
 
// Función que limpia los pasos de la pelota
function limpiar() {
   contexto.fillStyle = "grey";
   rect(0, 0, ancho_rectangulo, alto_rectangulo);
}
 
// Función para iniciar el programa
function init() {
   canvas = document.getElementById("miCanvas");
   contexto = canvas.getContext("2d");
   ancho_rectangulo = canvas.width;
   alto_rectangulo = canvas.height;
 
   pelota1 = new Pelota(50, 50, 10, "silver");
   pelota2 = new Pelota(50, 250, 10, "black");
 
   setInterval(gameLoop, 20); //Llama a la función gameLoop cada 20 milisegundos de forma indefinida.
}
 
window.onload = init;
 
// Variables.
var canvas, contexto, ancho_rectangulo, alto_rectangulo;

Respuestas

#1

Así por encima será igual que en cualquier lenguaje, en cada loop tendrás que mirar si ambos centros estan a x distancia y si es así es que están tocándose. Oseáse si en cada tick de reloj la distancia entre centros es menor que el radio 1 + el radio 2 (creo que será la formula) entonces hay contacto y si hay contacto pos habrá que modificar los valores que sean de movimiento. no?

@aprendizenlared

1 marzo 2015, 14:00
#2

Sí, creo que lo he pillado, gracias por tu tiempo y por contestar.

@ucip3

1 marzo 2015, 15:35
#3

Estaba leyendo en esta pregunta de StackOverflow posibles soluciones a este problema.

Algunos usuarios sugieren que si es importante ofrecer un gran rendimiento en las animaciones, que sacrifiques un poco la precisión de las colisiones. Para ello, tienes que aproximar los objetos circulares por el mínimo rectángulo envolvente y después, utilizar esta fórmula para detectar colisiones:

function hanColisionado(rectanguloA, rectanguloB) {
    return !(
        ((rectanguloA.y + rectanguloA.height) < (rectanguloB.y)) ||
        (rectanguloA.y > (rectanguloB.y + rectanguloB.height)) ||
        ((rectanguloA.x + rectanguloA.width) < rectanguloB.x) ||
        (rectanguloA.x > (rectanguloB.x + rectanguloB.width))
    );
}

Si en cambio quieres detectar las colisiones con precisión para los objetos circulares, puedes utilizar la siguiente fórmula:

function hanColisionado(x1, y1, w1, x2, y2, w2) {
    var xd = x1 - x2;
    var yd = y1 - y2;
    var wt = w2 + w1;
    return (xd * xd + yd * yd <= wt * wt);
}

Donde (x1, y1) es el centro del primer círculo y w1 su diámetro; mientras que (x2, y2) es el centro del segundo círculo y w2 es su diámetro.

@javiereguiluz

1 marzo 2015, 16:52