box2d中物體移動的功能實現細節

 

     最近把box2d研究了一遍,整體上差很少是瞭解了,可是在運行那個小球下落的demo時候發現移動速度與物理狀況不同,仔細研究了半天才發現原來有這麼多細節概念要注意。這必須記錄下來,對之後有一樣問題的人確定會有所啓發。app

 

物理模型:ide

    一個邊長爲1m的正方形盒子懸停在20m高的位置,而後自由落體。根據公式h=gt^2/2可知下落到0m高時應該是2s。post

對應box2d的模型:測試

    一個形狀爲邊長爲1m的正四角型,不存在任何阻尼,放在20m高的空中,而後設置向下引力爲9.8m/s^2。動畫

 

注意一:單位換算 spa

    這個地方是新手最容易出錯的。因爲libgdx中的距離單位都是px,但box2d的距離單位都是m,因此你程序中全部要計算的值都要確保單位正確。 rest

    具體來講,好比個人顯示器分辨率是1440px*900px,那我建立一個遊戲界面爲200px*200px的,如new JoglApplication(new DemoGame(), "Demo Demo", 200, 200, false);。這個很好理解,你的整個遊戲界面就是這個大小,與顯示器的分辨率是正比關係。遊戲

    而後,遊戲中會有一個camera來看具體的界面內容,這裏只考慮正投影攝像機(不會由於距離對界面放大或者縮小),那麼定義其能看到的也是200px*200px的畫面new OrthographicCamera(200, 200),這 樣就真的是畫面上的1px表明你顯示器的1px了。camera大小的變化是你遊戲中的1px與實際顯示器1px是成反比的。我以爲這個與遊戲界面設置同樣最好理解了。ci

    那若是我但願10個像素表明1m(scale=10px/m),那計算出來的每一個物體位移1m,你對應的actor都要移動10個像素。就是這麼個換算關係。get

    注1:box2d中的單位都是標準單位,好比長度就是m,重量就是kg。

    注2:box2d的物理計算與畫面上的actor是分開的,須要把物理數據再經過同步來反應到actor上。可是在Box2DDebugRenderer能夠直觀的看這個物理世界。

 

注意二:最大速度限制

    這個時候物體移動起來有點意思了,當把世界引力加到很大,詭異的事情發生了,開始仍是加速運動,忽然到後面明顯變成勻速運動了。查了一遍資料才發現,原來box2d把每幀位移限制在2m/frame,因此在60Hz的刷新率狀況下物體最大移動速度就是120m/s了。悲劇的是這個限制是hard coding,這多是讓物體看起來移動比較連續吧。因此要把速度加上去,那就只能提升幀數了,好比world.step(1/100f, 3, 3);能提升最大速度就是200m/s,但這麼修改會有一個嚴重的問題,就是讓物體位移的時間刻度和實際時間不一致了,畫面看上去會和人感知不同。

    注:timestep是時間步長,這是動畫裏面一個重要概念,物理世界每次計算出來新的位置就必需要知道過了多久時間,這就讓離散的時間點計算變成一個連續的動畫。

 

注意三:初始化的影響

     以上工做都作好了,發現下落時間怎麼統計都不對。弄了半天才發反應過來原來第一次render的時候就已經開始計算物體移動了,而且把這個deltaTime帶入進去計算了。因爲初始化速度會比較慢,程序遠達不到60Hz的刷新度,因此我把那個時間做爲第一次下落時間實際上是已經晚了的。因此乾脆把第一次render再讓每一個物體喚醒,這就獲得指望的結果了。

 

 

附上測試代碼:

  
  
  
  
  1. public class DemoGame implements ApplicationListener { 
  2.  
  3.     protected OrthographicCamera camera;  
  4.     protected Box2DDebugRenderer renderer; // 測試用繪製器  
  5.     private World world; 
  6.     private float scale = 10f; // 屏幕的縮放比例,10像素/米 
  7.     private long start = 0
  8.     private Body body; 
  9.     private boolean isFinished = true
  10.     private int count = 0
  11.     private float totalTime = 0
  12.  
  13.     @Override  
  14.     public void create() {  
  15.         Thread.currentThread().setPriority(Thread.MAX_PRIORITY); 
  16.         camera = new OrthographicCamera(200/scale, 200/scale);// 這裏爲了展現物理世界,把視野也轉換成m 
  17.         camera.position.set(100/scale, 100/scale, 0);  // 攝像機的postion是畫面的中心點
  18.         renderer = new Box2DDebugRenderer();  
  19.          
  20.         world = new World(new Vector2(0, -9.8f), true); // 通常標準重力場  
  21.         BodyDef bd = new BodyDef(); //聲明物體定義  
  22.         bd.position.set(1020);  
  23.         bd.type = BodyType.DynamicBody;  
  24.         body = world.createBody(bd); //經過world建立一個物體  
  25.         PolygonShape box = new PolygonShape(); 
  26.         box.setAsBox(0.5f, 0.5f);   // 注意單位是邊長的一半 
  27.         FixtureDef fd = new FixtureDef(); 
  28.         fd.shape = box; 
  29.         fd.friction = 0
  30.         fd.restitution = 0
  31.         fd.density = 1
  32.         body.createFixture(fd); //將形狀和密度賦給物體  
  33.         body.setLinearDamping(0f);  // 沒有線性阻尼 
  34.         body.setAngularDamping(0f); // 沒有旋轉阻尼 
  35.         body.setAwake(false); 
  36.     } 
  37.  
  38.     @Override  
  39.     public void render() {  
  40.         world.step(Gdx.app.getGraphics().getDeltaTime(), 33);  
  41.         if(start == 0) { 
  42.             count = 1
  43.             start = System.currentTimeMillis(); 
  44.             System.out.println("start postion:" + body.getPosition().y); 
  45.             body.setAwake(true);    // 讓渲染的第一幀再讓物體運動,這樣時間得到才準確, 
  46.                                     // 不然第一個deltaTime有初始化時間,會讓整個計算不許 
  47.         } else { 
  48.             if(isFinished){ 
  49.                 System.out.println("動畫渲染次數: " + count++); 
  50.                 System.out.println("物體移動速度: " + body.getLinearVelocity().y); 
  51.                 System.out.println("---------------------------------"); 
  52.                 totalTime += Gdx.app.getGraphics().getDeltaTime(); 
  53.             } 
  54.             if(body.getPosition().y <= 0 && isFinished) { 
  55.                 System.out.println("實際時間差:" + (System.currentTimeMillis() - start)/1000f); 
  56.                 System.out.println("圖像增量統計時間差:" + totalTime); 
  57.                 System.out.println("end postion:" + body.getPosition().y); 
  58.                 isFinished = false
  59.                 world.destroyBody(body); 
  60.             } 
  61.         } 
  62.         GL10 gl = Gdx.app.getGraphics().getGL10();  
  63.         gl.glClear(GL10.GL_COLOR_BUFFER_BIT);  
  64.         camera.update();  
  65.         camera.apply(gl);  
  66.         renderer.render(world, camera.combined);  
  67.     } 
  68.      
  69.     @Override  
  70.     public void dispose() {  
  71.         renderer.dispose();  
  72.         world.dispose(); 
  73.          
  74.         renderer = null;  
  75.         world = null;  
  76.     } 
  77.      
  78.     @Override  
  79.     public void pause() {  
  80.     } 
  81.  
  82.     @Override  
  83.     public void resize(int width, int height) {  
  84.     } 
  85.  
  86.     @Override  
  87.     public void resume() {  
  88.     } 
相關文章
相關標籤/搜索