我有一个精灵动画,我设置了一个停止距离,并想要计算我必须在停止距离内减慢对象的速度以达到新的目标速度。但目前我没有得到正确的结果。
我的代码如下所示:
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;
}
}
}
我的问题是精灵远远超过了停止距离,速度远高于目标速度。
我创建了一个带有红点的小提琴,前往目的地: http://jsfiddle.net/4tLmz3ch/1/
当它行进obj.stopDist
设定的距离时,它应该达到目标速度,该速度应该在到达目的地之前。但我显然在这里得到的数据不正确。
希望你能帮助解释我的误解。
答案 0 :(得分:3)
如果您提前确定所需的加速度,并且在每次刷新期间使用该问题,则此问题会更加简单。然后每个帧的整个代码(不包括绘图逻辑和假设一个维度)就变成:
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);
}
给定开始和结束位置和速度,所需加速度的公式(假设恒定加速度)为:
这样初始化对象:
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();
&#13;
#canvas{
background-color:black;
}
&#13;
<canvas id="canvas" width="700" height="700"></canvas>
&#13;
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();
&#13;
#canvas{
background-color:black;
}
&#13;
<canvas id="canvas" width="700" height="700"></canvas>
&#13;
http://jsfiddle.net/1r3q4oob/3/
编辑关于我在事后做出的修复:
我最初实现的问题是,如果当前速度的X和Y分量都大于目标速度,它只会更新速度。如果出现以下情况,这将阻止正确的行为:
我通过添加reached()
函数解决了这个问题,如果(a)目标速度在当前速度和起始速度(即当前速度)之间,它基本上返回true已超过目的地速度),或(b)当前速度等于目标速度。