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

标签 javascript html 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>

最佳答案

牛顿和开普勒的发条宇宙

牛顿在计算轨道数学时,注意到了一些促使他创造“发条宇宙”的术语。在两个物体的模拟中,两个物体的轨道均精确重复。这意味着两个物体将同时以它们在最后一个轨道上的完全相同的速度处于完全相同的位置,而不会进动。

重力,力,质量和距离。

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

球体的质量

我们给恒星和行星一些质量。假设在计算机中1像素的立方等于1单位质量。因此,半径为R的球的质量为4/3 * R3 * PI。

力,质量和加速度

力始终沿物体之间的线施加,称为加速度。

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

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

用开普勒清理。

所有这些数学都很好,但是并不能很好地模拟。完成数学运算后,两个对象都开始移动,并且如果起始速度不平衡,则整个系统将缓慢移动画布。

我们可以只相对于其中一个物体进行显示,这样可以消除系统中的任何漂移,但是仍然存在轨道获取的问题。如果一个物体快速移动,它将飞走,再也不会回来。如果速度太慢,则它会非常靠近另一个对象的中心。如果发生这种情况,速度的变化将接近无穷大,这是某些计算机不能很好地处理的。

因此,要获得良好的圆轨道,我们需要数学的最后一点。

使用开普勒第二定律经过修改以适合牛顿数学,我们得到一个公式,该公式将给出近似值(由于实际计算涉及无穷级数,因此我不介意将其写出来。)轨道速度v = sqrt(G *(m1 + m2)/ r)。它看起来类似于牛顿的重力定律,但是在这种情况下,质量之和不相乘,距离也不平方。

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

我创建了一个设置函数,可以为太阳和行星设置正确的轨道。但是,G(引力常数)的值可能很大。为了获得更好的值,我对G进行了缩放(通过kludge数学),以使太阳的轨道速度接近理想的理想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);

<canvas id='canvas'></canvas>

关于javascript - HTML5 Canvas-如何在一个方向上移动对象?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40906725/

相关文章:

javascript - 如何将此退出弹出窗口从 iframe 转换为重定向?

javascript - 清除 Canvas 后不绘制图像(html5 Canvas )

javascript - 使用 React 将轨道添加/删除到播放列表

javascript - 使用 javascript 将远程页面加载到 DOM 中

html - 修复了菜单和 div 的问题

javascript - 更改 Canvas 上图像的窗口级别(窗口中心)和窗口宽度

javascript - 当 Canvas 宽度改变时, Canvas 线宽也会改变

javascript - jQuery-toggleClass + slipToggle 未按预期工作

javascript - 将下拉列表转换为带有颜色的选择框并触发下拉 Action

html - CSS:如何删除链接中由换行符引起的额外空白?