javascript - 在基于时间的动画中取消运动的逻辑
问题描述
我在我的简单游戏中设计了一个运动引擎,您可以在其中拥有特定的坐标(x,y)
,然后单击即可前往任何目的地(x,y)
。这个想法是你有speed
并且fuel consumption
整个运动也是基于时间的。所以最后,如果你决定从 A 点到 B 点,它将花费 X 时间(由速度决定)和 X 燃料量(由消耗决定)。我面临的问题是计算精度,可能不是到达目的地后停止旅行的最佳逻辑。
所做的计算是正确的,但它们的精度导致了一些问题。
在我包含的演示中可以看出,我当前的取消逻辑正在产生剩余物(例如,应该燃烧 20 升燃料,但我只剩下 0.12...):
if ( Math.floor( this.distance ) === 0 ) {
而我的前一个永远不会结束(因为它永远不会在没有小数位的情况下达到理想的 0):
if ( this.distance > 0 ) {
我的问题是,如何改进我的代码,使旅行始终以正确的点结束并且燃料始终处于应有的状态。
const tools = {
distance: (p1, p2) => Math.sqrt((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y)),
rftv: (p1, p2) => Math.atan2(p2.y - p1.y, p2.x - p1.x)
};
this.fuel = 200;
this.x = 100;
this.y = 50;
this.rad = 0; // radian angle between current and destination
this.speed = 100; // 100 pixels per second
this.consumption = 10; // 10 liters of fuel per 100 pixels
this.destination = {
x: 220,
y: 140
};
/*
Based on above
distance : 150
time : ( distance / speed ) => 150 / 100 => 1.5s
fuel : ( distance / consumption ) => 150 / 10 => 15 liters
So to summary, between two points we have 150 pixels of distance,
this trip should take 1.5s and burn 15 liters of fuel
*/
this.now = undefined;
this.delta = undefined;
this.then = Date.now();
this.setDelta = function() {
this.now = Date.now();
this.delta = (this.now - this.then) / 1000;
this.then = this.now;
};
this.update = function() {
this.rad = tools.rftv(
{ x: this.x, y: this.y },
{ x: this.destination.x, y: this.destination.y }
);
let step = this.speed * this.delta;
this.x += Math.cos(this.rad) * step;
this.y += Math.sin(this.rad) * step;
this.fuel -= step / this.consumption;
};
this.move = function() {
this.distance = tools.distance(
{ x: this.x, y: this.y },
{ x: this.destination.x, y: this.destination.y }
);
if ( Math.floor( this.distance ) === 0 ) {
clearInterval(tsid);
console.log('done', this.x, this.y, this.fuel, this.distance, '[ ' + (Date.now() - startedAt) + ' ]');
} else {
this.setDelta();
this.update();
console.log('going', this.x, this.y, this.fuel, this.distance, '[ ' + (Date.now() - startedAt) + ' ]');
}
};
let tsid;
let startedAt = Date.now();
tsid = setInterval(function() {
this.move();
}, 10);
解决方案
如果您的对象总是直接朝向目标行进,则不需要任何三角函数。只需使用矢量数学。
逻辑上
update
应该调用move
,而不是相反。距离计算应在与移动代码相同的函数中执行。
检查距离
move
并设置完成标志。一步使用的燃料 = 消耗(使用量/像素)×步长(以像素为单位),所以
this.fuel -= step / this.consumption;
是不正确的。
代码:
this.fuel = 200;
this.x = 100;
this.y = 50;
// no need for rad
this.speed = 100;
this.consumption = 10;
this.destination = {
x: 220,
y: 140
};
this.complete = false; // completion flag
...
// swap functions
this.update = function() {
this.update();
if (this.complete) {
clearInterval(tsid);
console.log('done', this.x, this.y, this.fuel, this.distance, '[ ' + (Date.now() - startedAt) + ' ]');
} else {
this.setDelta();
this.move();
console.log('going', this.x, this.y, this.fuel, this.distance, '[ ' + (Date.now() - startedAt) + ' ]');
}
};
this.move = function() {
let step = this.speed * this.delta;
let dist = tools.distance(
{ x: this.x, y: this.y },
{ x: this.destination.x, y: this.destination.y }
);
/*
would be cleaner to replace this with:
Math.hypot(this.destination.x - this.x, this.destination.y - this.y);
*/
// check distance
if (dist <= step) {
step = dist;
this.complete = true;
}
// vector math not trigonometry
this.x += (this.destination.x - this.x) * (step / dist);
this.y += (this.destination.y - this.y) * (step / dist);
this.distance -= step;
this.fuel -= step * this.consumption; // should be * not /
};
...
tsid = setInterval(function() {
this.update();
}, 10);
推荐阅读
- ruby-on-rails - 如何修复“Bundler 2 需要 RubyGems 2.5 或更高版本”
- javascript - 如何在获取承诺中获得第一个数组
- windows-10 - 运行 npm run dist 后 dist 文件夹被锁定
- spring-boot - 如何为 Camel JMS 应用程序实现 CRL 和 OCSP?
- angular - 如何将 Firebase 实时数据库连接到我的 Angular 7 应用程序?使用 CRUD 操作
- xml - 错误:“(”需要启动 ATTLIST 枚举
- android - 检测导航组件中的弹出
- ios - 退格不能在 swift 的正则表达式之外工作
- amazon-web-services - 如何从另一个 AWS 账户访问公共 S3 存储桶?
- c# - 如何组合 Swashbuckle 过滤器?