本文纯属个人见解,是对前面习学的总结,如有描述不正确的地方还请高手指正~
周末然突想到了,游戏引擎里自然是要需一个物理引擎的,于是究研了一下JBox2D(至于为什么是JBox2D,也只是因为认为JBox2D的可移植性更好,是不是采取其他的物理引擎等后以再说)。
由于本人开辟的JavaFX的游戏引擎WJFXGame现在在赶工中,里头的物理引擎的例子发出来也不容易习学。所以我又从新写了个。
其实很单简,JBox2D的物理作操和图形渲染是离开的,我们只要需担任理处图形渲染的分部可即。
示例地址: 点击
我们在这里用使的是官网下载的最新版的JBox2D,如下图所示:
首先,我们建新一个JBox2D中的刚体与JavaFX里头的图形结合的一个类WBody。
import javafx.scene.canvas.GraphicsContext; import javafx.scene.paint.Paint; import org.jbox2d.dynamics.Body; public abstract class WBody{ protected Body mBody; protected Paint mColor; protected final static int RATE = 30; protected double x,y,width,height; public Body getBody() { return mBody; } public void setBody(Body mBody) { this.mBody = mBody; } public Paint getColor() { return mColor; } public void setColor(Paint mColor) { this.mColor = mColor; } public abstract void draw(GraphicsContext gc); public abstract void update(); public double getX() { return x; } public void setX(double x) { this.x = x; } public double getY() { return y; } public void setY(double y) { this.y = y; } public void setLocation(double x,double y){ setX(x); setY(y); } public double getWidth() { return width; } public void setWidth(double width) { this.width = width; } public double getHeight() { return height; } public void setHeight(double height) { this.height = height; } }
这个类中,包含了一个刚体body,和一系列位置小大等属性的设置。为了单简,这里都是用的通普的属性,没有用使可绑定的属性(在WJFXGame中,我都是用使的JavaFX的可绑定的属性)。
RATE是屏幕与天下的比例。平日这个比例越大,由于重力速度雷同,在屏幕里看到的会运动的更快。
然后我们创立一个圆的WBody,叫WCircleBody。
import org.jbox2d.dynamics.Body; import javafx.beans.property.DoubleProperty; import javafx.beans.property.SimpleDoubleProperty; import javafx.scene.canvas.GraphicsContext; import javafx.scene.paint.Paint; public class WCircleBody extends WBody { protected DoubleProperty radius; public WCircleBody(Body body, double radius, Paint color){ this.mBody = body; this.radius = new SimpleDoubleProperty(radius); this.mColor = color; } @Override public void draw(GraphicsContext gc) { gc.save(); gc.setFill(mColor); gc.fillOval(getX(), getY(), getRaidus() * 2, getRaidus() * 2); gc.restore(); } @Override public void update() { setX(mBody.getPosition().x * RATE - getRaidus()); setY(mBody.getPosition().y * RATE - getRaidus()); } public double getWidth(){ return 2 * getRaidus(); } public double getHeight(){ return 2 * getRaidus(); } public DoubleProperty radiusProperty(){ return radius; } public double getRaidus(){ return radius.get(); } public void setRadius(double radius){ this.radius.set(radius); } }
这个承继于WBody,draw方法中根据x,y和半径,来制绘圆。在update方法中,将圆的图形位置设置为Body刚体的位置。由于刚体里的position是正心中的,所以我们要需自己停止盘算左上角的位置。
然后创立一个矩形的WBody,叫WBoxBody。
import javafx.scene.canvas.GraphicsContext; import javafx.scene.paint.Paint; import org.jbox2d.dynamics.Body; public class WBoxBody extends WBody { public WBoxBody(double width,double height, Body body, Paint paint) { this.mBody = body; this.mColor = paint; this.width = width; this.height = height; } @Override public void draw(GraphicsContext gc) { gc.save(); gc.setFill(mColor); gc.fillRect(getX(), getY(), getWidth(),getHeight()); gc.restore(); } @Override public void update() { setX(mBody.getPosition().x * RATE - getWidth() / 2); setY(mBody.getPosition().y * RATE - getHeight() / 2); } }
样同的,WBoxBody里跟WCircleBody里似类。就不做过多解释了。
面下我们来建新一个类承继Canvas,来担任全部的制绘和新更作操。
import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; import javafx.animation.KeyFrame; import javafx.animation.Timeline; import javafx.event.ActionEvent; import javafx.event.EventHandler; import javafx.scene.canvas.Canvas; import javafx.scene.canvas.GraphicsContext; import javafx.scene.paint.Color; import javafx.util.Duration; import org.jbox2d.collision.AABB; import org.jbox2d.collision.shapes.CircleShape; import org.jbox2d.collision.shapes.PolygonShape; import org.jbox2d.common.Vec2; import org.jbox2d.dynamics.Body; import org.jbox2d.dynamics.BodyDef; import org.jbox2d.dynamics.BodyType; import org.jbox2d.dynamics.FixtureDef; import org.jbox2d.dynamics.World; import org.wing.jfx.physics.test.body.WBody; import org.wing.jfx.physics.test.body.WBoxBody; import org.wing.jfx.physics.test.body.WCircleBody; public class CanvasScreen extends Canvas { private GraphicsContext gc; private Timeline mTimeline; private double duration = 10; private static final int RATE = 10; private World world; private AABB aabb; private BodyDef bd; private List<WBody> bodys = new CopyOnWriteArrayList<>(); public CanvasScreen(double width, double height) { super(width, height); Vec2 gravity = new Vec2(-1.0f, 10.0f); world = new World(gravity); Vec2 minV = new Vec2(-100.0f, -100.0f); Vec2 maxV = new Vec2(100.0f, 100.0f); aabb = new AABB(); aabb.lowerBound.set(minV); aabb.upperBound.set(maxV); world.queryAABB(null, aabb); bd = new BodyDef(); gc = getGraphicsContext2D(); init(); initObjects(); } public void init() { mTimeline = new Timeline(); mTimeline.setCycleCount(Timeline.INDEFINITE); KeyFrame frame = new KeyFrame(Duration.millis(duration), new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent event) { draw(gc); update(); } }); mTimeline.getKeyFrames().add(frame); mTimeline.play(); } public void initObjects(){ createPolygon(10, 500, (int) getWidth() - 10 * 2, 10, 0.3f, 0.6f, true); createPolygon(10, 10, 10, 500, 0.3f, 0.6f, true); createPolygon((int) getWidth() - 10 * 2, 10, 10, 500, 0.3f, 0.6f, true); float restitution[] = new float[] { 0.9f, 0.9f, 0.9f, 0.9f, 0.75f, 0.9f, 1.0f, 0.6f, 0.8f, 0.4f, 0.9f, 0.9f, 0.9f, 0.9f, 0.75f, 0.9f, 1.0f, 0.6f, 0.8f, 0.4f, 0.9f, 0.9f, 0.9f, 0.9f, 0.75f, 0.9f, 1.0f, 0.6f, 0.8f, 0.4f, 0.9f, 0.9f, 0.9f, }; for (int i = 0; i < restitution.length; i++) { createCircle(80 + i * 20, 5 * i, 15, 0.3f, restitution[i], false); } } public void draw(GraphicsContext gc) { gc.clearRect(0, 0, getWidth(), getHeight()); gc.setFill(Color.BLACK); gc.fillRect(0, 0, getWidth(), getHeight()); for(WBody mBody : bodys){ mBody.draw(gc); } } public void update() { for(WBody body : bodys){ body.update(); } world.step(1.0f / 60.0f, 8, 3); } /** * 创立矩形 */ public void createPolygon(float x, float y, float w, float h, float friction, float restitution, boolean isStatic) { // 创立多边形皮肤 PolygonShape shape = new PolygonShape(); shape.setAsBox(w / 2 / RATE, h / 2 / RATE); FixtureDef fd = new FixtureDef(); fd.shape = shape; fd.density = 1.0f; // 设置密度 fd.friction = friction; // 设置摩擦力 fd.restitution = restitution; // 设置多边形的恢复力 // 设置刚体初始坐标 bd.type = isStatic ? BodyType.STATIC : BodyType.DYNAMIC; bd.position.set((x + w / 2) / RATE, (y + h / 2) / RATE); // 创立物体 Body body = world.createBody(bd); // 物理天下创立物体 // 此方法改变了 // body.createShape(pDef); //为body添加皮肤 body.createFixture(fd); WBoxBody boxBody = new WBoxBody(w, h, body, Color.WHITE); boxBody.setLocation(x, y); bodys.add(boxBody); } /** * 创立圆形 */ public void createCircle(float x, float y, float r, float friction, float restitution, boolean isStatic) { CircleShape shape = new CircleShape(); shape.m_radius = r / RATE; FixtureDef fd = new FixtureDef(); fd.shape = shape; fd.density = 1.0f; fd.friction = friction; fd.restitution = restitution; // 创立刚体 bd.type = isStatic ? BodyType.STATIC : BodyType.DYNAMIC; bd.position.set((x + r) / RATE, (y + r) / RATE); // 创立一个body Body body = world.createBody(bd); body.createFixture(fd); WCircleBody circleBody = new WCircleBody(body, r, Color.RED); circleBody.setLocation(x, y); bodys.add(circleBody); } }
在这个CanvasScreen里,重要做了以下几个作操。
1.创立了JBox2D的天下World
2.创立了一个Timeline,增加了一个Keyframe,通过无限循环来模拟游戏的新更作操。
3.通过createPolygon和createCircle来创立Body并初始化WBody素元。这里要需注意Body的position为心中位置,而我们制绘的Object是以左上为position,要需自行理处一下。还有就是屏幕与天下的比例RATE,在停止position的时候的理处。
4.在keyframe中,制绘WBody表列并在update中新更WBody表列。
5.新更World,我们在update中world.step(...)停止新更JBox的World里的物理变化。如果没有world.step,全部的物理作操将法无停止。
接下来是我们的主类。
import javafx.application.Application; import javafx.scene.Group; import javafx.scene.Scene; import javafx.stage.Stage; public class MainClass extends Application { private static final int WIDTH = 800; private static final int HEIGHT = 600; @Override public void start(Stage primaryStage) { CanvasScreen mCanvas = new CanvasScreen(WIDTH, HEIGHT); Group root = new Group(); root.getChildren().add(mCanvas); Scene scene = new Scene(root, WIDTH, HEIGHT); primaryStage.setTitle("JavaFX中JBox2D的用使"); primaryStage.setScene(scene); primaryStage.show(); } public static void main(String[] args) { launch(args); } }
大家可以看看行运果效。
本人用使的是JDK7,大家看示提下载吧。
果效图:
文章结束给大家分享下程序员的一些笑话语录: 据说有一位软件工程师,一位硬件工程师和一位项目经理同坐车参加研讨会。不幸在从盘山公路下山时坏在半路上了。于是两位工程师和一位经理就如何修车的问题展开了讨论。
硬件工程师说:“我可以用随身携带的瑞士军刀把车坏的部分拆下来,找出原因,排除故障。”
项目经理说:“根据经营管理学,应该召开会议,根据问题现状写出需求报告,制订计划,编写日程安排,逐步逼近,alpha测试,beta1测试和beta2测试解决问题。”
软件工程说:“咱们还是应该把车推回山顶再开下来,看看问题是否重复发生。”