如何解决弹性碰撞?

时间:2019-02-03 00:16:14

标签: javascript typescript game-physics

我一直在进行弹性碰撞演示​​,似乎无法完全正确。 circle1对象似乎从对象的一侧正确碰撞  circle2对象,但如果它们从另一侧碰撞,则通过circle2。

如何将这种弹性碰撞修改为更准确?

这是带有实时演示的CodePen的链接。

main.ts

class DemoCanvas {
    canvasWidth: number = 500;
    canvasHeight: number = 500;
    canvas: HTMLCanvasElement = document.createElement('canvas');
    constructor() {
        this.canvas.width = this.canvasWidth;
        this.canvas.height = this.canvasHeight;
        this.canvas.style.border = '1px solid black';
        this.canvas.style.position = 'absolute';
        this.canvas.style.left = '50%';
        this.canvas.style.top = '50%';
        this.canvas.style.transform = 'translate(-50%, -50%)';
        document.body.appendChild(this.canvas);
    }

    clear() {
        this.canvas.getContext('2d').clearRect(0, 0, this.canvas.width, this.canvas.height);
    }

    getContext(): CanvasRenderingContext2D {
        return this.canvas.getContext('2d');
    }

    getWidth(): number {
        return this.canvasWidth;
    }

    getHeight(): number {
        return this.canvasHeight;
    }

    getTop(): number {
        return this.canvas.getBoundingClientRect().top;
    }

    getRight(): number {
        return this.canvas.getBoundingClientRect().right;
    }

    getBottom(): number {
        return this.canvas.getBoundingClientRect().bottom;
    }    

    getLeft(): number {
        return this.canvas.getBoundingClientRect().left;
    }
}

class Circle {
    x: number;
    y: number;
    dx: number;
    dy: number;
    xVelocity: number;
    yVelocity: number;
    radius: number;
    color: string;
    canvas: DemoCanvas;
    context: CanvasRenderingContext2D;

    constructor(x: number, y: number, xVelocity: number, yVelocity: number, color: string, gameCanvas: DemoCanvas) {
        this.radius = 20;
        this.x = x;
        this.y = y;
        this.xVelocity = xVelocity;
        this.yVelocity = yVelocity;
        this.color = color;
        this.canvas = gameCanvas;
        this.context = this.canvas.getContext();
    }

    public draw(): void {
        this.context.fillStyle = this.color;
        this.context.beginPath();
        this.context.arc(this.x, this.y, this.radius, 0, 2 * Math.PI);
        this.context.fill();
    }

    public move(): void {
        this.x += this.xVelocity;
        this.y += this.yVelocity;
    }

    checkWallCollision(gameCanvas: DemoCanvas): void {
        let top = 0;
        let right = 500;
        let bottom = 500;
        let left = 0;

        if(this.y < top + this.radius) {
            this.y = top + this.radius;
            this.yVelocity *= -1;
        }

        if(this.x > right - this.radius) {
            this.x = right - this.radius;
            this.xVelocity *= -1;
        }

        if(this.y > bottom - this.radius) {
            this.y = bottom - this.radius;
            this.yVelocity *= -1;
        }

        if(this.x < left + this.radius) {
            this.x = left + this.radius;
            this.xVelocity *= -1;
        }
    }
}

let demoCanvas = new DemoCanvas();
let circle1: Circle = new Circle(250, 250, 5, 5, "#F77", demoCanvas);
let circle2: Circle = new Circle(250, 540, 5, 5, "#7FF", demoCanvas);


function detectCollisions():void {

    if (circle1.x + circle1.radius + circle2.radius > circle2.x 
        && circle1.x < circle2.x + circle1.radius + circle2.radius
        && circle1.y + circle1.radius + circle2.radius > circle2.y 
        && circle1.y < circle2.y + circle1.radius + circle2.radius) {
            if (distanceTo() < circle1.radius + circle2.radius) {
                calculateNewVelocities();
            }
    }
}

function distanceTo():Number {
    var distance = Math.sqrt(((circle1.x - circle2.x) * (circle1.x - circle2.x)) + ((circle1.y - circle2.y) * (circle1.y - circle2.y)));
    if (distance < 0) { 
        distance = distance * -1; 
    }
    return distance;
}

function calculateNewVelocities():void {
    var mass1 = circle1.radius;
    var mass2 = circle2.radius;
    var velX1 = circle1.xVelocity;
    var velX2 = circle2.xVelocity;
    var velY1 = circle1.yVelocity;
    var velY2 = circle2.yVelocity;

    var newVelX1 = (velX1 * (mass1 - mass2) + (2 * mass2 * velX2)) / (mass1 + mass2);
    var newVelY1 = (velY1 * (mass1 - mass2) + (2 * mass2 * velY2)) / (mass1 + mass2);

    circle1.xVelocity = newVelX1;
    circle1.yVelocity = newVelY1;

    circle1.x = circle1.x + newVelX1;
    circle1.y = circle1.y + newVelY1;
}

addEventListener('mousemove', function(e) {
    let mouseX = e.clientX - demoCanvas.getLeft();
    let mouseY = e.clientY - demoCanvas.getTop();
    circle2.x = mouseX;
    circle2.y = mouseY;
});

function loop() {
    demoCanvas.clear();
    circle1.draw();
    circle2.draw();
    circle1.move();
    circle1.checkWallCollision(demoCanvas);
    circle2.checkWallCollision(demoCanvas);
    detectCollisions();
    requestAnimationFrame(loop);
}
requestAnimationFrame(loop);

1 个答案:

答案 0 :(得分:1)

嗯,我不确定当circle1circle2碰撞时,您的公式在哪里更新circle2的速度,但是the correct formula取决于它的相对位置两个圆(不仅是它们之间的距离,还有接触角)。

如果我们假设circle1将是静止的(或者太大,以至于calculateNewVelocities()不会影响其运动),那么 var xd = circle1.x - circle2.x; // x displacement between circles var yd = circle1.y - circle2.y; // y displacement between circles var dsq = xd*xd + yd*yd; // square of distance between circles var DV = 2*(velX1*xd + velY1*yd)/dsq; // velocity change factor // EDIT // fix to prevent orbiting... if circle1 is already rebounding, // leave it alone if (DV > 0) { DV=0; } // velocity change is in the opposite direction of // the displacement between circles var newVelX1 = velX1 - DV * xd; var newVelY1 = velY1 - DV * yd; 中的代码应如下所示:< / p>

circle1

<击>这应该给你的东西隐约合理时,碰撞来自外部。如果您发现圆圈重叠,可能会导致circle2绕行circle1或类似的疯狂动作,因此您可能需要进行调整以防止此类情况发生。

编辑:我认为我前面提到的“绕动”问题是由于碰撞后圆圈继续重叠而导致快速连续触发多次碰撞的结果。解决的方法是,如果circle2的速度已经远离circle1的中心,则不要对其进行修改。这样,只有第一个冲突才有效,随后的检测到的冲突将被忽略,直到circle2再次朝着Ionic: ionic (Ionic CLI) : 4.7.0 (C:\Users\HOLLY SPIRIT\AppData\Roaming\npm\node_modules\ionic) Ionic Framework : ionic-angular 3.9.2 @ionic/app-scripts : 3.2.1 Cordova: cordova (Cordova CLI) : 7.1.0 Cordova Platforms : android 6.4.0 Cordova Plugins : cordova-plugin-ionic-keyboard 2.1.3, cordova-plugin-ionic-webview 1.2.1, (and 22 other plugins) System: NodeJS : v10.15.0 (C:\Program Files\nodejs\node.exe) npm : 6.4.1 OS : Windows 10 移动。

无论如何,这只是一个建议。希望您找到想要的东西。祝你好运。