JBox2D第一課-搭建swing繪製框架並實現小球自由落體

JBox2D第一課-搭建swing繪製框架並實現小球自由落體

jbox2d官網

http://www.jbox2d.org/

下載好jbox2d.jar

這是下載地址
這裡我們使用jbox2d-2.0.1-full.jar

接下來就是搭建繪製框架了

這裡寫圖片描述
這裡寫圖片描述

下面是具體的實現

  1. 寫一個通用的窗體MyFrame.java,待會我們的MainFrame繼承這個來建立一個本課的窗體
package cn.ncgds.common;
import org.jbox2d.collision.AABB;
import org.jbox2d.dynamics.World;
import javax.swing.JFrame;
import java.util.ArrayList;
/**
1. 通用遊戲窗體類
*/
public abstract class MyFrame extends JFrame {
/*管理碰撞的世界*/
public AABB aabb;
/*物理世界*/
public World world;
/*所有剛體集合*/
public ArrayList<MyBody> bodyList;
public MyFrame(){
init();
setFrame();
}
/**
* 設定窗體資訊
*/
protected abstract void setFrame();
/**
* 初始化
*/
protected abstract void init();
}

上面的程式碼相信聰明的你都可以看懂
2. 下面我們來建立我們的抽象剛體類MyBody.java,待會我們的MyCircleBody繼承這個類

package cn.ncgds.common;
import org.jbox2d.dynamics.Body;
import java.awt.*;
/**
*
* @Author: EasyDots
* @Url: www.ncgds.cn
* @Description: 抽象剛體類
*/
public abstract class MyBody{
//JBox2D物理引擎中的剛體
public Body body;
//剛體的顏色
public Color color;
//繪製的方法
public abstract void drawSelf(Graphics canvas);
}

上面的程式碼也簡單吧?沒有不明白的,那我們繼續下一步
3.建立圓球剛體類MyCircleBody,繼承上面的MyBody

package cn.ncgds.common;
import org.jbox2d.dynamics.Body;
import java.awt.*;
/**
* 圓球剛體類
*/
public class MyCircleBody extends MyBody {
public int r;
public MyCircleBody(Body body, int r, Color color){
this.r = r;
this.color = color;
this.body =body;
}
@Override
public void drawSelf(Graphics canvas) {
canvas.setColor(color);
int x= (int) body.getPosition().x;
int y= (int) body.getPosition().y;
canvas.drawOval(x-r,y-r,r*2,r*2);
}
}

上面的程式碼有問題嗎?或許你對下面這段程式碼有疑問?那我加上註釋吧
注意到,body的getPosition()方法返回的x,y是中心點,如果是圓,那麼是返回圓心的座標

/**
* 繪製橢圓
* @param x: 橢圓所在矩形的左上角橫座標
* @param y: 橢圓所在矩形的左上角縱座標
* @param w: 橢圓所在矩形的寬,其實就是橢圓中的2a,叫長軸長
* @param h: 橢圓所在矩形的高,其實就是橢圓中的2b,叫短軸長
*/
canvas.drawOval(x-r,y-r,r*2,r*2);

4.建立剛體工廠類Box2dUtil.java來生成我們的剛體

package cn.ncgds.demo1;
import cn.ncgds.common.MyCircleBody;
import org.jbox2d.collision.CircleDef;
import org.jbox2d.dynamics.Body;
import org.jbox2d.dynamics.BodyDef;
import org.jbox2d.dynamics.World;
import java.awt.*;
import static cn.ncgds.common.Conf.RATE;
/**
* 剛體工廠類
*/
public class Box2dUtil {
public static CircleDef circleDef;
public static MyCircleBody createCircle(int x, int y, int r, World world, Color color){
//圓形
circleDef = new CircleDef();
//密度
circleDef.density = 2;
//摩擦係數
circleDef.friction = 0.0f;
//能量損失率
circleDef.restitution = 0.95f;
//半徑
circleDef.radius = r/RATE;
BodyDef bodyDef = new BodyDef();
bodyDef.position.set(x/RATE, y/RATE);
//在世界建立剛體
Body bodyTemp = world.createBody(bodyDef);
//指定剛體形狀
bodyTemp.createShape(circleDef);
//從附加的形狀計算質量屬性
bodyTemp.setMassFromShapes();
return new MyCircleBody(bodyTemp, r, color);
}
}

5.接下來建立我們的繪製檢視類GView.java,這個類繼承自JPanel, 重寫paint方法來進行繪製

package cn.ncgds.common;
import javax.swing.*;
import java.awt.*;
/**
* 繪製檢視類
* @author easydots
* @url www.ncgds.cn
*/
public class GView extends JPanel{
MyFrame myFrame;
DrawThread drawThread;
public GView(MyFrame myFrame){
this.myFrame = myFrame;
/*設定背景透明*/
setBackground(null);
setOpaque(false);
drawThread = new DrawThread(this);
drawThread.start();
}
@Override
public void paint(Graphics g) {
super.paint(g);
onDraw(g);
}
/**
* 繪製方法
* @param g
*/
synchronized public void onDraw(Graphics g){
/*迴圈繪製所有剛體*/
for (MyBody body:myFrame.bodyList) {
body.drawSelf(g);
};
}
}

6.看了這裡肯定會有疑問了,DrawThread是什麼?別急,我們先建立一個配置類Conf.java

package cn.ncgds.common;
/**
* 配置資訊
*/
public class Conf {
//螢幕與現實世界的比例
public static final int RATE = 1;
//繪製執行緒工作標誌位
public static final boolean DRAW_THREAD_FLAG=true;
//模擬的頻率
public static final float TIME_STEP = 2.0f/60.0f;
//迭代次數
public static final int ITERA = 10;
}

7.那麼我們來建立我們的繪製執行緒DrawThread.java,負責繪製所有圖形

package cn.ncgds.common;
/**
* 繪製執行緒
*/
public class DrawThread extends Thread{
GView gView;
public DrawThread(GView gView){
this.gView = gView;
}
@Override
synchronized public void run() {
while (Conf.DRAW_THREAD_FLAG){
//設定物理世界時間步長來進行模擬
gView.myFrame.world.step(Conf.TIME_STEP, Conf.ITERA);
//重新繪製
gView.repaint();
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}

8.有了上面這些東西后我們可以來建立我們的小球了,MainFrame繼承自MyFrame

package cn.ncgds.demo1;
import cn.ncgds.common.GView;
import cn.ncgds.common.MyBody;
import cn.ncgds.common.MyCircleBody;
import cn.ncgds.common.MyFrame;
import org.jbox2d.collision.AABB;
import org.jbox2d.common.Vec2;
import org.jbox2d.dynamics.World;
import javax.swing.*;
import java.awt.*;
import java.util.ArrayList;
/**
* @Author: EasyDots
* @Url: www.ncgds.cn
* @Description: 本課主窗體
*/
public class MainFrame extends MyFrame {
/*繪製檢視*/
private GView gv;
public MainFrame(){
init();
addBody();
setFrame();
}
/**
* 新增剛體
*/
private void addBody() {
int ballR = 40;
MyCircleBody ball = Box2dUtil.createCircle(100,100,ballR,world, Color.red);
bodyList.add(ball);
}
@Override
public void init() {
bodyList = new ArrayList<MyBody>();
aabb = new AABB();
aabb.lowerBound.set(100.0f, 100.0f);
aabb.upperBound.set(800.0f, 400.0f);
Vec2 gravity = new Vec2(0.0f, 10.0f);
boolean doSleep = true;
world = new World(aabb, gravity, doSleep);
gv = new GView(this);
}
@Override
public void setFrame(){
setTitle("Hello JBox2D");
setBounds(100,100,800,600);
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
setVisible(true);
add(gv);
}
}

我主要解釋一下下面這段程式碼

        //軸對齊邊界框,可以理解為世界的邊界盒子
aabb = new AABB();
//下邊界
aabb.lowerBound.set(100.0f, 100.0f);
//上邊界
aabb.upperBound.set(800.0f, 400.0f);
//重力向量
Vec2 gravity = new Vec2(0.0f, 10.0f);
//是否節省效能,當沒有碰撞時休眠
boolean doSleep = true;
//例項化我們的世界
world = new World(aabb, gravity, doSleep);

9.終於做好所有工作了,是不是迫不及待想看看小球自由落體呢?那執行吧,其實還執行不了,我們需要一個入口函式,這裡我們建立一個Main.java

package cn.ncgds;
import cn.ncgds.demo1.MainFrame;
public class Main {
public static void main(String[] args) {
new MainFrame();
}
}

執行效果
這裡寫圖片描述

好了今天的教程就到這裡了,本專案原始碼已在github上,請自行檢視我建立的Box2dgithub