HTML5 Canvas - 如何沿某个方向移动对象?

Had*_*hes 1 html javascript 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);
Run Code Online (Sandbox Code Playgroud)
<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>
Run Code Online (Sandbox Code Playgroud)

Bli*_*n67 5

牛顿和开普勒的发条宇宙

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

重力、力、质量和距离。

要获得更准确的重力模型,您可以使用牛顿发现的万有引力定律。F = G * (m1*m2)/(r*r)其中F是力,G是引力常数(对于模拟来说,它只是一个比例因子)m1m2是每个物体的质量,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 的形式查看这两个公式,我们可以看到我们施加力的物体的质量被抵消了= G * (m2)/(r*r)。现在我们可以计算重力加速度。加速度只是速度随时间的变化,我们知道这种变化是朝着另一个物体的方向的。因此,我们得到物体之间的向量(o1o2对于物体 1 和 2)dx = o2.x-o1.xdy = o2.y-o1.y然后找到该向量的长度(即重力公式中的rdist = Math.sqrt(dx* dx + dy * dy) ) 。然后我们通过除以向量的长度来标准化向量(使其长度= 1)。dx /= dist, dy /= dist. 计算 a(加速度)并将对象之间的归一化向量乘以a,然后将其添加到对象的速度即可。完美的牛顿发条轨道(对于两个物体而言)。

用开普勒清理。

所有这些数学都很好,但它并不能带来很好的模拟。当数学完成后,两个物体都开始移动,如果起始速度不平衡,那么整个系统将慢慢地在画布上漂移。

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

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

使用经过修改以适应牛顿数学的开普勒第二定律,我们得到一个公式,该公式将给出近似值(这是一个近似值,因为实际计算涉及无限级数,我懒得把它写出来。)轨道速度v = sqrt(G *(m1 + m2)/r)。它看起来类似于牛顿引力定律,但其中质量是相加而不是相乘,并且距离不是平方。

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

我创建了一个设置函数,为太阳和行星设置正确的轨道。但G (万有引力常数)的值可能太大了。为了获得更好的值,我缩放G(通过拼凑数学),以便太阳的轨道速度接近所需的理想速度sunV(每帧像素) 为了使整个模拟运行得更快,增加该值

由于我已将代码设置为具有两个以上的物体,因此只有当每个物体比下一个物体大得多时,起始速度的计算才会起作用。我在行星上添加了一个月亮(你需要取消注释才能看到),但它太大了,而且它的起始速度有点太低了。它被地球拉(重力弹射)到更高的轨道。但这也将地球拉入较低的轨道,使其轨道更加偏心

注意:毕竟我发现有些事情不太正确,系统中仍然存在一点点漂移。由于时间不够,我刚刚修复了太阳位置以将系统保留在画布上。

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);
Run Code Online (Sandbox Code Playgroud)
<canvas id='canvas'></canvas>
Run Code Online (Sandbox Code Playgroud)