การตรวจจับการชนกันของรูปหกเหลี่ยมสำหรับวัตถุที่เคลื่อนไหวเร็วหรือไม่


39

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

BoundingBox การชนกันล้มเหลว

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

ชนะหกเหลี่ยม

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

ตอนนี้ดีมาก แต่ฉันจะตรวจสอบว่า Hexagons สองแห่งของจุดตัดแบบนี้ได้อย่างไร โปรดทราบว่าสิ่งเหล่านี้เป็นรูปหกเหลี่ยมที่เฉพาะเจาะจงมาก

ข้อมูลจำเพาะหกเหลี่ยม

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


สำหรับ (บรรทัดใน A) สำหรับ (บรรทัดใน B) หากการปะทะกัน (ข้ามเส้น) - ยกเว้นที่ไม่ครอบคลุม A ใน B หรือ B ในกรณี A ฮึ่ม =)
Jari Komppa

4
คุณมุ่งมั่นที่จะกล่อง? กล่องที่คุณวาดสามารถถูกแทนด้วยวงกลมที่มีการสูญเสียความแม่นยำน้อยที่สุด แต่อัลโกการชนกันค่อนข้างง่าย ค้นหาการตรวจจับการชนกันของวงกวาด หากอัตราส่วนความยาว / ความกว้างของคุณเลื่อนออกไปจาก 1 ความน่าดึงดูดที่น้อยกว่านี้น่าจะเป็น
Steve H

@ SteveH ฉันกำลังมองหาทางออกที่ยืดหยุ่นที่สุดดังนั้นอัตราส่วนความยาว / ความกว้างจึงเป็นเรื่องใหญ่
API-Beast

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

2
ฉันไม่เคยลองแบบนี้มาก่อน แต่ดูเหมือนว่าแทนที่จะเป็นรูปหกเหลี่ยมในพื้นที่ 2d คุณสามารถนึกถึงการเคลื่อนไหวในรูปแบบ 2d เป็นปริมาณในพื้นที่ 3 มิติที่แกนหนึ่งมีเวลา จากนั้นคุณจะตัดโพลีเฮดราสามมิติสองตัวด้วยพิกัด (x, y, t) หากวัตถุทึบสองอันตัดกันคุณต้องหาค่าต่ำสุด t คุณอาจลดความซับซ้อนลงเล็กน้อยโดยการแปลงพิกัด B ทั้งหมดให้อยู่ในกรอบอ้างอิงของ A ฉันไม่ได้ใช้สิ่งนี้ แต่นั่นคือสิ่งที่ฉันจะเริ่มต้น
amitp

คำตอบ:


34

การแก้ปัญหานั้นง่ายกว่าที่คาดไว้จริงๆ เคล็ดลับคือการใช้การลบ Minkowskiก่อนเทคนิคหกเหลี่ยมของคุณ

นี่คือรูปสี่เหลี่ยม A และ B ของคุณกับความเร็วของพวกเขาและvA vBโปรดทราบว่าvAและvBไม่ใช่ความเร็วจริง ๆ พวกมันคือระยะทางที่เดินทางระหว่างหนึ่งเฟรม

ขั้นตอนที่ 1

ตอนนี้แทนที่สี่เหลี่ยม B ด้วยจุด P และสี่เหลี่ยมผืนผ้า A ด้วยสี่เหลี่ยม C = A + (- B) ซึ่งมีขนาดผลรวมของมิติของ A และ B คุณสมบัติการเพิ่ม Minkowski ระบุว่าการชนกันระหว่างจุดและสี่เหลี่ยมผืนผ้าใหม่เกิดขึ้น ถ้าเพียง แต่ถ้าการปะทะกันระหว่างสี่เหลี่ยมสองรูปแบบเดิมเกิดขึ้น:

ขั้นตอนที่ 2

แต่ถ้าสี่เหลี่ยม C เคลื่อนที่ไปตามเวกเตอร์vAและจุด P เคลื่อนที่ไปตามเวกเตอร์vBการเปลี่ยนแปลงอย่างง่ายของกรอบอ้างอิงบอกเราว่ามันเหมือนกับว่าสี่เหลี่ยม C ยังคงอยู่และจุด P เคลื่อนไปตามเวกเตอร์vB-vA:

ขั้นตอนที่ 3

จากนั้นคุณสามารถใช้สูตรการตัดส่วนกล่องอย่างง่ายเพื่อบอกตำแหน่งที่เกิดการชนในกรอบอ้างอิงใหม่

ขั้นตอนสุดท้ายคือเลื่อนกลับไปที่กรอบอ้างอิงที่เหมาะสม เพียงแค่แบ่งระยะทางที่เดินทางโดยจุดจนถึงสี่แยกวงกลมโดยความยาวของเวกเตอร์vB-vAและคุณจะได้รับค่าดังกล่าวว่าs 0 < s < 1การชนเกิดขึ้นในเวลาs * Tที่Tเป็นระยะเวลาของเฟรมของคุณ

ความคิดเห็นโดย madshogo :
ข้อดีข้อหนึ่งของเทคนิคนี้มากกว่าคำตอบของ Mr Beast คือถ้าไม่มีการหมุนคุณสามารถคำนวณ"Minkowski ลบ" A + (- B)ได้ทุกครั้งตามเวลาที่กำหนด !

ดังนั้นอัลกอริทึมเดียวที่ใช้เวลาในทั้งหมดนี้ (ผลรวมของ Minkowski, ความซับซ้อนO (mn)โดยที่mคือจำนวนจุดยอดในAและnจำนวนจุดยอดในB ) สามารถใช้เพียงครั้งเดียวทำให้การตรวจจับการชนเป็นค่าคงที่ได้อย่างมีประสิทธิภาพ ปัญหาเวลา!

หลังจากนั้นคุณสามารถโยนผลรวมออกไปได้เมื่อคุณรู้แน่ว่าAและBอยู่ในส่วนต่าง ๆ ของฉาก (จากควอดทรีของคุณ) และจะไม่ชนกันอีกต่อไป

ในทางตรงกันข้ามวิธีการของ Mr Beast ต้องการการคำนวณค่อนข้างมากในแต่ละขั้นตอน

นอกจากนี้สำหรับสี่เหลี่ยมที่เรียงตามแนวแกนA + (- - B)สามารถคำนวณได้ง่ายกว่าการคำนวณผลรวมทั้งหมดจุดยอดโดยจุดยอด เพียงแค่ขยายAโดยการเพิ่มความสูงของBถึงความสูงและความกว้างของBกับความกว้าง (ครึ่งหนึ่งในแต่ละด้าน)

แต่ทั้งหมดนี้ใช้ได้เฉพาะในกรณีที่AหรือBไม่หมุนและหากทั้งคู่เป็นแบบนูน หากมีการหมุนหรือถ้าคุณใช้รูปร่างเว้าคุณต้องใช้ปริมาณ / พื้นที่กวาด
จบความคิดเห็น


4
ดูเหมือนว่าจะเป็นวิธีที่น่าสนใจ แต่ฉันยังไม่เข้าใจเลย 100% จะเกิดอะไรขึ้นเมื่อวัตถุมีขนาดเล็กมากและเคลื่อนที่ระหว่างสองบรรทัด i.imgur.com/hRolvAF.png
API-Beast

-1: วิธีนี้ไม่ได้ช่วยให้คุณมั่นใจได้ว่าการชนเกิดขึ้น ช่วยให้คุณมั่นใจได้ว่าจะไม่เกิดขึ้นในกรณีที่ส่วนและปริมาตรที่อัดขึ้นรูปไม่ได้ตัดกัน แต่มันเป็นไปได้ทั้งหมดที่พวกมันตัดกันและยังไม่มีการชนกัน มีอะไรผิดปกติคือ "ตอนนี้คุณสามารถใช้ [... ] การตัดส่วนกลุ่มอย่างง่าย ๆ เพื่อตัดสินใจว่าส่วนใดที่เกิดการชนกัน"
jrsala

2
@madshogo คุณพูดถูก ฉันคิดว่าการประทับเวลานั้นมีขนาดเล็กพอเมื่อเทียบกับขนาดของวัตถุที่ว่านี้จะไม่เป็นปัญหา แต่แน่นอนว่าในกรณีทั่วไปนั้นไม่ค่อยแข็งแกร่งเท่าไหร่ ฉันจะตรวจสอบการแก้ไข
sam hocevar

@ SamHocevar หากคุณสามารถแก้ไขคำตอบที่จะดี
API-Beast

1
@ LuisAlves ใช่และไม่ใช่ ... ตรรกะทั้งหมดทำงานได้ แต่คุณจะต้องแทนที่vB-vAด้วยg(t)-f(t)ตำแหน่งfและgตำแหน่งของ A และ B เมื่อเวลาผ่านไป เนื่องจากนี่ไม่ใช่เส้นตรงอีกต่อไปคุณจะต้องแก้ปัญหาการตัดกันเส้นโค้งพาราเมตริก
sam hocevar

17

ประการแรกในกรณีของสี่เหลี่ยมที่เรียงตามแนวแกนคำตอบของ Kevin Reid นั้นดีที่สุดและอัลกอริทึมนั้นเร็วที่สุด

ประการที่สองสำหรับรูปร่างที่เรียบง่ายให้ใช้ความเร็วสัมพัทธ์ (ดังที่เห็นด้านล่าง) และทฤษฎีการแยกแกนสำหรับการตรวจจับการชนกัน มันจะบอกคุณว่ามีการชนกันในกรณีของการเคลื่อนที่เชิงเส้น (ไม่มีการหมุน) และหากมีการหมุนคุณต้องมีการประทับเวลาเล็กน้อยเพื่อให้แม่นยำ ตอนนี้เพื่อตอบคำถาม:


จะบอกได้อย่างไรในกรณีทั่วไปว่ารูปร่างนูนสองจุดตัดกันหรือไม่

ฉันจะให้อัลกอริทึมที่เหมาะกับรูปร่างนูนทั้งหมดไม่ใช่แค่รูปหกเหลี่ยม

สมมติว่าXและYเป็นสองรูปร่างนูน พวกเขาตัดและถ้าหากพวกเขามีจุดที่เหมือนกันคือมีจุดx XและจุดY ∈ Yเช่นว่าx y หากคุณคิดว่าพื้นที่ที่เป็นพื้นที่เวกเตอร์แล้วจำนวนนี้เพื่อบอกx - Y = 0 และตอนนี้เราไปถึงธุรกิจ Minkowski นี้:

รวมคอฟสกีของXและYคือชุดของทุกx + y ที่สำหรับx ∈ XและY ∈ Y


ตัวอย่างสำหรับ X และ Y


X, Y และผลรวม Minkowski, X + Y

เผื่อว่า(-Y)เป็นชุดของทุก-yสำหรับปี∈ Y , ได้รับแล้วย่อหน้าก่อนหน้า, XและYตัดถ้าหากว่าX + (-Y)มี0นั่นคือจุดเริ่มต้น

หมายเหตุด้านข้าง: ทำไมผมเขียนX + (-Y)แทนX - Y ? เพราะในวิชาคณิตศาสตร์มีการดำเนินการที่เรียกว่าความแตกต่างของ Minkowski ของAและBซึ่งบางครั้งเขียนX - Yแต่ไม่มีอะไรเกี่ยวข้องกับเซตของx - y ทั้งหมดสำหรับx ∈ Xและy ∈ Y (Minkowski จริง ความแตกต่างนั้นซับซ้อนกว่าเล็กน้อย)

ดังนั้นเราจึงต้องการคำนวณผลรวมของ Minkowski ของXและ-Yและเพื่อค้นหาว่ามันมีต้นกำเนิดอยู่หรือไม่ ที่มาไม่ได้พิเศษเมื่อเทียบกับจุดอื่น ๆ เพื่อที่จะค้นหาว่าต้นกำเนิดอยู่ในโดเมนที่แน่นอนเราใช้อัลกอริทึมที่สามารถบอกเราว่าจุดใดก็ตามที่เป็นของโดเมนนั้น

ผลรวมของ Minkowski ของXและYมีคุณสมบัติเจ๋ง ๆ ซึ่งก็คือถ้าXและYเป็นส่วนนูนดังนั้นX + Yก็เช่นกัน และพบว่าจุดที่เป็นชุดนูนเป็นมากง่ายกว่าถ้าชุดที่ไม่ได้ (ที่รู้จักกันจะ) นูน

เราไม่สามารถคำนวณx - y ทั้งหมดสำหรับx ∈ Xและy ∈ Y ได้เพราะมีจุดอนันต์เช่นxและyดังนั้นหวังว่าเนื่องจากX , YและX + Yเป็นรูปโค้งเราจึงสามารถใช้ จุด "นอกสุด" กำหนดรูปร่างXและYซึ่งเป็นจุดยอดของพวกเขาและเราจะได้รับจุดนอกสุดของX + Yและอีกมากมาย

จุดเพิ่มเติมเหล่านี้คือ "ล้อมรอบ" โดยจุดนอกสุดของX + Yเพื่อไม่ให้มีส่วนช่วยในการกำหนดรูปร่างนูนที่ได้รับใหม่ เราบอกว่าพวกเขาไม่ได้นิยาม " ตัวเรือนูน " ของชุดคะแนน ดังนั้นสิ่งที่เราทำคือเรากำจัดพวกมันเพื่อเตรียมอัลกอริธึมสุดท้ายที่บอกเราว่าต้นกำเนิดนั้นอยู่ภายในตัวเรือนูนหรือไม่


ฮัลล์นูนของ X + Y เราได้ลบจุดยอด "ภายใน"

เราจึงได้

อัลกอริทึมไร้เดียงสาแรก

boolean intersect(Shape X, Shape Y) {

  SetOfVertices minkowski = new SetOfVertices();
  for (Vertice x in X) {
    for (Vertice y in Y) {
      minkowski.addVertice(x-y);
    }
  }
  return contains(convexHull(minkowski), Vector2D(0,0));

}

เห็นได้ชัดว่าลูปมีความซับซ้อนO (mn)โดยที่mและnเป็นจำนวนจุดยอดของแต่ละรูปร่าง minkoswkiชุดประกอบด้วยล้านองค์ประกอบที่มากที่สุด convexHullอัลกอริทึมที่มีความซับซ้อนที่ขึ้นอยู่กับขั้นตอนวิธีการที่คุณใช้และคุณสามารถมีจุดมุ่งหมายเพื่อO (บันทึก k (k))ที่kคือขนาดของชุดของจุดดังนั้นในกรณีของเราเราได้รับO (ล้านบาทเข้าสู่ระบบ (ล้านบาท) ) . containsอัลกอริทึมที่มีความซับซ้อนที่เป็นเชิงเส้นที่มีจำนวนขอบ (ในแบบ 2D) หรือใบหน้า (3 มิติ) ของเปลือกนูนจึงจริงๆขึ้นอยู่กับรูปทรงเริ่มต้นของคุณ แต่มันจะไม่เป็นมากกว่าO (ล้านบาท)

ฉันจะให้คุณ google สำหรับcontainsอัลกอริทึมสำหรับรูปร่างนูนมันเป็นเรื่องธรรมดา ฉันอาจวางไว้ที่นี่ถ้าฉันมีเวลา


แต่เป็นการตรวจจับการชนกันของข้อมูลที่เรากำลังทำอยู่

ตอนแรกเรามีสองร่างAและBเคลื่อนไหวโดยไม่มีการหมุนในช่วงเวลาdt (จากสิ่งที่ฉันสามารถบอกได้โดยดูที่รูปของคุณ) โทร Let 's วีและวีBความเร็วที่เกี่ยวข้องของและBซึ่งเป็นค่าคงที่ในช่วง timestep ของระยะเวลาของเราdt เราได้รับดังต่อไปนี้:

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

และพวกเขาก็จบลงด้วยการ 'AและB'หลังจากเวลาที่กำหนด

เมื่อต้องการใช้อัลกอริทึมไร้เดียงสาของเราที่นี่เราจะต้องคำนวณปริมาณที่กวาด แต่เราไม่ได้ทำสิ่งนี้

ในกรอบอ้างอิงของB , Bจะไม่ย้าย (duh!) และAมีความเร็วบางอย่าง เทียบกับ Bที่คุณได้จากการคำนวณv A - v B (คุณสามารถทำการสนทนา, คำนวณความเร็วสัมพัทธ์ของBในกรอบอ้างอิงของA )

การเคลื่อนไหวสัมพัทธ์

จากซ้ายไปขวา: ความเร็วในกรอบอ้างอิงฐาน ความเร็วสัมพัทธ์ การคำนวณความเร็วสัมพัทธ์

โดยเกี่ยวกับBเป็นเคลื่อนที่ในกรอบอ้างอิงของตัวเองคุณจะต้องคำนวณปริมาณที่เรตติ้งผ่านขณะที่มันเคลื่อนในช่วงdtกับญาติความเร็ววีv - B

สิ่งนี้จะลดจำนวนของจุดยอดที่จะใช้ในการคำนวณผลรวมของ Minkowski (บางครั้งก็มาก)

การปรับให้เหมาะสมที่เป็นไปได้อีกอย่างคือจุดที่คุณคำนวณปริมาตรที่พัดผ่านโดยวัตถุตัวใดตัวหนึ่งสมมติว่า A. คุณไม่จำเป็นต้องแปลจุดยอดทั้งหมดที่ประกอบเป็น A. เฉพาะสิ่งที่อยู่ในขอบ (ใบหน้าใน 3D) "ใบหน้า" ด้านนอกปกติทิศทางของการกวาด แน่นอนคุณสังเกตเห็นแล้วว่าเมื่อคุณคำนวณพื้นที่กวาดของคุณสำหรับสี่เหลี่ยม คุณสามารถบอกได้ว่าปกติเป็นไปสู่ทิศทางการกวาดโดยใช้ผลิตภัณฑ์ดอทกับทิศทางการกวาดซึ่งจะต้องเป็นบวก

การเพิ่มประสิทธิภาพครั้งล่าสุดซึ่งไม่เกี่ยวข้องกับคำถามของคุณเกี่ยวกับทางแยกมีประโยชน์จริง ๆ ในกรณีของเรา มันใช้ความเร็วสัมพัทธ์ที่เรากล่าวถึงและวิธีการแยกแกนที่เรียกว่า แน่นอนคุณรู้เกี่ยวกับมันแล้ว

สมมติว่าคุณรู้รัศมีของAและBเกี่ยวกับศูนย์กลางมวลของมัน (กล่าวคือระยะห่างระหว่างจุดศูนย์กลางมวลและจุดสุดยอดมากที่สุดจากจุดนี้):

ชนสามารถเกิดขึ้นได้ แต่ถ้ามันเป็นไปได้ว่าวงกลมวิ่งของพบว่าB เราดูที่นี่ว่ามันจะไม่ได้และวิธีที่จะบอกคอมพิวเตอร์ที่อยู่ในการคำนวณระยะทางจากC Bไปฉันตามในภาพต่อไปนี้และให้แน่ใจว่ามันมีขนาดใหญ่กว่าผลรวมของรัศมีของและB ถ้ามันใหญ่กว่าก็ไม่ชนกัน หากมีขนาดเล็กก็ให้ชนกัน

นี้ไม่ได้ทำงานเป็นอย่างดีกับรูปร่างที่ค่อนข้างยาว แต่ในกรณีของสี่เหลี่ยมหรือรูปทรงอื่น ๆ เช่นมันเป็นแก้ปัญหาที่ดีมากที่จะออกกฎการปะทะกัน

ทฤษฎีการแยกแกนนำไปใช้กับBและปริมาตรที่พัดผ่านโดยAจะบอกคุณได้หรือไม่ว่าการชนเกิดขึ้น ความซับซ้อนของอัลกอริธึมที่เกี่ยวข้องนั้นเป็นเส้นตรงกับผลรวมของจำนวนจุดยอดของรูปร่างนูนแต่ละอัน แต่มันวิเศษน้อยลงเมื่อถึงเวลาที่ต้องจัดการกับการชน

อัลกอริทึมใหม่ที่ดีกว่าของเราที่ใช้การแยกเพื่อช่วยตรวจจับการชน แต่ก็ยังไม่ดีเท่าทฤษฎีแกนการแยกที่บอกว่าการชนเกิดขึ้นจริงหรือไม่

boolean mayCollide(Body A, Body B) {

  Vector2D relativeVelocity = A.velocity - B.velocity;
  if (radiiHeuristic(A, B, relativeVelocity)) {
    return false; // there is a separating axis between them
  }

  Volume sweptA = sweptVolume(A, relativeVelocity);
  return contains(convexHull(minkowskiMinus(sweptA, B)), Vector2D(0,0));

}

boolean radiiHeuristic(A, B, relativeVelocity)) {
  // the code here
}

Volume convexHull(SetOfVertices s) {
  // the code here
}

boolean contains(Volume v, Vector2D p) {
  // the code here
}

SetOfVertices minkowskiMinus(Body X, Body Y) {

  SetOfVertices result = new SetOfVertices();
  for (Vertice x in X) {
    for (Vertice y in Y) {
      result.addVertice(x-y);
    }
  }
  return result;

}

2

ฉันไม่คิดว่าการใช้ 'หกเหลี่ยม' จะเป็นประโยชน์ ต่อไปนี้เป็นภาพร่างของวิธีการชนกันที่แน่นอนสำหรับสี่เหลี่ยมที่เรียงตามแนวแกน:

สี่เหลี่ยมเรียงสองแกนซ้อนทับกันถ้าหากช่วงพิกัด X ของพวกเขาทับซ้อนกันและช่วงพิกัด Y ของพวกเขาทับซ้อนกัน (นี่อาจเป็นกรณีพิเศษของทฤษฎีแกนการแยก) นั่นคือถ้าคุณฉายสี่เหลี่ยมลงบนแกน X และ Y คุณได้ลดปัญหานี้เป็นจุดตัดสองเส้น

คำนวณช่วงเวลาที่เส้นสองเส้นบนหนึ่งแกนตัดกัน (เช่นเริ่มต้นที่เวลา (การแยกวัตถุในปัจจุบัน / ความเร็วที่ใกล้เข้ามาถึงวัตถุ) และทำเช่นเดียวกันกับแกนอื่น ๆ หากช่วงเวลาเหล่านั้นทับซ้อนกันเวลาที่เร็วที่สุดในการทับซ้อนกันคือเวลาของการชนกัน


3
คุณลืมร่างของคุณ
MichaelHouse

2
@ Byte56 ไม่ฉันหมายถึงเป็นภาพร่างของอัลกอริธึมไม่ใช่รหัสเทียม
Kevin Reid

อ้อเข้าใจแล้ว. ความผิดพลาดของฉัน.
MichaelHouse

นี่เป็นวิธีที่ง่ายที่สุด ฉันเพิ่มรหัสที่เกี่ยวข้องเพื่อใช้งาน
มหาอำมาตย์

1

ฉันไม่คิดว่าจะมีวิธีง่ายๆในการคำนวณการชนกันของรูปหลายเหลี่ยมที่มีด้านมากกว่ารูปสี่เหลี่ยมผืนผ้า ฉันจะแบ่งมันออกเป็นรูปร่างดั้งเดิมเช่นเส้นและสี่เหลี่ยม:

function objectsWillCollide(object1,object2) {
    var lineA, lineB, lineC, lineD;
    //get projected paths of objects and store them in the 'line' variables

    var AC = lineCollision(lineA,lineC);
    var AD = lineCollision(lineA,lineD);
    var BC = lineCollision(lineB,lineC);
    var BD = lineCollision(lineB,lineD);
    var objectToObjectCollision = rectangleCollision(object1.getRectangle(), object2.getRectangle());

    return (AC || AD || BC || BD || objectToObjectCollision);
}

ภาพประกอบของเส้นทางของวัตถุและปลายทาง

สังเกตว่าฉันเพิกเฉยต่อสถานะเริ่มต้นของแต่ละวัตถุที่ควรตรวจสอบในระหว่างการคำนวณก่อนหน้านี้อย่างไร


3
ปัญหาเกี่ยวกับเรื่องนี้คือถ้าขนาดของวัตถุแตกต่างกันมากวัตถุที่มีขนาดเล็กสามารถเคลื่อนที่ภายในเส้นทางของวัตถุขนาดใหญ่โดยไม่ทำให้เกิดการชน
API-Beast

0

ทฤษฎีบทแกนแยก

ทฤษฎีแกนกลางแยกออกมากล่าวว่า"ถ้าเราสามารถหาแกนที่รูปร่างนูนสองอันไม่ได้ตัดกันแล้วรูปร่างทั้งสองจะไม่ตัดกัน"หรือเป็นไปได้มากขึ้นสำหรับ IT:

"รูปทรงนูนสองรูปตัดกันเฉพาะเมื่อพวกมันตัดกันกับแกนที่เป็นไปได้ทั้งหมด"

สำหรับสี่เหลี่ยมที่จัดแนวแกนจะมีแกนที่เป็นไปได้ 2 แกนคือ: x และ y แต่ทฤษฎีบทไม่ได้ จำกัด อยู่แค่รูปสี่เหลี่ยมมันสามารถใช้กับรูปร่างนูนใด ๆ ได้โดยเพิ่มแกนอื่น ๆ ที่รูปร่างสามารถตัดกันได้ สำหรับรายละเอียดเพิ่มเติมเกี่ยวกับหัวข้อลองดูบทช่วยสอนนี้โดยผู้พัฒนา N: http://www.metanetsoftware.com/technique/tutorialA.html#section1

ดำเนินการดูเหมือนว่านี้:

axes = [... possible axes ...];
collision = true;
for every index i of axes
{
  range1[i] = shape1.getRangeOnAxis(axes[i]);
  range2[i] = shape2.getRangeOnAxis(axes[i]);
  rangeIntersection[i] = range1[i].intersectionWith(range2[i]);
  if(rangeIntersection[i].length() <= 0)
  {
    collision = false;
    break;
  }
}

แกนสามารถแสดงได้เหมือนเวกเตอร์ที่ทำให้เป็นมาตรฐาน

ช่วงคือเส้น 1 มิติ จุดเริ่มต้นควรกำหนดเป็นจุดฉายที่เล็กที่สุดจุดสิ้นสุดไปยังจุดฉายที่ใหญ่ที่สุด

นำไปใช้กับสี่เหลี่ยมผืนผ้า "กวาด"

รูปหกเหลี่ยมในคำถามถูกสร้างขึ้นโดย "การกวาด" AABB ของวัตถุ การกวาดเพิ่มหนึ่งแกนการชนกันที่เป็นไปได้ให้กับรูปร่างใด ๆ : เวกเตอร์การเคลื่อนไหว

shape1 = sweep(originalShape1, movementVectorOfShape1);
shape2 = sweep(originalShape2, movementVectorOfShape2);

axes[0] = vector2f(1.0, 0.0); // X-Axis
axes[1] = vector2f(0.0, 1.0); // Y-Axis
axes[2] = movementVectorOfShape1.normalized();
axes[3] = movementVectorOfShape2.normalized();

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

วิธีการแก้ปัญหานี้จะใช้ได้กับรูปร่างนูนใด ๆ (เช่นสามเหลี่ยม) และรูปร่างนูนใด ๆ ที่กวาด (เช่นแปดเหลี่ยมกวาด) อย่างไรก็ตามรูปร่างที่ซับซ้อนยิ่งมีประสิทธิภาพน้อยลง


โบนัส: ความมหัศจรรย์เกิดขึ้นที่ใด

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

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

shapeRange1 = originalShape1.getRangeOnAxis(axes[2]);
shapeRange2 = originalShape2.getRangeOnAxis(axes[3]);
// Project them on a scale from 0-1 so we can compare the time ranges
timeFrame1 = (rangeIntersection[2] - shapeRange1.center())/movementVectorOfShape1.project(axes[2]);
timeFrame2 = (rangeIntersection[3] - shapeRange2.center())/movementVectorOfShape2.project(axes[3]);
timeIntersection = timeFrame1.intersectionWith(timeFrame2);

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

if(collision)
{
  [... timeIntersection = see above ...]
  if(timeIntersection.length() <= 0)
    collision = false;
  else
    collisionTime = timeIntersection.start; // 0: Start of the frame, 1: End of the frame
}

หากคุณสังเกตเห็นข้อผิดพลาดในตัวอย่างโค้ดแจ้งให้ฉันทราบว่าฉันยังไม่ได้ติดตั้งและยังไม่สามารถทดสอบได้


1
ขอแสดงความยินดีกับการหาทางออก! แต่อย่างที่ฉันพูดไปก่อนหน้านี้เพียงเพราะรูปหกเหลี่ยมตัดกันไม่ได้หมายความว่าจะมีการชนกัน คุณสามารถใช้วิธีการคำนวณเวลาการชนทั้งหมดที่คุณต้องการหากไม่มีการชนกันมันไม่มีประโยชน์มาก ประการที่สองคุณสามารถใช้ความเร็วสัมพัทธ์เพื่อคำนวณเพียง 1 swept volume และเพื่อทำให้การคำนวณง่ายขึ้นเมื่อใช้ SAT ในที่สุดฉันมีความคิดคร่าวๆว่าเคล็ดลับ "เวลาทางแยก" ของคุณทำงานอย่างไรเพราะบางทีคุณอาจทำให้ดัชนีของคุณปะปนอยู่โดยดูshapeRange1 == shapeRange2รหัสของคุณใช่ไหม
jrsala

@madshogo ควรเข้าท่ามากกว่านี้
API-Beast

ฉันยังไม่เข้าใจว่าช่วงการปรับสภาพมาตรฐานทำงานอย่างไร แต่ฉันคิดว่านั่นเป็นเพราะฉันต้องการรูปภาพ ฉันหวังว่ามันจะเหมาะกับคุณ
jrsala

-2

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

  1. สัมผัสกับขอบของพวกเขาหรือไม่? (การชนกันของเส้นบรรทัด) ตรวจสอบว่าเส้นขอบของพื้นที่ที่ถูกกวาดนั้นตัดกับเส้นขอบของพื้นที่ที่ถูกกวาดอื่น ๆ หรือไม่ แต่ละพื้นที่กวาดมี 6 ด้าน

  2. อันเล็กอยู่ข้างในตัวอันใหญ่หรือเปล่า? (ใช้รูปร่างที่เรียงตามแนวแกน (จุดตัด & จุด - สาม)) ปรับทิศทางใหม่ (หมุน) พื้นที่กวาดเพื่อให้รูปร่างที่มีขนาดใหญ่กว่าจัดแนวแกนและทดสอบว่าชิ้นเล็กกว่าอยู่ภายในหรือไม่ ควรจะเป็นทั้งหมดหรือไม่มีเลย) อยู่ในพื้นที่ที่จัดแนวแกนกวาด) ขั้นตอนนี้เสร็จสิ้นเพื่อแยก hex ของคุณออกเป็น tris และ rects

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

คุณอาจพบว่าการใช้วงกลม swept bounding (แคปซูลไม่ใช่ฐานสิบหก) ง่ายขึ้นเพราะมันง่ายกว่าที่จะแบ่งมันออกเป็นสองครึ่งวงกลมและ rect เมื่อวางแนวแกนไว้ .. ฉันจะให้คุณวาดทางออก


ไม่ทำงานหากหนึ่งในสี่เหลี่ยมมีขนาดเล็กมากและเคลื่อนที่ภายในช่องว่างระหว่างเส้นขอบสองเส้น
jrsala

@madshogo ฉันเพิ่งเพิ่มไปยังคำตอบของฉัน ควรเป็นโซลูชันที่สมบูรณ์ในตอนนี้
axon

1
"ใช้รูปร่างที่เรียงตามแนวแกน (point-rect & point-tri)": คุณจะจัดแนวสามเหลี่ยมหรือ "จุดสามเหลี่ยม" ได้อย่างไร (ตามความหมาย) ด้วยแกน? "เพื่อให้อันที่ใหญ่กว่านั้นจัดแนวแกน": คุณจะบอกได้อย่างไรว่าอันไหนที่ใหญ่กว่าอันอื่น? คุณคำนวณพื้นที่ของพวกเขาหรือไม่? "นี่เสร็จสิ้นแล้วเพื่อแยก hex ของคุณออกเป็น tris และ rects": hex ตัวไหน? มีสอง. "(หรือยกเลิกการตอบสนองนี้หากคุณต้องการให้ฉันอธิบายให้คุณเห็น)": คุณจริงจังไหม?
jrsala

"คุณจะจัดแนวสามเหลี่ยมกับแกนได้อย่างไร" ตอบ: จัดแนวเส้นทางของ obj ทำให้พื้นที่ถูกกวาด เลือกขอบและใช้ตรีโกณมิติ "คุณจะบอกได้อย่างไรว่าอันไหนที่ใหญ่กว่าอันอื่น" ตอบ: ยกตัวอย่างเช่นใช้ระยะห่างระหว่างจุดตรงข้ามสองจุดในแนวทแยงของ rect (ตรงกลางของฐานสิบหก) "hex ตัวไหน?" A: ตัวใหญ่
axon
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.