การตรวจจับการชนกับเส้นโค้ง


12

ฉันกำลังทำงานในเกม 2D ที่ฉันต้องการตรวจจับการชนกันระหว่างวงกลมที่กำลังเคลื่อนที่และเส้นโค้งคงที่บางประเภท (อาจเป็นเส้นโค้ง Bezier)

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

ฉันจะทำการตรวจจับการชนกันชนิดนี้ได้อย่างตรงไปตรงมาได้อย่างไร? ฉันรู้เช่นว่า Box2D มีการตรวจจับการชนกับเส้นโค้ง Bezier ฉันไม่ต้องการกลไกตรวจจับการชนกันแบบเต็มรูปแบบเพียงบางสิ่งที่สามารถทำสิ่งที่ฉันได้อธิบายไว้


อัปเดต: ขอบคุณมากสำหรับคำตอบที่ยอดเยี่ยม! ฉันจะต้องอ่านโค้ง Bezier เพื่อทำความเข้าใจวิธีการที่คุณอธิบาย จากนั้นฉันจะกลับไปหาคุณ

คำตอบ:


6

29/09/2012 - 23:20

ฉันสร้าง git Repo ที่นี่: https://github.com/ArthurWulfWhite/Bezier-D Distance/

คุณสามารถดาวน์โหลดไฟล์ต้นฉบับเป็นไฟล์ zip ได้ นอกจากนี้ยังมีตัวอย่างที่คุณสามารถรวบรวมโดยใช้ FlashDevelop หากต้องการใช้การสาธิตให้เปิดโครงการใน Flash Develop และคลิก 'ทดสอบโครงการ' ขณะใช้งานการสาธิตให้คลิก LMB เพื่อสุ่มเส้นโค้ง Bezier ใหม่และวงกลมใหม่

โชคดี!

ลิงก์ซิปมองเห็นยาก - เพียงใช้ Ctrl + F แล้วพิมพ์ zip แหล่งข้อมูลนี้แสดงถึงการค้นหาและการเขียนโปรแกรมสองสามสัปดาห์ฉันหวังว่าคุณจะสนุกกับมัน


หากคุณวางแผนที่จะแบ่ง bezier ซ้ำ ๆ เป็นเซ็กเมนต์และตรวจสอบการชนกับพวกเขาฉันขอแนะนำให้สร้าง 100,100 อาร์เรย์ (ตาราง) และวางแต่ละเซ็กเมนต์ในสี่ช่องที่ใกล้ที่สุดดังนั้นคุณจะต้องตรวจสอบการชนกับ 4 / 10,000 ของ แบ่งแต่ละเฟรม

ฉันคิดว่าคุณจะได้รับประโยชน์จาก box2d ทั้งในฐานะโปรแกรมเมอร์และผู้สร้างเกมเนื่องจากมีอุปสรรค์เล็ก ๆ น้อย ๆ ที่ซ่อนอยู่มากมายในการสร้างกลไกทางฟิสิกส์ที่ 'เรียบง่าย' ที่ทำให้การเคลื่อนไหวดูเหมือนเป็นเรื่องเล็กน้อยและลื่นไหลน้อยลง

คำตอบเก่า: วิธีที่บริสุทธิ์

คุณสามารถดูว่าวงกลมชนกับเส้นโค้ง Bezier หรือไม่โดยตรวจสอบระยะห่างระหว่างระยะห่างระหว่างศูนย์กลางของวงกลมกับจุดที่ใกล้ที่สุดบนเส้นโค้ง

สมการสำหรับระยะทาง (โดยทั่วไป)

อธิบายว่า:

สมการ Bezier:

q(t) = (1-t) * ((1-t) * start.(x,y) + t * control.(x,y)) + t*(t * control.(x,y) + (1 - t) * end.(x,y))

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

q(t) = (start -2 * cont + end) t^2 + (-2 * start + 2 * control) + start

ระยะทางจากจุด (x, y) คือ:

sqrt ((q(t).x - point.x)^2 + (q(t).y - point.y)^2)

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

อนุพันธ์ที่คุณต้องหาหาคือ:

สมมติว่า: a = start b = การควบคุม c = end d = จุดศูนย์กลาง cirlce

อนุพันธ์โดยใช้วุลแฟรมอัลฟ่า

ส่วนที่ยุ่งยากคือการคูณจุดนี้คุณต้องใช้ผลิตภัณฑ์ดอท

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

ฉันขอแนะนำให้หลีกเลี่ยงตอนนี้เพียงรวมค่าสัมประสิทธิ์สำหรับแกน x และสำหรับแกน y และเพิ่มขึ้น

ใช้วิธีการที่เชื่อถือได้ใด ๆ ที่คุณอาจเลือกเช่นนิวตันเพื่อค้นหารากตรวจสอบระยะทางจากจุดที่อยู่บน bezier, 0 <= t <= 1 ถึงศูนย์กลางวงกลมและตรวจสอบระยะทางสำหรับปลายทั้งสองของ bezier (เริ่มต้น และสิ้นสุด) ไปยังศูนย์กลางวงกลมไม่ว่าอันไหนที่ใกล้เคียงที่สุดจะบอกคุณว่ามีการชน

หากรัศมีมีขนาดเล็กกว่าระยะทางต่ำสุดจะมีการชนกัน

มุมประมาณหนึ่งระหว่างศูนย์กลางของวงกลมและจุดที่ใกล้ที่สุดใน bezier

ที่ถูกกล่าวว่าหากคุณต้องการทำเกมที่มีการชนกันของข้อมูลอย่างแท้จริงฉันขอแนะนำให้คุณทำซ้ำ bezier

    q(t) = (1-t) * ((1-t) * start.(x,y) + t * control.(x,y)) + t*(t * control.(x,y) + (1 - t) * end.(x,y))

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


วิธีที่คุณอธิบายการคำนวณจุดที่สั้นที่สุดไปยังเส้นโค้งนั้นเป็นวิธีที่ฉันกำลังใช้กับเส้นตรงแทนที่จะเป็นเส้นโค้ง แต่ทำแบบเดียวกันกับเส้นโค้งด้วยวิธีที่คุณอธิบายฟังดูซับซ้อนเกินไป ซึ่งอย่างที่ฉันเข้าใจมันก็เป็นสิ่งที่คุณคิด และเกี่ยวกับ Box2D ฉันมั่นใจว่ามันเป็นงานที่ยอดเยี่ยม แต่ฟิสิกส์ในเกมของฉันนั้นเรียบง่ายมากดังนั้นฉันจึงตัดสินใจว่าเอนจิ้นฟิสิกส์ที่ระเบิดเต็มไปหมด
paldepind

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

บ่อยครั้งที่มีความท้าทายที่ไม่คาดคิดเมื่อนำสิ่งใหม่ ๆ มาใช้และความงามของการใช้ 2D ฟิสิกส์ api คือมันเหมือนกับการใช้ภาษาโปรแกรมใด ๆ มันไม่จำเป็นต้องใช้ความพยายามเป็นพิเศษในส่วนอื่นของคุณนอกเหนือจากการลงทุนสองสามชั่วโมงเพื่อเรียนรู้ ผลลัพธ์เป็นที่น่าพอใจมาก
AturSams

ฉันเพิ่มรายละเอียดอีกสองสามอย่างในตอนนี้โชคดี :)
AturSams

ฉันกำลังสร้างเกม Elasto Mania อย่างง่าย ๆ มีเพียงสามวงกลมที่เคลื่อนไหวและรูปทรงคงที่ เครื่องยนต์ทั้งหมดเสร็จสิ้นและใช้งานได้ดี สิ่งเดียวที่เหลือคืออนุญาตให้โค้งซึ่งฉันกำลังจะแก้ไข atm ขอบคุณความช่วยเหลือในคำตอบนี้ :) รู้สึกฟรีเพื่อโพสต์รหัสที่คุณกล่าวถึง คุณคิดว่ามันเหมาะสมกับการใช้งานในชีวิตจริงอย่างไร? ดีกว่าแปลง bezier เป็นเส้นเล็ก ๆ ?
paldepind

7

หากต้องการทำสิ่งนี้ฉันจะ:

  • แบ่งเส้นโค้งเบซิเยร์ออกเป็นเซกเมนต์ของเส้นแบ่งและจัดเก็บ

  • ใส่เซกเมนต์ทั้งหมดเหล่านี้ลงในกล่องขอบที่จัดแนวแกนสำหรับส่วนโค้งทั้งหมด

การตรวจจับการชนกัน:

1) ตรวจสอบว่าทรงกลมอยู่ในกล่องขอบเขตหลักหรือไม่ ถ้าไม่มีไม่มีการชนกัน

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

แก้ไข: หากคุณต้องการความแม่นยำสูงและต้องการประสิทธิภาพที่ดีคุณยังสามารถสร้างกล่องขอบเขตหลักสำหรับเส้นโค้งทั้งหมดแล้วแบ่งส่วนโค้งออกเป็นสองส่วน (เช่น: [0.0 - 0.5]และ[0.5 - 1.0]) สร้างกล่อง bouding สำหรับแต่ละของพวกเขาอีกครั้งแล้วแยกย่อยแต่ละกลุ่มเหล่านี้ในทั้งสองกลุ่ม (จึงให้[0 - 0.25], [0.25 - 0.5]และ[0.5 - 0.75], [0.75 - 1.0]) ทำต่อไปเช่นนี้จนกว่าคุณจะได้ความแม่นยำที่เพียงพอ ในท้ายที่สุดคุณจะมีbinary treeกล่องที่มีขอบเขตด้วยเส้นโค้งหลักที่กล่องที่ส่วนรากและเส้นที่ใบ การค้นหาในต้นไม้จะให้คุณO(log n)แทนO(n)(โดยที่n= จำนวนส่วนของเส้นตรงสำหรับส่วนโค้ง)


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

5

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

บทความนี้ครอบคลุมมันเกินจุด: http://students.cs.byu.edu/~tom/557/text/cic.pdf

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

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


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