§1 简介与前期准备
1.dyn4j简介
dyn4j (dynamic for java) 是github上的大佬William Bittle开发的一款2D物理引擎。(介绍就是这么草率,想看更详细的可以去官网 )
官网首页:
2.教程概述
这个系列的教程,不出意外的话,将会带大家了解dyn4j物理引擎的运作方式、各个类和方法的使用、一些特性,并根据需要进行封装,最终使用JavaFx做成一个可视化的物理引擎。由于国内外有关这款物理引擎的资源极少,本教程可能会有讲得不详细的地方,但对最终效果影响不大。
3.前期准备
首先你得会Java,最好也了解一下JavaFx。然后有高中物理知识的基础就更好啦!
本教程将使用Java8 64位,编辑器是MyEclipse,以及dyn4j 4.0.0(下载地址)。dyn4j是纯Java开发的,因此只需要在工程中导入jar包就可以使用,Java9及以上版本可以根据需求添加Module-info.java,具体的配置方法不在此赘述。
为了方便后续教程,请创建如下项目结构:
Main类将作为程序入口,Simulation类将主要负责管理物理引擎。两个java文件如下:
Main.java:
package com.application;
public class Main {
public static void main(String[] args) {
}
}
Simulation.java:
package com.physics;
public class Simulation {
}
没错,啥都没有,成功水了几行教程。
如果你想验证你的项目中dyn4j是否导入成功,可以尝试将Main类改成:
package com.application;
import org.dyn4j.dynamics.Body;
import org.dyn4j.world.World;
public class Main {
public static void main(String[] args) {
World<Body> world=new World<Body>();//暂时不需要理解这行代码
}
}
不报错的话你就成功了嗷!
4.Get Started!!!
水了那么多字,终于可以开始写代码了。 我们将通过一个案例来熟悉dyn4j物理引擎的基本逻辑。
案例要求:创建一个小球,使之做自由落体运动。创建一个地面,使小球落在地面上。以0.1秒为间隔输出小球的高度(无需实时输出)。
因为大家现在还啥都没学,我将直接给出代码,对照代码进行讲解。
package com.application;
import org.dyn4j.dynamics.Body;
import org.dyn4j.geometry.Ellipse;
import org.dyn4j.geometry.MassType;
import org.dyn4j.geometry.Rectangle;
import org.dyn4j.geometry.Vector2;
import org.dyn4j.world.World;
public class Main {
public static void main(String[] args) {
/*------创建世界------*/
World<Body> world=new World<Body>();//创建世界
world.setGravity(new Vector2(0,-10));//重力加速度设为10m·s^(-2)
//以下是另外两种写法
// world.setGravity(0,-10);
// world.setGravity(Vector2.create(10,-Math.PI/2));
world.getSettings().setStepFrequency(0.001);//设置步频,两次计算间隔1毫秒
/*------创建世界------*/
/*------创建实体------*/
Body ball=new Body();//创建容器存放小球
ball.addFixture(new Ellipse(0.1,0.1));//创建小球并加入容器,宽高均为0.1m,即半径0.05m
ball.getTransform().setTranslationY(10);//将小球的Y坐标设为10m
ball.setMass(MassType.NORMAL);//自动计算小球的质量
Body ground=new Body();//同样的方法创建地面
ground.addFixture(new Rectangle(100,0.1));//地面是一个长方形,宽100m,高0.1m
ground.getTransform().setTranslationY(-0.05);//将地面下移0.05m,保证地面上沿的Y坐标为0
ground.setMass(MassType.INFINITE);//将质量设为无穷大,即不会发生位移或旋转
world.addBody(ball);//将刚才创建的两个Body加入世界
world.addBody(ground);
/*------创建实体------*/
/*------模拟------*/
double oldValue=-1,cur;
while(true) {
world.step(100);//步进100,刚才设置了步频是0.001s,计算100次就相当于过了0.1s
cur=ball.getTransform().getTranslationY();//获取小球Y坐标,即高度
if(cur==oldValue) break;//如果新的高度等于旧的高度,说明小球停止运动,结束程序
oldValue=cur;//更新旧高度
System.out.printf("%fm\n",cur);//按要求输出
}
/*------模拟------*/
}
}
以上代码的输出结果:
9.949500m
9.799000m
9.548500m
9.198000m
8.747500m
8.197000m
7.546500m
6.796000m
5.945500m
4.995000m
3.944500m
2.794000m
1.543500m
0.193000m
0.045000m
可以发现一共输出15次,通过高中物理知识即可简单验证,这个结果是正确的。
以上就是这个案例的完整代码,建议先尝试理解代码,再看以下内容。
以上代码中,在模拟之前我们创建了这样一个世界:
这图是PPT画的
通过代码,我们可以了解到dyn4j的一些基本逻辑:
(了解即可,不必完全掌握)
所有的模拟都要在World中进行,也就是说,一个World对象管理一个模拟,其中包括一些参数,比如上面代码中的重力加速度,以及后面要讲到的精度等乱七八糟的零碎信息。所有的Body也要通过World的addBody方法加入到世界中,才能被模拟。
事实上,World类中还可以加入Joint,用来关联、约束实体,还可以加入碰撞检测等监听器以实现更多功能,如果有机会将在后续教程中讲解。
Body是一个实体,它拥有形状、尺寸、质量、位置等物理性质。一个Body对象中需要通过addFixture方法加入BodyFixture对象 (上面代码中,Rectangle和Ellipse类均继承自BodyFixture),才会生效。
我们可以这样理解:Body本质上是一个容器,一个Body对象中可以加入若干个BodyFixture对象,这些BodyFixture对象就像零件一样,共同组成一个实体,因此说Body是一个实体也没错。
细心的同学可以发现,当你敲出addFixture这个方法名时,代码补全会提示你两类方法(不会有哪个狠人用记事本写代码吧?不会吧不会吧? ),一类是传入BodyFixture对象,另一类是传入Convex对象,即凸多边形(这个物理引擎不支持凹多边形)。本教程中不会讲解Convex类,但相信在大家学过BodyFixture类之后,可以自己通过文档等手段掌握Convex的使用方法!
dyn4j中有平面向量这个概念,由Vector2类实现。你可以在上面代码中设置重力加速度的位置,找到两种创建Vector2对象的方法。第一种是new Vector2(x值, y值),第二种是调用 Vector2.create(大小,方向) 这个静态方法。其中方向是弧度制,且 -π≤方向≤π。坐标、力、多边形等功能的实现都离不开Vector2,你不必现在就理解Vector2。
如果你看到这里,恭喜你,这篇教程你看完了!!!
建议尝试一下模拟方块的自由落体,可以翻翻文档之类的,看看如何给方块加一个初始的角度等,反正电脑在你手里,多尝试一些东西不好玩么?
你可能会觉得以上内容晦涩难懂,这很正常,因为我们还没有写UI,全靠想象。不出意外下篇教程我就会写UI啦!
如果看得人多,我会出下一期的!创作不易,希望有经济能力的话可以打赏一波!