2017-06-01 40 views
0

好的,所以我正在JS/Canvas上進行一個彈球遊戲,我想知道如何處理鰭狀肢和球之間的碰撞。帆布/ JS:計算與傾斜斜坡碰撞的物體的新速度矢量?

我可以讓腳蹼擊球,但我很困惑如何改變球的速度方向與不同的腳蹼位置(角度)。下面是我可以從兩個鰭狀肢和球使用的信息:我沒有計算擋板的速度BTW

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); 

。我只是想知道如何改變球速度矢量導向關於線的斜率。謝謝!

回答

1

反射矢量

基本彈跳作爲反射向量是簡單的事情。

給定線

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 
} 

首先轉化行成一個更易於管理的形式

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; 

而且正常化球三角洲

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; 

反射矢量僅需要進行歸一化,然後通過球的速度減去任何能量損失擊中線時相乘。

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%我得到了正確的數學我認爲最好用的演示來檢查。我忘了它有多複雜。

演示顯示移動的線條,但該移動不會傳遞到球。

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>