HTML5 Canvas - 如何在一个方向上移动对象?

时间:2016-12-01 09:21:51

标签: javascript html5 canvas

我有一个想要围绕恒星运行的物体。我设法使物体朝向恒星移动,但现在我也需要设置横向运动。

显然,这并不像调整X一样容易,因为当它绕着星星的一侧移动时我也必须调整Y.我想知道如何利用一些数学来计算出当物体围绕恒星移动时我需要多少调整X和Y.

到目前为止,这是我的代码:



var c = document.getElementById('canvas');
var ctx = c.getContext('2d');

c.width = window.innerWidth;
c.height = window.innerHeight;

var star = {
    x: c.width / 2,
    y: c.height / 2,
    r: 100,
    g: 2,
    draw: function()
    {
        ctx.beginPath();
        ctx.arc(this.x, this.y, this.r, 0, 2*Math.PI);
        ctx.fillStyle = 'orange';
        ctx.fill();
        ctx.closePath();
    }
};

var node = {
    x: c.width / 2,
    y: 100,
    r: 20,
    draw: function()
    {
        ctx.beginPath();
        ctx.arc(this.x, this.y, this.r, 0, 2*Math.PI);
        ctx.fillStyle = 'blue';
        ctx.fill();
        ctx.closePath();
    }
};


//GAME LOOP
function gameLoop()
{
    update();
    render();
    
    window.requestAnimationFrame(gameLoop);
}

function update()
{
    //Move towards star
    var dx = star.x - node.x;
    var dy = star.y - node.y;
    var angle = Math.atan2(dy, dx);
    
    node.x += Math.cos(angle);
    node.y += Math.sin(angle);
    
    //Lateral movement
    node.x += 2;
}

function render()
{
    ctx.clearRect(0, 0, c.width, c.height);
    star.draw();
    node.draw();
}

window.requestAnimationFrame(gameLoop);

<html>
    <head>
        <style>
        body
        {
            margin: 0;
            padding: 0;
            overflow: hidden;
        }
        
        #canvas
        {
            background-color: #001319;
        }
        </style>
    </head>
    
    <body>
        <canvas id="canvas">
        </canvas>
        <script src="Orbit.js"></script>
    </body>
</html>
&#13;
&#13;
&#13;

2 个答案:

答案 0 :(得分:4)

牛顿和开普勒的发条宇宙

当牛顿算出计算轨道的数学时,他注意到了一些东西,这些东西促使他投入了“发条宇宙”这个词。在两体模拟中,两个物体的轨道路径精确重复。这意味着两个物体将同时处于完全相同的位置,速度与最后一个轨道完全相同,不会有进动。

重力,力,质量和距离。

对于更精确的引力模型,您可以使用牛顿发现的引力定律。 F = G *(m1 * m2)/(r * r)其中 F 是力, G 是引力常数(对于模拟)它只是一个比例因子) m1 m2 是每个身体的质量, r 是身体之间的距离。

球体质量

我们给星和星球一些质量。让我们说在计算机中1个像素立方体等于1个单位质量。因此,半径 R 的球体质量 4/3 * R 3 * PI

力,质量和加速度

力总是沿着身体之间的线施加,称为加速度。

当一个力被施加到一个物体时,我们使用另一个牛顿发现的定律, F = ma ,其中 a 是加速度。我们有 F (强制)和 m (质量),所以现在我们所需要的只是 a 。重新排列 F = ma 以获得 a = f / m

如果我们用 a (加速度) a =(G *(m1 * m2)/(r * r))/ m1 来看两个公式我们可以看出我们施加的物体的质量被抵消了 a = G *(m2)/(r * r)。现在我们可以计算重力引起的加速度。加速度只是速度随时间的变化,我们知道这种变化是在另一个身体的方向上。因此,我们得到了主体之间的向量(o1o2对象1和2)dx = o2.x-o1.xdy = o2.y-o1.y然后找到该向量的长度(这是 r 在引力公式中)dist = Math.sqrt(dx* dx + dy * dy)。然后我们通过除以它的长度来标准化矢量(使其长度= 1)。 dx /= distdy /= dist。计算a(加速度)并将对象之间的归一化向量乘以 a ,然后将其添加到对象的速度即可。完美的牛顿发条轨道(对于两个实体)。

用开普勒清理。

所有这些数学都很好,但它不能很好地模拟。当数学完成时,两个对象都开始移动,如果起始速度不平衡,那么整个系统将慢慢漂移画布。

我们可以只显示相对于其中一个物体的显示,这样可以消除系统中的任何漂移,但我们仍然有获得轨道的问题。如果一个物体移动到快速,它会飞走而永远不会回来。如果它太慢,那么它将非常接近另一个物体的中心点。如果发生这种情况,速度的变化将会无穷无尽,计算机在处理方面并不擅长。

因此,为了得到漂亮的圆形轨道,我们需要最后一点数学。

使用Kepler的第二定律进行修改以适应牛顿数学,我们得到一个公式,给出近似值(这是一个近似值,因为实际计算涉及一个无限系列,我不能打扰写作那个。)轨道速度 v = sqrt(G *(m1 + m2)/ r)。它看起来类似于牛顿的引力定律,但在这种情况下,质量总和不会成倍增加,并且距离不是平方的。

所以我们用它来计算两个物体的切向速度,使它们接近圆形轨道。重要的是每个物体彼此相反的方向。

我创建了一个设置功能,为太阳和行星设置正确的轨道。但 G (引力常数)的值可能很大。为了获得更好的价值,我缩放 G (通过kludge数学运算),以便太阳的轨道速度接近理想的理想sunV(每帧像素数)。 sim运行得更快增加这个值

由于我已经设置了具有两个以上物体的代码,因此只有当每个物体的质量明显大于下一个物体时,才能计算起始速度。我已经为这个星球添加了一个月亮(你需要不要发表评论),但它太大了,它的起始速度有点太低了。它被地球拉到(重力吊索射击)到更高的轨道。但这也将地球拉入较低的轨道,使其轨道更加偏心

  

注意毕竟我发现某些东西不太正确,系统中仍有一点点漂移。由于我没有时间,我刚刚修复了太阳位置以将系统保持在画布上。

&#13;
&#13;
var c = document.getElementById('canvas');
c.width = innerWidth;
c.height = innerHeight;
var ctx = c.getContext('2d');
const STAR_RADIUS = 100;
const PLANET_RADIUS = 10;
const MOON_RADIUS = 4.5;
var G = 1; // gravitational constant is not so constant as need to 
           // scale it to find best value for the system. 
           // for that I will scale it so that the suns orbital speed around the 
           // planet is approx 0.1 pixels per frame
const sunV = 0.1; // the sun's orbital desired speed. THis is used to tune G               
const DRAW = function () {
    ctx.beginPath();
    ctx.arc(this.x, this.y, this.r, 0, 2*Math.PI);
    ctx.fillStyle = this.col;
    ctx.fill();
    ctx.closePath();
}
var star = {
    x: c.width / 2,
    y: c.height / 2,
    vx : 0,
    vy : 0,
    r: STAR_RADIUS,
    mass : (4/3) * Math.pow(STAR_RADIUS,3) * Math.PI,
    col : 'orange',
    draw : DRAW,
};
// kludge to fix drift
const sunStartX = star.x;
const sunStartY = star.y;

var node = {
    x: c.width / 2 - STAR_RADIUS - PLANET_RADIUS * 5,
    y: c.height / 2,
    r: PLANET_RADIUS,
    mass : (4/3) * Math.pow(PLANET_RADIUS,3) * Math.PI,
    col : "blue",
    draw : DRAW,
    vx: -1,
    vy: 0,
};

var moon = {
    x: c.width / 2- STAR_RADIUS - PLANET_RADIUS * 7 ,
    y: c.height / 2,
    r: MOON_RADIUS,
    mass : (4/3) * Math.pow(PLANET_RADIUS,3) * Math.PI,
    col : "#888",
    draw : DRAW,
    vx: -1,
    vy: 0,
};
const objects = [star, node];//, moon];
function setup(){
    var dist,dx,dy,o1,o2,v,c,dv;
    o1 = objects[0];
    o1.vx = 0;
    o1.vy = 0;
    for(var j = 0; j < objects.length; j ++){
        if(j !== 0){ // object can not apply force to them selves
            o2 = objects[j];
            dx = o2.x - o1.x;
            dy = o2.y - o1.y;
            dist = Math.sqrt(dx * dx + dy * dy);
            dx /= dist;  
            dy /= dist;
            // Find value og G
            if(j === 1){ // is this not  sun
                v = Math.sqrt(G  * ( o2.mass ) / dist);
                dv = sunV - v;
                while(Math.abs(dv) > sunV * sunV){
                    if(dv < 0){  // sun too fast
                        G *= 0.75;
                    }else{
                        G += G * 0.1;
                    }
                    v = Math.sqrt(G  * ( o2.mass ) / dist);
                    dv = sunV - v;
                }
            }
            
            v = Math.sqrt(G  * ( o2.mass ) / dist);
            o1.vx -= v * dy; // along the tangent
            o1.vy += v * dx;
        }
    }
    for(var i = 1; i < objects.length; i ++){
        o1 = objects[i];
        o1.vx = 0;
        o1.vy = 0;
        for(var j = 0; j <objects.length; j ++){
            if(j !== i){
                o2 = objects[j];
                dx = o2.x - o1.x;
                dy = o2.y - o1.y;
                dist = Math.sqrt(dx * dx + dy * dy);
                dx /= dist;  
                dy /= dist;                    
                v = Math.sqrt(G  * ( o2.mass ) / dist);
                o1.vx += v * dy; // along the tangent
                o1.vy -= v * dx;
            }
        }
    }
}


//GAME LOOP
function gameLoop(){
    update();
    render();
    requestAnimationFrame(gameLoop);
}

// every object exerts a force on every other object
function update(){
    var dist,dx,dy,o1,o2,a;
    // find force of acceleration each object applies to each object
    for(var i = 0; i < objects.length; i ++){
        o1 = objects[i];
        for(var j = 0; j < objects.length; j ++){
            if(i !== j){ // object can not apply force to them selves
                o2 = objects[j];
                dx = o2.x - o1.x;
                dy = o2.y - o1.y;
                dist = Math.sqrt(dx * dx + dy * dy);
                dx /= dist;  // normalise the line between the objects (makes the vector 1 unit long)
                dy /= dist;
                // get force
                a = (G  * o2.mass ) / (dist * dist);
                o1.vx += a * dx;
                o1.vy += a * dy;
            }
        }
    }
    // once all the forces have been found update objects positions
    for(var i = 0; i < objects.length; i ++){
        o1 = objects[i];
        o1.x += o1.vx;
        o1.y += o1.vy;
    }
            
}

function render(){
    ctx.clearRect(0, 0, c.width, c.height);
  // kludge to fix drift
    var offsetX =  objects[0].x - sunStartX;
    var offsetY =  objects[0].y - sunStartY;
    ctx.setTransform(1,0,0,1,-offsetX,-offsetY);

    for(var i = 0; i < objects.length; i ++){
        objects[i].draw();
    }
    ctx.setTransform(1,0,0,1,0,0);
}
setup();
requestAnimationFrame(gameLoop);
&#13;
<canvas id='canvas'></canvas>
&#13;
&#13;
&#13;

答案 1 :(得分:0)

好的,我已经回答了我自己的问题。

不是让星的引力直接影响x和y坐标,我有一个对象的vx和vy,我使引力影响该值,然后只需用vx调整x和y并在每次更新时加入。

以下是代码:

&#13;
&#13;
btnSubmit_Click
&#13;
touchBar
&#13;
&#13;
&#13;