Canvas/JS:从具有倾斜坡度的碰撞中计算对象的新速度矢量? [英] Canvas/JS: Calculate New Velocity Vector of Object from Collision with Slanted Slope?

查看:37
本文介绍了Canvas/JS:从具有倾斜坡度的碰撞中计算对象的新速度矢量?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

好的,所以我正在JS/Canvas上进行弹球游戏,我想知道如何处理脚蹼和球之间的碰撞.

Alright, so I'm working on a pinball game on JS/Canvas and I'm wondering how to handle the collision between the flipper and the ball.

我可以使鳍状肢击球,但是我对于如何通过变化的鳍状肢位置(角度)改变球的速度方向感到困惑.这是我可以从脚蹼和球中使用的信息:

I can get the flipper to hit the ball, but I'm confused about how to change the ball's velocity direction with varied flipper positions (angles). Here's the information that I could use from both a flipper and ball:

this.ballPosX = ballPosX;
this.ballPosY = ballPosY;
this.ballVelX = 0;
this.ballVelY = 0;

// thruster is a line shape with a variable end Y position
ctx.moveTo(125, 480);
ctx.lineTo(215, this.posY);

我没有计算脚蹼的速度.我只想知道如何根据直线的斜率来改变球速度矢量的方向.谢谢!

I'm not calculating the velocity of the flipper btw. I just want to know how to change the ball velocity vector director with regards to the slope of the line. Thanks!

推荐答案

反射向量

作为反射向量的基本反弹很容易.

Reflected vector

The basic bounce as a reflected vector is simple to do.

给一行

const line = {  // numbers are arbitrary
   p1 : { x : 0, y : 0 },
   p2 : { x : 0, y : 0 }
}

和一个球

const ball = {
   pos : { x : 0, y: 0},
   radius : 0,
   delta : { x : 0, y : 0},  // movement as vector
}

首先将行转换为更易于管理的形式

First convert the line into a more manageable form

 line.vec = {};  // get vector for the line
 line.vec.x = line.p2.x - line.p1.x;
 line.vec.y = line.p2.y - line.p1.y;
 // get line length
 line.len = Math.sqrt(line.vec.x * line.vec.x + line.vec.y * line.vec.y);
 // the normalised vector (1 unit long)
 line.vnorm = {};
 line.vnorm.x = line.vec.x / line.len;
 line.vnorm.y = line.vec.y / line.len;

也可以归一化球的变化量

Also normalise the ball delta

ball.speed = Math.sqrt(ball.delta.x * ball.delta.x + ball.delta.y * ball.delta.y);
ball.dnorm = {};
ball.dnorm.x = ball.delta.x / ball.speed;
ball.dnorm.y = ball.delta.y / ball.speed;

现在反射使用矢量

// ball velocity vector line normal dot product times 2
var dd = (ball.dnorm.x * line.vnorm.x + ball.dnorm.y * line.vnorm.y) * 2
ball.ref = {}; // the balls reflected delta
ball.ref.x = line.vnorm.x * dd - ball.dnorm.x;
ball.ref.y = line.vnorm.y * dd - ball.dnorm.y;

只需要对反射向量进行归一化,然后乘以球速减去击中线时的任何能量损失即可.

The reflected vector just needs to be normalised and then multiplied by the ball speed minus any energy loss when hitting the line.

var len = Math.sqrt(ball.ref.x * ball.ref.x + ball.ref.y * ball.ref.y);
ball.delta.x = (ball.ref.x / len) * ball.speed; // I have kept the same speed.
ball.delta.y = (ball.ref.y / len) * ball.speed;

演示

不是100%,我的数学正确,我认为最好通过演示进行检查.我忘了这变得多么复杂.

Demo

Not being 100% i got the math correct i thought it best to check with a demo. I forgot how complex this gets.

演示显示了运动线,但是运动没有传递到球上.

Demo shows moving lines but that movement is not transferred to the balls.

var W,H; // canvas width and height
// get the canvas context
const ctx = canvas.getContext("2d");
const gravity = 0.19;
function vec(x,y){return {x,y}}

const line = {  
   p1 : { x : 0, y : 0 },
   p2 : { x : 0, y : 0 },
   vnorm : { x : 0, y : 0 }, // normalied vector
   vec : { x : 0, y : 0 }, // line as a vector
   len : 0,   
   update(){
     this.vec.x = this.p2.x - this.p1.x;
     this.vec.y = this.p2.y - this.p1.y;
     this.len = Math.sqrt(this.vec.x * this.vec.x + this.vec.y * this.vec.y);
     this.vnorm.x = this.vec.x / this.len;
     this.vnorm.y = this.vec.y / this.len;
   },
   draw(){
      ctx.beginPath();
      ctx.moveTo(this.p1.x,this.p1.y);
      ctx.lineTo(this.p2.x,this.p2.y);
      ctx.stroke();
   },
   testBall(ball){  // line must be updated befor this call
       // line is one sided and ball can only approch from the right
       // find line radius distance from line
       var x = this.p1.x + this.vnorm.y * ball.radius;
       var y = this.p1.y - this.vnorm.x * ball.radius;
       x = ball.pos.x - x;
       y = ball.pos.y - y;
       // get cross product to see if ball hits line
       var c = x * this.vec.y - y * this.vec.x;
       if(c <= 0){ // ball has hit the line

            // get perpendicular line away from line
            var ox = this.vnorm.y * ball.radius
            var oy = -this.vnorm.x * ball.radius
            // get relative ball pos
            var px = ball.pos.x - (this.p1.x + ox);
            var py = ball.pos.y - (this.p1.y + oy);
            // find ball position that contacts the line
            var ld = (px * this.vec.x + py * this.vec.y)/(this.len * this.len);
            ball.pos.x  = this.vec.x * ld + (this.p1.x+ox);
            ball.pos.y = this.vec.y * ld + (this.p1.y+oy);            
            // find the reflection delta (bounce direction)
            var dd = (ball.dnorm.x * this.vnorm.x + ball.dnorm.y * this.vnorm.y) * 2;
            ball.delta.x = this.vnorm.x * dd - ball.dnorm.x;
            ball.delta.y = this.vnorm.y * dd - ball.dnorm.y;
            
            // the ball has lost some speed (should not have but this is a fix)
            
           var m = Math.sqrt(ball.delta.x * ball.delta.x + ball.delta.y * ball.delta.y);
            ball.delta.x = (ball.delta.x / m) * ball.speed;
            ball.delta.y = (ball.delta.y / m) * ball.speed;
            
            ball.updateContact();
            ball.col = "red";
       
       }
   }
}



// create a ball
function createLine(x,y,x1,y1){
    var l = Object.assign({},line,{
       p1 : vec(x,y),
       p2 : vec(x1,y1),
       vec : vec(0,0),
       vnorm : vec(0,0),
    
    });
    l.update();
    return l;
}
const lines = {
   items : [],
   add(line){ lines.items.push(line); return line },
   update(){
        var i;
        for(i = 0; i < lines.items.length; i++){
            lines.items[i].update();
        }
        return lines;
    },
   draw(){
        var i;
        for(i = 0; i < lines.items.length; i++){
            lines.items[i].draw();
        }
        return lines;
    },
    testBall(ball){
        var i;
        for(i = 0; i < lines.items.length; i++){
            lines.items[i].testBall(ball);
        }
        return lines;
    }
    

};

var ball = {
   pos : { x : 0, y: 0},
   radius : 0,
   speed : 0,
   col : "black",
   delta : { x : 0, y : 0},  // movement as vector
   dnorm : { x : 0, y: 0},  // normalised delta
   updateContact(){ // update after ball hits wall
      this.speed = Math.sqrt(this.delta.x * this.delta.x + this.delta.y * this.delta.y);
      this.dnorm.x = this.delta.x / this.speed;
      this.dnorm.y = this.delta.y / this.speed;   
   },
   update(){
      this.col = "black";
      this.delta.y += gravity;
      this.pos.x += this.delta.x;
      this.pos.y += this.delta.y;
      this.speed = Math.sqrt(this.delta.x * this.delta.x + this.delta.y * this.delta.y);
      this.dnorm.x = this.delta.x / this.speed;
      this.dnorm.y = this.delta.y / this.speed;   
   },
   draw(){
      ctx.strokeStyle = this.col;
      ctx.beginPath();
      ctx.arc(this.pos.x,this.pos.y,this.radius,0,Math.PI*2);
      ctx.stroke();
   },
}
// create a ball
function createBall(x,y,r){
    var b = Object.assign({},ball,{
      pos : vec(x,y),
      radius : r,
      delta : vec(0,0),
      dnorm : vec(0,0),
    });
    return b;
}

const balls = {
    items : [],
    add(b){
        balls.items.push(b);
    },
    update(){
        var i;
        for(i = 0; i < balls.items.length; i++){
            balls.items[i].update();
            lines.testBall(balls.items[i]);
            if(balls.items[i].pos.y - balls.items[i].radius > canvas.height ||
              balls.items[i].pos.x < -50 || balls.items[i].pos.x - 50  > W){
                balls.items.splice(i--,1);
            }
        }
        return balls;
    },
    draw(){
        var i;
        for(i = 0; i < balls.items.length; i++){
            balls.items[i].draw();
        }
        return balls;
    }
}
    




const l1 = lines.add(createLine(0,0,100,100)); 
const l2 = lines.add(createLine(0,0,100,100)); 
const mouse = { x : 0, y : 0};
canvas.addEventListener("mousemove",(e)=>{
   mouse.x = e.pageX;
   mouse.y = e.pageY;
})


function mainLoop(time){
    // resize if needed
    if(canvas.width !== innerWidth || canvas.height !== innerHeight){ // resize canvas if window size has changed
        W = canvas.width = innerWidth;
        H = canvas.height = innerHeight;
    }
    // clear canvas 
    ctx.setTransform(1,0,0,1,0,0); // set default transform
    ctx.clearRect(0,0,W,H); // clear the canvas
    if(balls.items.length < 10){
        balls.add(createBall(Math.random() * W , Math.random() * 30, Math.random() * 20 + 10));

    }

    time /= 5;
    l1.p1.x = 0;
    l1.p2.x = W ;
    l2.p1.x = 0;
    l2.p2.x = W ;
    l1.p1.y = Math.sin(time / 1000) * H * 0.4 + H * 0.6;
    l1.p2.y = Math.cos(time / 1000) * H * 0.4 + H * 0.6;
    l2.p1.y = Math.sin(time / 500) * H * 0.4 + H * 0.6;
    l2.p2.y = Math.cos(time / 500) * H * 0.4 + H * 0.6;
    lines.update().draw(); 
    balls.update().draw();    


    // get next animation loop
    requestAnimationFrame(mainLoop);
}
requestAnimationFrame(mainLoop);

canvas {
    position : absolute;
    top : 0px;
    left : 0px;
    z-index : -10;
}

<canvas id=canvas></canvas>

这篇关于Canvas/JS:从具有倾斜坡度的碰撞中计算对象的新速度矢量?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆