การปะทะกันบนต้นไม้รูปสี่เหลี่ยม / ตาราง - นำตรรกะไปปฏิบัติ


13

ก่อนอื่นฉันเพิ่งเขียนตรรกะเกมของตัวเองในระยะเวลาหนึ่งดังนั้นฉันต้องขออภัยถ้าสิ่งนี้อาจดูตรงไปตรงมา

ฉันอ่านเกี่ยวกับต้นไม้สี่ต้นและการตรวจจับการชนกันของกริดเป็นอย่างมาก ฉันเข้าใจตรรกะ - โดยทั่วไปจะไม่ตรวจสอบการชนยกเว้นว่าวัตถุอยู่ใกล้พื้น แต่มันก็ไม่เคยถูกกล่าวถึงว่ามันใช้งานจริงอย่างไร

ฉันมีวิธีที่เป็นไปได้สองสามอย่างในหัว แต่ก็ไม่แน่ใจว่าวิธีไหนดีที่สุด

การทดสอบการชนทั่วไป - ไม่มีการเพิ่มประสิทธิภาพ

for(var i:int = 0; i < objects.length; i++){
        //find object A
        var objectA = objects[i];

        for(var j:int = i + 1; j < objects.length; j++){
            //find object B
            var objectB = objects[j];

            if(objectA.collidesWith(objectB){

               //handle collision logic
              }
        }

เก็บเพื่อนบ้าน (วิธีที่ 1) แต่ถ้าเราต้องการปรับการชนให้เหมาะสมเพื่อตรวจสอบเฉพาะวัตถุที่อยู่ใกล้ เรายังคงวิ่งผ่านวัตถุทั้งหมดหรือไม่หรือเราสร้างอาร์เรย์ด้วยวัตถุใกล้เพื่อตรวจสอบ?

var objects:Array = new Array();
    var neighbours:Array = new Array();

    for(var i:int = 0; i < objects.length; i++){
        //find object A
        var objectA = objects[i];

        for(var j:int = i + 1; j < objects.length; j++){
            //find object B
            var objectB = objects[j];

            if(objectA.isNear(objectB){

               neighbours.push(objectA, objectB);
              }
        }

    }

    //somewhere else
    for(i:int = 0; i < neighbours.length; i++){
        //only check neighbours

        for(j:int = i + 1; j < neighbours.length; j++){

            if(objectA.collidesWith(objectB){

               //handle collision logic
              }
        }
    }

วนรอบวัตถุทั้งหมด แต่เพียงตรวจสอบเพื่อนบ้านเพื่อหาการชนกัน (วิธีที่ 3) ความเป็นไปได้อื่น ๆ คือเรายังคงวนรอบทุกอย่าง แต่ตรวจสอบว่าวัตถุอยู่ใกล้ก่อนทดสอบการชนกันหรือไม่

for(var i:int = 0; i < objects.length; i++){
        //find object A
        var objectA = objects[i];

        for(var j:int = i + 1; j < objects.length; j++){
            //find object B
            var objectB = objects[j];

            if(objectA.isNear(objectB){
               //they are near - check collision!
               if(objectA.collidesWith(objectB){

               //handle collision logic
              }
            }
        }

    }

จัดเก็บวัตถุในข้อมูลไทล์ (วิธีที่ 3) การใช้ sytem แบบเรียงต่อกันอนุญาตให้มีตัวเลือกอื่น เก็บวัตถุที่อยู่บนไทล์เฉพาะในข้อมูลไทล์เอง ตรวจสอบดูว่าไทล์ของวัตถุใดที่เป็นกระเบื้องล้อมรอบมีวัตถุใด ๆ ที่มันอาจชนกับ:

var ObjectA;

for(var i:int = 0; i < 4; i ++){
//check 4 surrounding tiles from object A

   if(Object.currentTile + surroundingTile[i] CONTAINS collidable object){
   //check collision!
    if(objectA.collidesWith(surroundingTile.object){

    //handle collision logic
     }

}
}

ฉันมักจะลองดูที่โลกแห่งความจริงเป็นตัวอย่าง ถ้าฉันต้องการเปรียบเทียบรายการที่มีสีเดียวกันมันก็ดูไร้เหตุผลที่จะตรวจสอบทุกรายการแม้ว่าพวกเขาจะไม่ตรงกับสี (วิธีที่ 2 ให้ตรวจสอบแต่ละรายการ) ฉันอาจจะรวบรวมสิ่งของที่มีสีเดียวกัน (วัตถุที่อยู่ใกล้กัน) และตรวจสอบสิ่งเหล่านั้น (วิธีที่ 1) แทนที่จะตรวจสอบทุกอย่าง

นี่ไม่ใช่การเปรียบเทียบที่เหมาะสมเนื่องจากรายการที่อยู่ในการตรวจสอบการชนนั้นมีการเคลื่อนไหวอยู่ตลอดเวลาเพื่อให้คำสั่งซื้อผสมกัน นั่นทำให้ฉันสับสน

มันจะมีประสิทธิภาพมากขึ้นในการตรวจสอบแต่ละรายการซึ่งจะเป็นการขจัดความเครียดในการสร้างอาร์เรย์ของเพื่อนบ้าน

หรือมีประสิทธิภาพมากขึ้นในการค้นหาเพื่อนบ้านจึงไม่ต้องวนซ้ำวัตถุมากมายเพื่อตรวจสอบการชนกันหรือไม่

การเปลี่ยนแปลงข้อมูลในไทล์แต่ละไฟล์ดูเหมือนจะเข้มข้นมากเช่นกันดังนั้นฉันไม่แน่ใจว่าเป็นความคิดที่ดีหรือไม่

ฉันเคยคิดถึงเกมป้องกันหอคอยที่หอคอยต้องการตรวจจับวัตถุหากวัตถุอยู่ในระยะก่อนที่มันจะยิงเข้ามา และดูเหมือนว่าโง่ที่จะตรวจสอบสิ่งของทั้งหมดในบางครั้งจะไม่มีวัตถุใด ๆ อยู่ใกล้เลย

ฉันขอโทษสำหรับการโพสต์ยาวมักจะมีปัญหาในการอธิบายตัวเอง!


นี่คือภาษาอะไร? Javascript?
Kyle C

2
Actionscript 3 แต่ภาษานั้นไม่เกี่ยวข้องเลย เพียงแค่ต้องการค้นพบว่าอะไรคือวิธีที่ดีที่สุดในการจัดการและจัดโครงสร้างนี้ :)
omgnoseat

คำตอบ:


8

คุณต้องลดจำนวนการตรวจสอบการชนจริง ใช่ฉันรู้อย่างชัดเจน ดังนั้นขออธิบายอย่างละเอียดเกี่ยวกับสิ่งนี้:

อัลกอริทึมการตรวจสอบการชนครั้งแรกของคุณโดยไม่มีการเพิ่มประสิทธิภาพ: จะมีการตรวจสอบจำนวนเท่าใดในการรันครั้งเดียว หาก n คือจำนวนวัตถุการตรวจสอบนี้จะประมาณ n * (n-1) สำหรับวัตถุจำนวนมาก (> 100) สิ่งนี้จะช้าอย่างน่ากลัว

วิธีการตรวจสอบเพื่อนบ้านของคุณจะไม่เร็วขึ้นมาก หากวัตถุของคุณไม่คงที่และเคลื่อนที่ไปมามากคุณจะต้องสร้างรายการเพื่อนบ้านเหล่านี้สำหรับแต่ละวัตถุในเกมวนรอบของคุณ นี่เป็นสิ่งที่เลวร้ายยิ่งกว่าอัลกอริทึมแรกเนื่องจากคุณกำลังทำ n * (n-1) เพื่อสร้างรายชื่อเพื่อนบ้านจากนั้นตรวจสอบวัตถุแต่ละรายการว่ามันชนกับเพื่อนบ้านคนใดคนหนึ่งหรือไม่

คุณต้องแบ่งพื้นที่เกมของคุณ สมมติว่าพื้นที่เกมของคุณกว้าง 400x400 พิกเซล คุณสามารถแบ่งพาร์ติชันนี้ออกเป็น 4 sub-space แต่ละขนาด 200x200 และตรวจสอบแต่ละออบเจกต์ที่เป็นของ sub-space นั้น (วัตถุหนึ่งสามารถอยู่ภายในมากกว่าหนึ่ง sub-space) จากนั้นคุณจะต้องตรวจสอบว่าวัตถุในแต่ละพื้นที่ย่อยชนกับวัตถุอื่นในพื้นที่ย่อยเดียวกันหรือไม่

ดังนั้นต้นทุนรันไทม์จะเป็น: n สำหรับการสร้าง 4 รายการพื้นที่ย่อย + (n / 4) * ((n-1) / 4) ซึ่งดีกว่าราคารันไทม์ก่อนหน้ามาก สิ่งนี้สามารถลดลงได้อีกโดยทำให้พื้นที่ย่อยมีขนาดเล็กลง (เช่น 50x50)

ดังนั้นอัลกอริทึมของเราตอนนี้มีลักษณะดังนี้:

for each (object in objects)
  check into which subspace the object belongs

for each (subspace in subspaces)
  for each (object in subspace)
    check object for collision with other objects in same subspace

สิ่งนี้ค่อนข้างคล้ายกับแนวคิดข้อมูลไทล์ของคุณ แต่พื้นที่ย่อยไม่จำเป็นต้องมีขนาดเท่ากับกระเบื้อง

เราสามารถทำหนึ่งขั้นตอนสุดท้ายเพื่อเพิ่มประสิทธิภาพต่อไปนี้โดยใช้ควอดทรี ให้ k เป็นจำนวนของพื้นที่ย่อย ในการสร้างรายการวัตถุว่างเรากำลังทำการตรวจสอบ k * n นำไปสู่การตรวจสอบจำนวนมากหากโลกของเกมของคุณมีขนาดใหญ่

เพื่อลดต้นทุนนี้เราใช้ควอดทรี quadtree เป็นอีกวิธีในการแบ่งพื้นที่เกมของเรา แทนที่จะแบ่งพื้นที่ 400x400 ของเราออกเป็น 64 50x50 subspaces และตรวจสอบแต่ละวัตถุที่ 64 subspaces ในปัจจุบันคือพื้นที่ของเกมแบ่งออกเป็น 4 subspaces ครึ่งหนึ่งของขนาดของพื้นที่เกม (200x200) ซึ่งจะแบ่งออกเป็น subspaces ขนาดเล็ก (100x100) ซึ่งจะถูกแบ่งอีกครั้งเป็น 50x50 subspaces นี่คือควอดทรีของเรา

ตอนนี้ถ้าเราต้องการทราบว่าสเปซย่อย 50x50 เป็นของวัตถุใดเราตรวจสอบว่าสเปซย่อย 200x200 ใดเป็นของมัน จากนั้นเราไปที่ระดับหนึ่งลึกลงในควอดทรีของเราและตรวจสอบกับ 4 100x100 subspaces ซึ่งอยู่ในพื้นที่ 200x200 ที่เราเพิ่งค้นพบ สิ่งนี้ซ้ำจนกว่าเราจะทราบว่าวัตถุย่อย 50x50 เป็นของอะไร ดังนั้นจำเป็นต้องมีการตรวจสอบกี่ชุด? 4 สำหรับแต่ละระดับของควอดทรี (จำได้ว่าวัตถุสามารถอยู่บนขอบของสอง subspaces) ดังนั้นเราจึงต้องการการตรวจสอบ 4 * 3 เพื่อกำหนดวัตถุให้กับพื้นที่ย่อย 50x50 ที่ถูกต้อง ดีกว่ามาก 64 การตรวจสอบ

ดังนั้นอัลกอริทึมควอดทรีของเราต้องการการตรวจสอบ 4 * 3 * n เพื่อสร้างรายการย่อยและจากนั้นบางอย่างเช่นการตรวจสอบ k * (n / k) เพื่อตรวจสอบการชนของแต่ละวัตถุ


เป็น awnser ที่ชัดเจนและเข้าใจได้มากขอบคุณมาก! มันเป็นวิธีการจัดการกับวัตถุย่อยที่เป็นอย่างไร subspace ทำหน้าที่เหมือนวัตถุหรือไม่ซึ่งวัตถุบนนั้นถูกบันทึกเป็นอาร์เรย์และตรวจสอบซึ่งกันและกันหรือไม่? หรือคุณเพียงแค่ตรวจสอบว่ามันเป็นพื้นที่ย่อยโดยการตรวจสอบ X และ Y ในวงหลัก? ดูเหมือนว่าการสร้างและการเปลี่ยนอาร์เรย์ตลอดเวลานั้นค่อนข้างไร้ประสิทธิภาพ ดังนั้นฉันต้องการให้แน่ใจว่าฉันใช้วิธีการที่เหมาะสม :) ฉันยังสามารถเห็นปัญหาที่เกิดขึ้นเมื่อวัตถุมีขนาดแตกต่างกัน ฉันเดาว่า subspace ที่เล็กที่สุดน่าจะใหญ่พอ ๆ กับวัตถุที่ใหญ่ที่สุด?
omgnoseat

ฉันดีใจที่ได้ช่วย :) สเปซนั้นจะเป็นวัตถุที่รักษา 4 สเปซไว้ในนั้น พื้นที่ย่อยที่เล็กที่สุดนั้นแตกต่างกันเนื่องจากไม่มีพื้นที่ย่อยอีกต่อไป แต่เป็นรายการของวัตถุของคุณ เมื่อพิจารณาถึงต้นทุนการอัพเดททรีของ subspace จะถูกสร้างขึ้นหนึ่งครั้งเมื่อเริ่มเกม ในแต่ละลูปของลูปหลักวัตถุจะแสดงรายการใน subspaces ที่เล็กที่สุดจะถูกล้างและเติมใหม่อีกครั้ง ดังนั้นรายการเพิ่ม / ลบเท่านั้นจึงเป็นสิ่งจำเป็น ไม่จำเป็นต้องเปลี่ยนอาร์เรย์ พื้นที่ย่อยที่เล็กที่สุดควรมีขนาดใหญ่พอที่จะบรรจุวัตถุเกมต่างๆ ขนาดขึ้นอยู่กับเกมของคุณและสามารถปรับแต่งภายหลังได้
สตีเฟ่น

ขอขอบคุณที่ลืมว่าเราจะตรวจสอบการชนกันระหว่างวัตถุจริงในพื้นที่ย่อยที่เล็กที่สุดด้วยทำให้รู้สึกว่ามันควรจะสามารถเก็บวัตถุหลายชิ้นได้ในขณะนี้ ฉันเริ่มการทดสอบแล้วและมันก็เข้ากันได้ดี ปัญหาที่ใหญ่ที่สุดที่ฉันมีคือการตรวจสอบว่ามีวัตถุย่อยอยู่ที่ใด ฉันวนลูปผ่าน subspaces childs ค่า X และ Y นี่เป็นวิธีที่เหมาะสมหรือไม่
omgnoseat

คุณจัดการกับวัตถุที่ขยายเข้าไปในพื้นที่ย่อยมากกว่าหนึ่งแห่ง (เช่นที่ตั้งอยู่ในหรือใกล้กับเขตแดน) ได้อย่างไร?
mghicks

เรียบง่าย: ฉันไม่ วัตถุสามารถเป็นส่วนหนึ่งของหลาย ๆ พื้นที่ย่อยหากพวกเขาอยู่ในขอบเขตของแต่ละพื้นที่ย่อย ดังนั้นหนึ่งวัตถุสามารถเป็นส่วนหนึ่งของช่องว่างย่อยได้สูงสุด 4 รายการ แต่นี่เป็นชนกลุ่มน้อย
สตีเฟ่น
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.