วิธีที่เร็วที่สุดในการตรวจสอบว่า AABB สองตัวกำลังทำการตัดกันคืออะไร


12

ฉันมี AABB สองตัวที่กำลังเคลื่อนที่วิธีที่เร็วที่สุดในการตรวจสอบว่าพวกมันจะตัดกันภายใต้เฟรมอย่างไร

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

สิ่งที่ฉันคิดคือการทำแบบนี้:

นี้

แต่รูปหกเหลี่ยมนั้นค่อนข้างซับซ้อนและฉันไม่รู้วิธีคำนวณจุดตัด AABB - รูปหลายเหลี่ยมอาจมีวิธีที่ง่ายกว่านี้ไหม

ภาษาการเขียนโปรแกรมใด ๆ ที่คุณชอบมากที่สุดฉันสามารถพอร์ตได้อย่างง่ายดาย

ขอบคุณ


3
ฉันสับสน คุณพูดถึง "การทดสอบแบบกวาด" โดยเฉพาะคุณลองใช้การทดสอบการกวาดแบบ AABB ทั่วไปหรือไม่ มันทำสิ่งที่คุณต้องการ
เยี่ยมชมที่รีเซิร์ฟ

1
ฉันเห็นด้วยกับความคิดเห็นข้างต้น - เกิดอะไรขึ้นกับการทดสอบ "คลาสสิค" ยิ่งกว่านั้นโซลูชันที่เสนอที่นี่ส่วนใหญ่จะช้ากว่านั้นอย่างเห็นได้ชัด ... และบางวิธีอาจให้ผลลัพธ์ที่ผิด (ไม่แข็งแกร่ง)
พุธที่

คุณสามารถลองทดสอบการแยกแกนได้gamedevelopment.tutsplus.com/tutorials/ …
Pharap

คำตอบ:


8

ใช้ผลรวม Minkowski

วิธีที่ดีในการแก้ปัญหานี้คือการพิจารณาจุดตัดระหว่างเส้นการเคลื่อนที่ ( v ) ที่แปลเป็นจุดกำเนิด ( v ' ) และผลรวมของMinkowskiของA ที่หมุนรอบ 180 องศาที่จุดกำเนิด ( A' ) และอุปสรรค (เพียงBในกรณีนี้): A' ⊕ B

ในภาพต่อไปนี้ฉันวางA smack-dab ในจุดเริ่มต้นของระบบพิกัดโดยพลการ ช่วยลดความยุ่งยากนี้การทำความเข้าใจเป็นหมุน 180 องศาส่งผลให้A 'และวีแปลไปจุดเริ่มต้นเท่ากับV'

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

ผลรวม Minkowski - กรณีเลวลง

ในภาพต่อไปนี้มีการใช้จุดกำเนิดที่แตกต่างกันและพบจุดตัดกันเดียวกัน

ผลรวมของ Minkowski - กรณีทั่วไปมากขึ้น

AABB ที่กำลังเคลื่อนที่หลายตัว

ในการทำให้งานนี้สำหรับ AABB สองตัวที่เคลื่อนที่แบบเชิงเส้นในช่วงเวลาหนึ่งคุณจะลบเวกเตอร์ความเร็วBของจากเวกเตอร์ความเร็วของAและใช้เป็นส่วนของเส้นตรงสำหรับการตัดกันเส้น AABB

รหัสหลอก

def normalize(aabb):
    return {x1: min(aabb.x1, aabb.x2), x2: max(aabb.x1, aabb.x2),
            y1: min(aabb.y1, aabb.y2), y2: max(aabb.y1, aabb.y2),

def rotate_about_origin(aabb):
    return normalize({x1: -aabb.x1, x2: -aabb.x2
                      y1: -aabb.y1, y2: -aabb.y2})

# given normalized aabb's
def minkowski_sum(aabb1, aabb2):
    return {x1: aabb1.x1+aabb2.x1, x2: aabb1.x2+aabb2.x2,
            y1: aabb1.y1+aabb2.y1, y2: aabb1.y2+aabb2.y2}

def get_line_segment_from_origin(v):
    return {x1: 0, y1: 0, x2: v.x, y2: v.y}

def moving_objects_with_aabb_intersection(object1, object2):
    A = object1.get_aabb()
    B = object2.get_aabb()

    # get A'⊕B
    rotated_A = rotate_about_origin(A)
    sum_aabb = minkowski_sum(rotated_A, B)

    # get v'
    total_relative_velocity = vector_subtract(object1.get_relative_velocity(), object2.get_relative_velocity())
    line_segment = get_line_segment_from_origin(total_relative_velocity)

    # call your favorite line clipping algorithm
    return line_aabb_intersection(line_segment, sum_aabb)

การตอบสนองการชนกัน

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

เมื่อมีการชนกันอัลกอริธึม line-AABB-intersection จะคืนค่าจุดตัด 1 หรือ 2 จุดขึ้นอยู่กับว่า A สิ้นสุดการเคลื่อนที่ภายใน B หรือผ่านตามลำดับ (นี่คือการลดกรณีที่เลวลงที่ A ถาโถม B ข้างตัวหรือตามมุมใดมุมหนึ่ง)

ไม่ว่าจะด้วยวิธีใดจุดตัดแรกตามส่วนของเส้นตรงคือจุดชนคุณจะต้องแปลงกลับไปยังตำแหน่งที่ถูกต้องในระบบพิกัดโลก (วงกลมสีฟ้าอ่อนแรกในรูปภาพที่สองตามต้นฉบับvเรียกว่าp ) จากนั้นตัดสินใจ (เช่นสำหรับการชนแบบยืดหยุ่นโดยการสะท้อนvตามการชนปกติที่p ) ตำแหน่งที่แท้จริงของAที่ท้ายเฟรมจะเป็นเท่าใด ( ที่ + 1 )

การตอบสนองการชนกัน

หากมี colliders มากกว่า 2 ตัวสิ่งนี้จะมีความซับซ้อนเพิ่มขึ้นเล็กน้อยเนื่องจากคุณต้องการตรวจจับการชนกันของวินาที, สะท้อน, ส่วนของvเช่นกัน


ขอบคุณที่น่าสนใจที่สุด คุณช่วยอธิบายได้อย่างไรว่าคุณจัดการกับกรณีอย่างไรเมื่อ A และ B ตัดกันระหว่างการย้าย แต่จบการย้ายโดยไม่ต้องแยก
GameAlchemist

@GameAlchemist นั่นคือการตอบสนองการชนกันของข้อมูลและไม่พบการชนกันของข้อมูลมากนัก (หัวเรื่องดั้งเดิมของคำถาม) แต่ฉันชอบ Paint ดังนั้นลองตรวจสอบการแก้ไข :-)
Eric

ขอบคุณสำหรับการอัปเดต (และ hurra สำหรับรูปแบบ :-)) นี่ไม่ใช่คำถามของฉัน แต่ช่วยให้ฉันเข้าใจว่าอัลกอริทึมของคุณจัดการกับกรณีแล้วเมื่อ A ผ่านไปอย่างสมบูรณ์ B
GameAlchemist

5

OBB - กล่องขอบเขตที่มุ่งเน้น นี่คือการสอน

อย่างมีประสิทธิภาพกล่องขอบเขตที่จัดชิดกับเวกเตอร์ความเร็วของวัตถุ A เป็นแกน y (ขึ้น) ความกว้างและความสูงของมันสามารถคำนวณได้จากจุดเริ่มต้นและจุดสิ้นสุดของวัตถุ A จากนั้นคุณเปรียบเทียบสิ่งนี้กับ AABB ของวัตถุ B (ถือว่าเป็น OOBB) และทองคำของคุณ

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


4

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

หากคุณกังวลว่าการทดสอบแบบ swept นี้มีราคาแพงกว่าเพราะมันส่งคืน "ผลกระทบเวลา" ฉันคิดว่าคุณเพิ่มประสิทธิภาพก่อนเวลาอันควร

ข้อมูลเชิงลึกเพิ่มเติมเกี่ยวกับการทดสอบแบบ swept สามารถพบได้ในหนังสือยอดเยี่ยม: การตรวจจับการชนแบบเรียลไทม์โดย Christer Ericson


3

AABB ประมาณจุดอ่อนของกรณีขอบ

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

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

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

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


2
หรือคำนวณล่วงหน้าความกว้างสูงสุดที่ BB สามารถใช้ในการหมุนและใช้งานได้
ratchet freak

2

คุณจะต้องสลายการเคลื่อนไหวออกเป็นขั้นตอนเล็ก ๆ ของการเคลื่อนไหว ตัวอย่างเช่น:

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

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


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

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

2

คุณควรใช้ความเร็วสัมพัทธ์สำหรับการตรวจสอบการชนดังนั้นหนึ่ง AABB คือ "คงที่" และการเคลื่อนที่อื่นด้วยความเร็วของความเร็วเองลบด้วยความเร็วของ "คงที่"

วิธีที่เร็วที่สุดในการดูว่าพวกเขาอาจตัดกันได้หรือไม่ก็แค่ขยาย AABB ที่กำลังเคลื่อนที่ด้วยความเร็ว

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

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

มิฉะนั้นคุณต้องตรวจสอบว่าเส้นทแยงมุมตัด ABB แบบคงที่หรือไม่

สิ่งนี้เกี่ยวข้องกับการรับพิกัดของเส้นทแยงมุมที่ x = ขอบซ้ายของสแตติกและขอบขวาดูว่า y อยู่ด้านล่างและด้านบนหรือไม่ (ทำซ้ำวิธีอื่น ๆ )

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