HTML5/SVG/KnockoutJS 轨迹运动模拟器






4.11/5 (2投票s)
使用 SVG / KnockoutJS 的简单轨迹模拟
引言
本文展示了如何使用 SVG/KnockoutJS 编写一个简单的轨迹运动模拟器。
轨迹运动是一个非常成熟的物理学领域,我们在现实世界中有许多应用。在众多现实世界的应用中,轨迹运动如今已成为许多电脑游戏的基础。使用轨迹运动的著名游戏是著名的《愤怒的小鸟》游戏,玩家可以通过特定的角度瞄准愤怒的小鸟,创造轨迹运动来击打和摧毁目标(城堡、物品、猪)。
背景
我之前的文章 使用 HTML5/KnockoutJS 进行 MVVM 编程 应该能帮助您开始使用 SVG/KnockoutJS 进行 MVVM 编程。因此,我不会在本文中详细介绍 SVG/MVVM/KnockoutJS 的基础知识。我假设您也知道 HTML5 和 SVG 的基础知识。
我邀请读者也查看关于轨迹运动模拟器的论文 《多层感知器作为游戏运动生成的基础》。
轨迹运动方程
简化的轨迹运动(不考虑空气阻力、质量和其他物理变量)由以下方程控制(维基百科)
x = Vx . t
y = Vy . t - 0.5 . G . t^2
也就是说,物体(x, y)在轨迹运动中的位置是物体速度和重力的函数。x 方向的速度越大,物体在 x 轴上移动的速度越快;物体在 y 轴上的速度越大,它可以达到的轨迹峰值就越高。轨迹的峰值高度也受到重力(即 -G)的负面影响。
编写模拟代码
我们首先要做的是创建我们的 ViewModel。在这里,我们将创建几个我们将用于轨迹模拟的 可观察变量。正如您所想象的,我们需要一个坐标(x, y)来描述物体的位置。然后,我们需要一个变量来存储模拟时间 t,Vx 和 Vy 分别作为 x 轴和 y 轴的初始速度,以及 g 我们的重力。更准确地说,它应该是 g-y,因为力只具有 y 轴分量),但为了简化,我们继续称其为 g。您会在 update() 函数中注意到,力 g 只与 y 轴分量相关。
最后,我们将 trails 引入 ViewModel 作为 可观察数组。此数组用于存储运动的轨迹,以便我们可以绘制物体在模拟时间 t=0 到 t=<模拟结束> 之间移动的二维空间中的每一点。
至此,我们的 ViewModel 已基本完成。现在我们需要定义我们的 update() 函数。正如您所见,我们的 update 函数非常直接,尤其是如果您熟悉轨迹运动方程。在此函数中,每次更新时,我们会将模拟时间乘以一个常数因子。这里选择 0.05 是任意的,以便接近我们的 50ms 更新周期。
每次调用此函数时,我们需要更新三件事:
1. 通过 Vx * t 更新 x。这表示物体在最后一个 0.05 个时间单位内以速度 Vx 在 x 轴上的位移。[注意:您可能会想,如果 Vx 本身有加速度怎么办?当然您可以尝试一下]
2. 通过 Vy * t 和 - 0.5.g.(t^2) 更新 y。这表示物体在最后一个 0.05 个时间单位内,以恒定速度 Vy 向上运动,同时重力以相反的方式作为阻力因子朝相反方向移动,在 y 轴上的位移。
3. 将最后一个点插入 trails 数组。
我应该提醒您,由于 x、y、trails 是可观察对象,因此我们按照可观察对象应有的方式更新这些变量。可观察对象是一个函数而不是属性,因此 this.x(n) 用值 n 更新可观察对象 x。
//
// View Model
//
var ViewModel = function () {
var self = this;
var offset = 0;
this.x = ko.observable(10);
this.y = ko.observable(10);
this.t = ko.observable(0);
this.vx = ko.observable(6);
this.vy = ko.observable(6);
this.g = ko.observable(10);
var handle;
this.trails = ko.observableArray([]);
// Trajectory motion formula
// x = vx.t;
// y = vy.t - 1/2.(g.t^2)
this.update = function () {
self.t(self.t() + 0.05);
self.x(self.x() + self.vx() * self.t());
self.y(self.y() + (self.vy() * self.t()) - (0.5 * self.g()) * Math.pow(self.t(), 2));
this.trails.push({ x: self.x(), y: self.y() });
if (self.y() < 0) {
clearInterval(handle);
}
}
}
接下来,我们将定义我们的 View。对于我们的模拟,我们需要一个模拟对象,我们将使用 svg <circle> 来表示我们的模拟对象。此外,为了绘制我们的模拟轨迹,我们将创建另一个 svg <circle>,通过 <!-- ko foreach:trails --> 指令生成(关于 ko foreach 的教程可以在 这里 找到)。svg <line> 代表初始速度向量——指示轨迹运动初始方向的指示器(“穷人版”的 愤怒的小鸟 射击方向指示器)。
<svg id="svgRoot"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- the initial velocity vector -->
<line x1="0" y1="480"
data-bind="attr: {y2:(480-4*vy()),x2:4*vx()}"></line>
<!-- the trails of the trajectory motion -->
<!-- ko foreach:trails -->
<circle r="1" data-bind="attr: {cx:x,cy:(480-y)}"></circle>
<!-- /ko -->
<!-- the simulation object -->
<circle r="4" data-bind="attr: {cx:x,cy:(480-y())}"></circle>
</svg>
有了这些 SVG 定义,我们距离完成模拟仅一步之遥。在定义了 ViewModel 和 View 之后,现在我们需要将它们绑定在一起。得益于简单的 KnockoutJS API,我们只需添加一行即可完成此操作。
var viewModel = new ViewModel();
ko.applyBinding(viewModel);
此时,模拟器已准备就绪,但我们还缺少一件事。我们需要一个计时器来更新我们的模拟计时器。我们将使用 JavaScript 的 setInterval(callback, milliseconds) 来实现此目的。
由于我们希望每 X 毫秒(选择 50)调用一次 update() 函数,因此可以通过调用以下方法轻松设置模拟循环:
setInterval(function() { viewModel.update() }, 50);
现在模拟循环已设置,我们可以运行模拟器了。您可以尝试不同的物理变量,在浏览器中创建一些有趣的轨迹运动。
您可以通过此 链接 尝试轨迹运动模拟器的实时演示版本。实时演示包含更多代码,允许我们交互式地修改物理变量并“开始”/“重置”模拟器。尽管如此,我还是邀请您查看实时演示的源代码,如果您好奇的话。好消息是,这些额外的功能仅需少量额外代码即可实现。得益于 MVVM 模式和 KnockoutJS 的数据绑定功能,数据更新可以自动刷新并显示在 UI 中。
下面是我们刚刚创建的轨迹模拟器的截图。
摘要
本文展示了如何使用 HTML5/SVG 和 KnockoutJS 编写轨迹运动模拟器。对于那些对图形编程了解不多的人来说,人们可能会想知道编写这种模拟器需要多少代码。
答案是,使用 HTML5/SVG 和 KnockoutJS,编写这样的东西只需要很少的代码。我记得大约 10 年前,我在撰写我的论文“《多层感知器作为游戏运动生成的基础》”时,我不得不编写大量的 C++/OpenGL 代码来编写一个轨迹运动模拟器。
回想起来,这表明 HTML5 和 Web 技术取得了多么大的进步,现在许多事情都可以更快、更容易地完成。
历史
V1.0:具有可控 x、y、t、Vx、Vy、G 变量的轨迹模拟。