<bdo id="4g88a"><xmp id="4g88a">
  • <legend id="4g88a"><code id="4g88a"></code></legend>

    2D物理引擎 Box2D for javascript Games 第七章 子彈和感應器

    2D物理引擎 Box2D for javascript Games 第七章 子彈和感應器

    你知道 Box2D 可以在每一個時間步中管理剛體間的碰撞并決算它們。

    總之,在憤怒的小鳥中制作攻城機器期間,發生了一些錯誤

    你可能需要注意一下,有時拋射物會穿過城堡,忽略了碰撞。

    這里發生了什么?

    通常,Javascript 游戲運行在 30 與 60 幀每秒之間,如果我們使世界的時間步與幀率同步,每一個時間步將代表 1/30 到 1/60 秒。

    依賴時間步的模擬叫作離散模擬,這不同于真實的世界,在真實世界中事件的發生的是連續的,我們稱之為連續模擬

    離散型模擬中,我們無法知道在時間中的第n步與第(n+1)步之間發生了什么

    如果一個剛體的移動真的很快,那么他可以在小于一個時間步的時間之內穿過另一個剛體,你會發現它在穿過的剛體的 另一邊了,而沒有發生碰撞。

    這個現象叫作隧道效應 (tunneling),并且很自然的,我們想要阻止它的發生

    在本章,你將學習兩種不同的方式去管理剛體間的接觸:

    • 設置剛體為子彈
    • 設置剛體為感應器

    通過本章的學習,你將不會再對管理高速移動的剛體有任何問題

    感受隧道效應

    Box2d 默認的情況下對阻止隧道效應做的很好,使用一個連續的碰撞檢測來計算離散型模擬。

    不幸的是,處于性能相關的考慮,這種類型的碰撞檢測只應用在 dynamic 類型剛體與 static 類型剛體之間的碰撞上。

    這意味著,我們可以在兩個 dynamic 類型的剛體之間產生隧道效應。

    1. 讓我們來看一下,下面的腳本:

       const stage = document.querySelector('#canvas');
       function main() {
            world = new b2World(gravity, sleep);
            debugDraw();
      
            var bodyDef = new b2BodyDef();
            bodyDef.position.Set(320 / worldScale, 470 / worldScale);
            var polygonShape = new b2PolygonShape();
            polygonShape.SetAsBox(320 / worldScale, 10 / worldScale);
            var fixtureDef = new b2FixtureDef();
            fixtureDef.shape = polygonShape;
            fixtureDef.density = 1;
            fixtureDef.restitution = 0.5;
            fixtureDef.friction = 0.5;
            var body = world.CreateBody(bodyDef);
            body.CreateFixture(fixtureDef);
      
            bodyDef.position.Set(600 / worldScale, 240 / worldScale);
            bodyDef.type = b2Body.b2_dynamicBody;
            polygonShape.SetAsBox(10 / worldScale, 220 / worldScale);
            var body2 = world.CreateBody(bodyDef);
            body2.CreateFixture(fixtureDef);
            
            bodyDef.position.Set(320 / worldScale, 455 / worldScale);
            polygonShape.SetAsBox(5 / worldScale, 5 / worldScale);
            var body3 = world.CreateBody(bodyDef);
            body3.CreateFixture(fixtureDef);
            body3.SetLinearVelocity(new b2Vec2(100, -10));
           
            stage.addEventListener('click', updateWorld);
         }
      

      這個腳本中沒有什么新的知識。

      它放置了三個剛體,分別叫作 body,body2,body3,它們分別代表了一個 static 類型的地面,一個 dynamic 類型的障礙物,以及一個 dynamic 類型的小子彈。

      如你所見,子彈以一個非??斓乃俣龋?00,-10)發射,然后 updateWorld() 方法將執行世界的時間步,它不是在每一幀被調用,而是在每一次的鼠標點擊時被調用。

      這樣可以使我們一步一步的來運行模擬,如你想要的一樣慢,并且我們可以看到發生了什么。

    2. 測試網頁,然后多次點擊鼠標。

      image

      源碼: article/ch07/ch07-1.html

      發生了什么?拋物塊在沒有接觸到障礙物的情況下穿過了它。

      我們剛剛體驗了隧道效應。

      現在,讓我們做些什么來阻止它的發生吧!

    阻止隧道效應——設置剛體為子彈

    因為連續的碰撞檢測使得隧道效應無法在 static 類型的剛體上發生,

    在某些情況下,我們也可以將這種方法應用到 dynamic 類型的剛體上,通過設置它們為子彈(bullets)。

    一個子彈(bullets)執行連續的碰撞檢測,來檢測它與 static 類型的和 dynamic 類型的剛體之間的碰撞。

    記住,如果你將所有的剛體都設置為子彈(bullets),你將會發現很高的性能消耗,所以這將由你在性能和精確度之間找到一個平衡點。

    從我的經驗來看,只是一些被玩家或敵人發射的粒子和投射物被設置為子彈(bullets),通常游戲的角色不會移動的快到需要將它們設置為子彈(bullets)

    1. 在 main 函數中僅需要加入一句代碼:

      function main() {
          world = new b2World(gravity, sleep);
          debugDraw();
      
          var bodyDef = new b2BodyDef();
          bodyDef.position.Set(320 / worldScale, 470 / worldScale);
          var polygonShape = new b2PolygonShape();
          polygonShape.SetAsBox(320 / worldScale, 10 / worldScale);
          var fixtureDef = new b2FixtureDef();
          fixtureDef.shape = polygonShape;
          fixtureDef.density = 1;
          fixtureDef.restitution = 0.5;
          fixtureDef.friction = 0.5;
          var body = world.CreateBody(bodyDef);
          body.CreateFixture(fixtureDef);
      
          bodyDef.position.Set(600 / worldScale, 240 / worldScale);
          bodyDef.type = b2Body.b2_dynamicBody;
          polygonShape.SetAsBox(10 / worldScale, 220 / worldScale);
          var body2 = world.CreateBody(bodyDef);
          body2.CreateFixture(fixtureDef);
      
          bodyDef.position.Set(320 / worldScale, 455 / worldScale);
      
          // 加上這一句
          bodyDef.bullet = true;
      
          polygonShape.SetAsBox(5 / worldScale, 5 / worldScale);
          var body3 = world.CreateBody(bodyDef);
          body3.CreateFixture(fixtureDef);
          body3.SetLinearVelocity(new b2Vec2(100, -10));
          stage.addEventListener('click', updateWorld);
          // setInterval(updateWorld, 1000 / 60);
      }
      

      在剛體定義中將bullet屬性設置為true將對子彈進行連續碰撞檢測。

      現在應該會從障礙物上彈回。

    2. 測試網頁,來看看發生了什么。

      image

      源碼: article/ch07/ch07-2.html

      看到了嗎?如你所見,接觸被決算,現在子彈被障礙物彈回。

    3. 將你的憤怒的小鳥模型中,通過拋擲器發射的投射物,應用bullet屬性,然后將產生一個精確的模擬運行

    現在,讓我們來看看最后一個你在 Box2D 可以創建的特殊類型的剛體。

    通過感應器檢測接觸,可以允許剛體重疊

    在你的游戲中,有時你可能需要兩個剛體就像沒有發生任何碰撞一樣重疊在一起,同時還能檢測到碰撞。

    使用感應器(sensor)可以實現這個功能;

    感應器:一個夾具可以在檢測到碰撞的情況下而不作出任何反應。

    你可以使用感應器(sensor)創建剛體,所以你將可以在剛體間沒有任何物理觸點的情況下,知道它們之間發生的碰撞。

    只要想象一下玩家控制的角色和一個開關:你想要知道當玩家控制的角色撞擊開關觸發一些事件,但是同時你不想開關響應玩家的碰撞。

    在最后的腳本中,我們將測試一個感應器

    1. 和往常一樣,我們給剛體 userData 屬性中添加剛體的名字

      將 barrier 剛體設置為 static 類型并通過 isSensor 屬性定義的它的夾具為感應器。

      function main() {
           world = new b2World(gravity, sleep);
           debugDraw();
      
           var bodyDef = new b2BodyDef();
           bodyDef.position.Set(320 / worldScale, 470 / worldScale);
           bodyDef.userData = "floor";
           var polygonShape = new b2PolygonShape();
           polygonShape.SetAsBox(320 / worldScale, 10 / worldScale);
           var fixtureDef = new b2FixtureDef();
           fixtureDef.shape = polygonShape;
           fixtureDef.density = 1;
           fixtureDef.restitution = 0.5;
           fixtureDef.friction = 0.5;
           fixtureDef.isSensor = true;
           var body = world.CreateBody(bodyDef);
           body.CreateFixture(fixtureDef);
      
           bodyDef.position.Set(600 / worldScale, 240 / worldScale);
           bodyDef.userData = "barrier";
           polygonShape.SetAsBox(10 / worldScale, 220 / worldScale);
           var body2 = world.CreateBody(bodyDef);
           body2.CreateFixture(fixtureDef);
      
           bodyDef.position.Set(320 / worldScale, 455 / worldScale);
           bodyDef.bullet = true;
           bodyDef.type = b2Body.b2_dynamicBody;
           bodyDef.userData = "bullet";
           polygonShape.SetAsBox(5 / worldScale, 5 / worldScale);
           fixtureDef.isSensor = false;
           var body3 = world.CreateBody(bodyDef);
           body3.CreateFixture(fixtureDef);
           body3.SetLinearVelocity(new b2Vec2(100, -10));
           // 鼠標多次點擊后查看效果
           stage.addEventListener('click', updateWorld);
       }
      

      為什么我們要將 barrier 設置為 static 類型呢?

      因為它是一個感應器,它的碰撞將不會被決算,所以如果我們設置它為 dynamic 類型,它將不會和地面發生碰撞,所以沒有支撐物,將會一直下落。

      將它設置為 static 類型可以確保它固定在一個位置。

    2. 最后,我們修改 updateWorld() 方法的方式與你在處理檢測碰撞時學到的方法是一樣:

      function updateWorld() {
           world.Step(1 / 30, 10, 10);
           
           world.ClearForces(); // 清除作用力
           for (var b = world.GetBodyList(); b; b = b.GetNext()) {
               for (var c = b.GetContactList(); c; c = c.next) {
                   var contact = c.contact;
                   var fixtureA = contact.GetFixtureA();
                   var fixtureB = contact.GetFixtureB();
                   var bodyA = fixtureA.GetBody();
                   var bodyB = fixtureB.GetBody();
                   var userDataA = bodyA.GetUserData();
                   var userDataB = bodyB.GetUserData();
                   
                   if (userDataA == "barrier" || userDataB == "barrier") {
                       console.log(userDataA + "->" + userDataB);
                   }
               }
           }
      
           world.DrawDebugData(); // 顯示剛體debug輪廓
       }
      
    3. 我們遍歷所有的剛體,然后遍歷所有的接觸的剛體,當我們發現與障礙物發生碰撞的剛體時,我們將在輸出窗口打印發生的相關信息

    4. 測試網頁,然后通過點擊使子彈運動:

      image

      源碼: article/ch07/ch07-3.html

      如你所見,子彈穿過障礙物,碰撞被檢測到,并在開發者工具控制臺輸出下面的文本:

      bullet->barrier
      

      上面的信息被輸出了兩次的情況,是因為當拋射物與障礙物發生碰撞時輸出一次,并且障礙物與拋射物發射碰撞時又輸出了一次。

    注意:在 Javascript 版中,bullet->barrier 輸出了 14 次,猜測是循環中

    小結

    本章探討了兩個屬性的使用,你學習了在一個離散型模擬中怎樣管理連續碰撞檢測,以及怎樣創建被動剛體(感應器),為了在檢測到碰撞時不要進行決算。

    太好了,你們在本書中的學習之旅也在此結束了,但是還有很多Box2D的知識。你也許可以通過本書的知識來制作 Box2D 游戲,但是程序的世界更新非常之快,你要一直更新自己的知識。

    我建議你經常去訪問 www.box2d.org 和 http://box2d.org/manual.pdf 官方網站和文檔,同樣我的博客 www.emanueleferonato.com 也會及時的更新最新的技巧和教程。

    一旦你完成了你的第一個 Box2d 游戲,不要忘記感謝 Erin Catto(類庫的設計者)


    本文相關代碼請在

    https://github.com/willian12345/Box2D-for-Javascript-Games

    注:轉載請注明出處博客園:王二狗Sheldon池中物 (willian12345@126.com)

    posted @ 2023-11-03 20:56  池中物王二狗  閱讀(241)  評論(0編輯  收藏  舉報
    轉載入注明博客園 王二狗Sheldon Email: willian12345@126.com https://github.com/willian12345
    免费视频精品一区二区_日韩一区二区三区精品_aaa在线观看免费完整版_世界一级真人片
    <bdo id="4g88a"><xmp id="4g88a">
  • <legend id="4g88a"><code id="4g88a"></code></legend>