【问题标题】:Objects drifting slowly to the right in gravity simulation重力模拟中物体缓慢向右漂移
【发布时间】:2014-07-14 15:58:12
【问题描述】:

所以我在 JSFiddle 中构建一个小重力模拟只是为了好玩。到目前为止,这是我的脚本:

var elements = [
    {
        "id": "earth",
        "mass": 30,
        "left": 30,
        "top": 30,
        "hSpeed": 300,
        "vSpeed": -300
    }, {
        "id": "sun",
        "mass": 5000,
        "left": 50,
        "top": 50,
        "hSpeed": 0,
        "vSpeed": 0
    }];

var intervalTime = 10;

var intervalID;

var getDistance = function(x1, y1, x2, y2) {

    // h^2 = x^2 + y^2

    var xD = x1 - x2;
    var yD = y1 - y2;
    var hSq = xD * xD + yD * yD;

    return Math.sqrt(hSq);
};

var getXPortion = function(val, x1, y1, x2, y2) {

    var hD = getDistance(x1, y1, x2, y2);
    var xD = x2 - x1;

    if ((hD == 0) || (xD == 0) || (val == 0)) {
        return 0;
    }

    return val / hD * xD;
};

var getYPortion = function(val, x1, y1, x2, y2) {

    var hD = getDistance(x1, y1, x2, y2);
    var yD = y2 - y1;

    if ((hD == 0) || (yD == 0) || (val == 0)) {
        return 0;
    }

    return val / hD * yD;
};

var animateWorld = function() {

    // Calculate the change in speed for each element
    // based on the gravity of all the other elements
    var i = 0;
    var j = 0;
    var dist = 0;
    var mass = 0;
    var grav = 0;
    var change = 0;
    var chX = 0;
    var chY = 0;
    for (i in elements) {
        for (j in elements) {
            if (i != j) {
                dist = getDistance(
                    elements[i].left,
                    elements[i].top,
                    elements[j].left,
                    elements[j].top
                );
                mass = elements[i].mass * elements[j].mass;
                grav = dist == 0 ? 0 : mass / (dist * dist);
                change = grav / elements[i].mass;
                elements[i].hSpeed += getXPortion(
                    change,
                    elements[i].left,
                    elements[i].top,
                    elements[j].left,
                    elements[j].top
                );
                elements[i].vSpeed += getYPortion(
                    change,
                    elements[i].left,
                    elements[i].top,
                    elements[j].left,
                    elements[j].top
                );
            }
        }
    }

    // Calculate the new position for each element
    // based on the speed and move each element to it's new position
    for (i in elements) {
        elements[i].left += elements[i].hSpeed / 1000;
        elements[i].top += elements[i].vSpeed / 1000;
        $('#' + elements[i].id).css('left', elements[i].left + '%');
        $('#' + elements[i].id).css('top', elements[i].top + '%');
    }

};

$(document).ready(function() {
    intervalID = window.setInterval(animateWorld, intervalTime);
});

我允许所有对象根据它们各自的质量和距离相互作用。问题是所有物体都在缓慢地向右漂移。由于这是一个封闭的系统,我希望只有当另一个基于质量同样向左移动时,一个才会向右移动,所以它们应该不可能随着时间的推移都向同一个方向漂移。我认为以下情况之一正在发生,但我不确定是哪一种:

  1. 数学中使用的浮点数会导致轻微的舍入问题(不太可能)
  2. 我首先计算每个物体对另一个物体的影响,然后移动它们,这一事实造成了一些偏差。我是这种情况,我该如何解决?

有什么想法吗?有没有更简单的方法来做到这一点?

【问题讨论】:

  • 乍一看,好像太阳只向右飘,不向左飘,也许这有助于调试过程
  • 你的地球似乎对你的太阳有相当大的影响。在开始时将地球的位置更改为右上角,并更改速度以将其向左下角发射,使其反向运行,太阳开始向另一方向移动
  • @AeroX。我不是想建立一个现实的规模,只是对重力效应的真实模拟,不管基本假设是什么
  • @neelsg 我对物理学一无所知,但只是注意到我能够通过更改起始参数使太阳向相反方向漂移。不知道它是否会给你其他地方开始调试
  • @AeroX。感谢您的建议

标签: javascript animation math simulation


【解决方案1】:

我认为这是你的第二个可能性。

您应该避免循环遍历对象并一次施加一个力。这意味着模拟的行为会根据数组中对象的顺序而有所不同,这是不应该发生的。

我建议为每个对象添加更多字段,包括“hAcceleration”和“vAcceleration”。循环遍历所有对象并计算每个对象的重力加速度。

然后,循环遍历所有对象,将加速度添加到速度上。然后,再次循环,将速度添加到位置。这就是我过去的做法,而且似乎效果很好。

【讨论】:

  • 其实不止这个。我遵循了您的建议,仅在计算所有对象的速度后才更新速度。漂移较慢,但仍然存在JSFiddle
【解决方案2】:

实际上一个问题是@kbelder 所说的,但另一个问题是我在JSFiddle 中修复的物理疏忽。问题是我给了一个物体(地球)一个速度(即动量)而另一个(太阳)没有补偿动量,所以整个群体的动量很小。注意“sun”的"hSpeed": -1.8 和"vSpeed": 1.8 值以进行补偿。

var elements = [
    {
        "id": "earth",
        "mass": 30.0,
        "left": 30.0,
        "top": 30.0,
        "hSpeed": 300.0,
        "vSpeed": -300.0
    }, {
        "id": "sun",
        "mass": 5000.0,
        "left": 50.0,
        "top": 50.0,
        "hSpeed": -1.8,
        "vSpeed": 1.8
    }];

【讨论】:

    【解决方案3】:

    您正在使用整数类型。整数除法必须在最后一步完成。您所做的每个除法都会导致少量信息丢失,因为必须将结果转换为整数。 尝试对双精度类型而不是整数进行所有操作,并仅在将新位置设置为元素时进行强制转换。任何其他计算都必须保持双倍。

    顺便说一下,将一个操作数乘以 1.0(即将其转换为双精度)会有所帮助。所以你可以这样做:

    return val * 1.0 / hD * xD;
    

    返回未转换的未舍入双精度。

    【讨论】:

    • 不是这样的。我根据你的建议修改了它,它仍然漂移JSFiddle
    • 我的意思不仅仅是这一行。您必须将所有数据保持为双精度,设置新位置,并根据双精度数据而不是新的整数位置计算下一个。
    • 感谢您的反馈,但如果您看一下小提琴,您会发现我为每个相关行都做到了
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-09-09
    • 2017-02-02
    • 2014-07-03
    • 1970-01-01
    • 2023-03-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多