javascript - 如何计算设定距离和目标速度的速度

标签 javascript math physics linear-algebra

我有一个 Sprite 动画,我在其中设置了一个停止距离,并想计算在该停止距离内我必须将对象减慢多少才能达到新的目标速度。但目前我没有得到正确的结果。

我的代码如下所示:

function updatePosition(obj,brake){
    var delta  = new Date().getTime() - obj.timer; //time since last frame

    if(brake){          
        obj.velocity -= (Math.pow(obj.velocity,2) - Math.pow(obj.targetSpeed,2)) / (2 * obj.stopDist); 
        if(obj.velocity < obj.targetSpeed){
            obj.velocity = obj.targetSpeed;
        }
    }
}

我的问题是 Sprite 以远高于目标速度的速度远远超过了停止距离。

我创建了一个带有红点的 fiddle ,前往此处的目的地: http://jsfiddle.net/4tLmz3ch/1/

当它行进由 obj.stopDist 设置的距离时,它应该以目标速度行驶,该速度应该在到达目的地之前就足够了。但我显然在这里的数学有问题。

希望您能帮我解释一下我的误解。

最佳答案

如果您提前确定所需的加速度并在每次刷新期间使用它,这个问题就会简单得多。那么每一帧的整个代码(不包括绘图逻辑并假设一维)就变成:

function frame() {
    var t = new Date().getTime();
    var tDelta = t - obj.lastTime;

    obj.lastTime = t;

    obj.pos += obj.velocity * tDelta;

    if (obj.velocity > obj.destVelocity) {
        obj.velocity += obj.acceleration * tDelta;
    }

    draw();

    setTimeout(frame, 1);
}

给定起始和结束位置和速度,所需加速度的公式(假设加速度恒定)为:

(2 * v0 * (vf - v0) + (vf - v0)^2) / (2 * (xf - x0))

所以像这样初始化对象:

var obj = {
    start: 10,
    height: 200,
    stopDist: 300, 
    dest: 500,
    lastTime: new Date().getTime(),
    velocity: 0.05,
    destVelocity: 0.01,
    pos: undefined,
    acceleration: undefined
};

以下是我们如何开始这一切:

function start(){
    var v0 = obj.velocity,
        vf = obj.destVelocity,
        x0 = obj.start,
        xf = x0 + x.stopDist,
        vDelta = vf - v0;

    obj.pos = x0;
    obj.acceleration = (2 * v0 * vDelta + vDelta * vDelta) / (2 * (xf - x0));

    frame();
}

正如我上面所做的,首先解决第一种情况是有帮助的。这就是全部内容。

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

function drawDot(color, x, y) {
    ctx.fillStyle = color;
    ctx.fillRect(x - 2, y - 2, 4, 4);
}

function draw() {
    ctx.clearRect(0, 0, canvas.width, canvas.height);

    drawDot("red", obj.pos, obj.height);
    drawDot("white", obj.start, obj.height);
    drawDot("green", obj.dest, obj.height);
    drawDot("yellow", obj.start + obj.stopDist, obj.height);

    ctx.fillText("x = " + obj.pos.toFixed(5), 20, 400);
    ctx.fillText("v = " + obj.velocity.toFixed(5), 20, 420);
    ctx.fillText("distance traveled: " + (obj.pos - obj.start).toFixed(2), 20, 440);
}

var obj = {
    start: 10,
    height: 200,
    stopDist: 300,
    dest: 500,
    lastTime: new Date().getTime(),
    velocity: 0.05,
    destVelocity: 0.01,
    pos: undefined,
    acceleration: undefined
};

function frame() {
    var t = new Date().getTime(),
        tDelta = t - obj.lastTime;
    obj.lastTime = t;

    obj.pos += obj.velocity * tDelta;
    if (obj.velocity > obj.destVelocity) {
        obj.velocity += obj.acceleration * tDelta;
    }

    draw();

    setTimeout(frame, 1);
}

function start() {
    var v0 = obj.velocity,
        vf = obj.destVelocity,
        x0 = obj.start,
        xf = x0 + obj.stopDist,
        vDelta = vf - v0;

    obj.pos = x0;
    obj.acceleration = (2 * v0 * vDelta + vDelta * vDelta) / (2 * (xf - x0));

    frame();
}

start();
#canvas{
    background-color:black;
}
<canvas id="canvas" width="700" height="700"></canvas>

http://jsfiddle.net/x7842xcb/3/

<小时/>

这是 2d 版本(请做好准备):

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

function drawDot(color, x, y) {
    ctx.fillStyle = color;
    ctx.fillRect(x - 2, y - 2, 4, 4);
}

function draw() {
    ctx.clearRect(0, 0, canvas.width, canvas.height);

    drawDot("red", obj.pos.x, obj.pos.y);
    drawDot("white", obj.start.x, obj.start.y);
    drawDot("green", obj.dest.x, obj.dest.y);
    drawDot("yellow", obj.stopLocation.x, obj.stopLocation.y);
    
    var dx = obj.pos.x - obj.start.x,
        dy = obj.pos.y - obj.start.y,
        dist = Math.sqrt(dx * dx + dy *dy),
        v = obj.velocity,
        speed = Math.sqrt(v.x * v.x + v.y * v.y);
    
    ctx.fillText("distance traveled: " + dist.toFixed(5), 20, 400);
    ctx.fillText("speed:             " + speed.toFixed(5), 20, 420);
}

var obj = {
    start: { x: 400, y: 230 },
    stopDist: 350,
    dest: { x: 50, y: 330 },
    lastTime: new Date().getTime(),
    startSpeed: 0.05,
    destSpeed: 0.1,

    pos: null,
    velocity: null,
    destVelocity: null,
    acceleration: null
};

function sign(value) {
    return value > 0 ? 1 : (value < 0 ? -1 : 0);
}

function reached(start, current, dest) {
    return current === dest || 
        sign(current - dest) === sign(dest - start);
}

function frame() {
    var t = new Date().getTime(),
        tDelta = t - obj.lastTime,
        v = obj.velocity,
        destv = obj.destVelocity,
        startv = obj.startVelocity;
    obj.lastTime = t;

    obj.pos.x += v.x * tDelta;
    obj.pos.y += v.y * tDelta;
    
    if (!reached(startv.x, v.x, destv.x) ||
        !reached(startv.y, v.y, destv.y)) {
        v.x += obj.acceleration.x * tDelta;
        v.y += obj.acceleration.y * tDelta;
    }

    draw();

    setTimeout(frame, 1);
}

function calcAcceleration(p0, pf, v0, vf) {
    var vDelta = vf - v0;
    
    return pf ===  p0 
        ? 0
        : (2 * v0 * vDelta + vDelta * vDelta) / (2 * (pf - p0));
}

function start() {
    // positions and deltas
    var start = obj.start,
        dest = obj.dest,
        dx = dest.x - start.x,
        dy = dest.y - start.y,
        totalDistance = Math.sqrt(dx * dx + dy * dy);
    
    // x and y component ratio
    var cx = dx / totalDistance,
        cy = dy / totalDistance;
    
    var stopLocation = { x: cx * obj.stopDist + start.x, 
                         y: cy * obj.stopDist + start.y };
    
    // velocities
    var startSpeed = obj.startSpeed,
        destSpeed = obj.destSpeed,
        startVelocity = { x: cx * startSpeed, y: cy * startSpeed },
        endVelocity = { x: cx * destSpeed, y: cy * destSpeed };
    console.log(startVelocity);
    console.log(endVelocity);
    
    // acceleration
    var acceleration = { 
        x: calcAcceleration(start.x, stopLocation.x, startVelocity.x, endVelocity.x),
        y: calcAcceleration(start.y, stopLocation.y, startVelocity.y, endVelocity.y) 
    };

    obj.pos = Object.create(start);
    obj.startVelocity = startVelocity;
    obj.velocity = Object.create(startVelocity);
    obj.stopLocation = stopLocation;
    obj.destVelocity = endVelocity;
    obj.acceleration = acceleration;
    
    frame();
}

start();
#canvas{
    background-color:black;
}
<canvas id="canvas" width="700" height="700"></canvas>

http://jsfiddle.net/1r3q4oob/3/

编辑关于我事后所做的修复:

我最初实现的问题是,只有当前速度的 X 和 Y 分量都大于目标速度时,它才会更新速度。如果出现以下情况,这将阻止正确的行为:

  • 起始速度和结束速度的 X 或 Y 分量均为 0(即,如果它完全水平或垂直移动)
  • 起始速度和结束速度的 X 或 Y 分量为负(即,如果它向上和向左行进)
  • 速度需要增加而不是减少(即点加速达到目标速度)

我通过添加 reached() 函数解决了这个问题,如果 (a) 目标速度当前速度和起始速度之间,该函数基本上返回 true (即当前速度已超过目标速度),或 (b) 当前速度等于目标速度。

关于javascript - 如何计算设定距离和目标速度的速度,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27854973/

相关文章:

javascript - 如何遍历 v8 中全局对象(库)的所有属性/函数?

javascript - 使数组中的数字相对于 0 - 100

math - 如何使用动能在刚体动力学仿真中找到位置

javascript - 如何判断两个物体是否与matter.js接触

javascript - 如何在 MongoDB findOne 上返回条件响应?

javascript - 对 Canvas 中的一组对象设置间隔

javascript - 将 javascript 映射传递给 json wcf 服务

php - 根据比例淡化十六进制颜色

javascript - 在 Javascript 中围绕起点创建 n 边形状的偏移量

ios - 以预设速度向滑动方向移动 SpriteNode