Эй, я хочу сделать эту маленькую программу с canvas и js, которая обнаруживает столкновения между кругами - PullRequest
0 голосов
/ 08 апреля 2020

Я изучаю холст и рендеринг на нем с помощью JS, и я пытаюсь сделать простую программу обнаружения столкновений, которая заставляет криклов, участвующих в столкновении, приобретать определенный цвет. Пока ... это вроде работает, а это просто означает, что это не работает: D я вижу только случайные круги, которые иногда становятся зелеными, а не те, которые на самом деле сталкиваются.

Так что я хотел опубликовать это здесь и иметь вы, ребята, посмотрите на это и посмотрите, что вы можете найти. Спасибо заранее!

Я считаю, что проблема в функции «столкновений», но я не совсем понимаю, что это такое.

Кстати, я также открыт для рекомендаций по улучшению этот код.

это html и css

<html lang="es">
    <head>
        <link rel="stylesheet" href="index.css">
        <meta charset="utf-8">
        <title>bubbles</title>
    </head>
    <body>

        <p id="fpsIndicator"></p>

        <canvas id="cnv"></canvas>    


    </body>


    <footer>
        <script src="Circle.js"></script>
        <script src="index.js"></script>
    </footer>

</html>
#cnv{
    position: absolute;
    top: 0;
    left: 0;
    height: 100%;
    width: 100%;
    /*background-color: blue;*/
}

это основной JS файл и класс Circle

let fpsInd = document.getElementById("fpsIndicator");
let canvas = document.getElementById("cnv");
let ctx = canvas.getContext("2d");

let frames = 0;
let fps = 0;
let lastCallTime;

let bubbles = 35;
let arrBubbles = [];

const RADIAE = 50;
const COLLISION_COLOR = "green";

adjustCanvas();
window.addEventListener("resize", adjustCanvas);





for(let i = 0; i < bubbles; i++){

    let x = randomInteger(RADIAE, canvas.width-RADIAE);
    let y = randomInteger(RADIAE, canvas.height-RADIAE);

    if(i == 0){
        arrBubbles.push(new Circle(x, y, RADIAE, "blue"));
        continue;
    }

    for(let j = 0; j < arrBubbles.length; j++){

        let d = distance(x, y, arrBubbles[j].x, arrBubbles[j].y);

        if(d <= RADIAE*2){
            x = randomInteger(RADIAE, canvas.width-RADIAE);
            y = randomInteger(RADIAE, canvas.height-RADIAE);
            j = -1;
        }        
    }

    arrBubbles.push(new Circle(x, y, RADIAE, "blue"));
}




loop();
function loop(){
    frames++;
    getFPS();

    if(frames % 3 == 0) 
        fpsInd.innerHTML = "FPS: "+fps;


    ctx.clearRect(0,0,window.innerWidth, window.innerHeight);


    arrBubbles.forEach( (item)=> {
       item.draw(ctx); 
       item.move(canvas.width, canvas.height);    
    });

    collisions();

    requestAnimationFrame(loop);
}


function collisions(){

    for(let i = 0; i < arrBubbles.length; i++){

        let first = arrBubbles[i];

        for(let p = 0; p < arrBubbles.length; p++){

            let second = arrBubbles[p];

            let d = distance(first.x, first.y, second.x, second.y);

            if(d <= first.radius + second.radius){
                second.color = COLLISION_COLOR;
                first.color = COLLISION_COLOR;
            }
            else {
                second.color = "blue";
                first.color = "blue";
            }
        }
    }

}



function distance(x1, y1, x2, y2){
    let distX = x2-x1;
    let distY = y2-y1;

    return Math.sqrt(distX*distX + distY*distY);
}



function randomInteger(min, max){
    return Math.floor(Math.random() * (max-min+1) + min);
}


function adjustCanvas(){
     canvas.setAttribute("width", window.innerWidth);
     canvas.setAttribute("height", window.innerHeight);
}


function getFPS(){

    let delta;

    if(!lastCallTime){
        lastCallTime = Date.now();
        fps = 0;
        return;
    }

    delta = (Date.now() - lastCallTime) / 1000;
    lastCallTime = Date.now();
    fps = Math.floor(1/delta);
}
class Circle{

    constructor(x, y, radius, color){
        this.x = x;
        this.y = y;
        this.radius = radius;
        this.color = color;

        this.velocity = {
            X: randomInteger(1, 3),
            Y: randomInteger(1, 3)
        }
    }


    move(canvasW, canvasH){


        if(this.x+1 >= canvasW-this.radius || this.x-1 <= this.radius)
            this.velocity.X = -this.velocity.X;

        if(this.y+1 >= canvasH-this.radius || this.y-1 <= this.radius)
            this.velocity.Y = -this.velocity.Y;


        this.x += this.velocity.X;
        this.y += this.velocity.Y;
    }

    draw(ctx){

        ctx.strokeStyle = this.color;
        ctx.beginPath();
        ctx.arc(this.x, this.y, this.radius, 0, 2*Math.PI);
        ctx.stroke();

    }
}

Ответы [ 2 ]

1 голос
/ 08 апреля 2020

Отличная работа. У вас есть все основы игры / анимации, настроенные правильно. Вы правы - единственная реальная проблема заключается в функции collisions. Посмотрите, что там происходит.

Он выбирает круг, называя его first. Затем для каждого другого круга на экране

  • , если они пересекаются, он меняет оба цвета на цвет столкновения
  • , если они не пересекаются, он меняет оба цвета на значение по умолчанию

Теперь снова посмотрите на это и подумайте, что произойдет, если круг first проверяет столкновение на наличие кругов, которые фактически сталкиваются с ним в первую очередь. Тогда ПОСЛЕДНИЙ случай, когда l oop будет проверять его столкновение с другими кругами (которые не сталкиваются с ним), и изменит все их цвета обратно на значения по умолчанию.

В основном вам нужно переосмыслить свои логи c в том, как вы создаете эти две петли. Я бы предложил, например, добавить логический флаг (скажем, colliding), который можно проверить после завершения внутреннего l oop - возможно, даже добавив его в качестве свойства ваших Circle экземпляров. Поэтому, если first сталкивается с чем-то во внутренней l oop, тогда установите first.colliding = true. В вашей функции Circle draw () вы можете установить цвет, основываясь на этом свойстве.

К счастью, способ, которым были установлены эти циклы, фактически перекрыл эту другую ошибку. Вы не учитываете, когда круг second является тем же объектом, что и круг first (он всегда будет иметь нулевое расстояние от себя, поэтому всегда должен становиться зеленым ... но ошибка выше всегда возвращала его назад). Вы можете объяснить это, добавив такой чек во внутреннюю l oop:

if(first !== second) или if(i !== p) et c.

0 голосов
/ 09 апреля 2020

Большое спасибо !! FacePalm Как я мог пропустить это, хе-хе, я пошел для добавления свойства .colliding к кругу. Я изменил функции рисования и столкновений, но все равно ничего хорошего, теперь все круги нарисованы постоянно цветом столкновения. Это новый код:

function collisions(){


    let p, quitLooping;
    let first, second, d;

    for(let i = 0; i < arrBubbles.length; i++){

        first = arrBubbles[i];
        p = 0;
        quitLooping = false;

        while(p < arrBubbles.length && !quitLooping){

            second = arrBubbles[p];
            d = distance(first.x, first.y, second.x, second.y);            

            if( i !== p)     
                if(d <= first.radius + second.radius){
                    first.colliding = true;
                    quitLooping = true;
                }
                else 
                    first.colliding = false;

            p++;
        }
    }

}

draw(ctx){

        (this.colliding) ? ctx.strokeStyle = COLLISION_COLOR : ctx.strokeStyle = this.color;

        ctx.beginPath();
        ctx.arc(this.x, this.y, this.radius, 0, 2*Math.PI);
        ctx.stroke();

    }
...