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

How to calculate velocity for a set distance and target velocity

我有一个 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;
        }
    }
}

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

我创建了一个 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();
}

正如我在上面所做的那样,先解决 1d 情况会很有帮助。就是这样,全部放在一起。

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) 目标速度介于 当前速度 之间,则基本上 returns 为真和起始速度(即当前速度已经超过目标速度),或 (b) 当前速度等于目标速度。