Ball to Ball Collision - การตรวจจับและการจัดการ


266

ด้วยความช่วยเหลือของชุมชน Stack Overflow ฉันได้เขียนแบบจำลองทางฟิสิกส์พื้นฐาน แต่สนุก

ข้อความแสดงแทน

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

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

ฉันเดาคำถามของฉันมีสองส่วน:

  1. วิธีที่ดีที่สุดในการตรวจจับการชนกันของลูกบอลคืออะไร?
    ฉันแค่มีวง O (n ^ 2) ที่วนซ้ำลูกบอลแต่ละลูกแล้วตรวจสอบลูกบอลอื่น ๆ เพื่อดูว่ารัศมีนั้นทับกันหรือไม่?
  2. ฉันใช้สมการอะไรในการจัดการลูกบอลเพื่อชนลูก? ฟิสิกส์ 101
    มันส่งผลต่อความเร็วของลูกบอลสองลูกที่ x / y เวกเตอร์อย่างไร ทิศทางของผลลัพธ์ที่ลูกบอลสองลูกมุ่งหน้าไปคืออะไร? ฉันจะใช้สิ่งนี้กับลูกบอลแต่ละลูกได้อย่างไร

ข้อความแสดงแทน

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

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


แก้ไข: แหล่งข้อมูลที่ฉันพบว่ามีประโยชน์

ฟิสิกส์ลูกบอล 2 มิติพร้อมเวกเตอร์: การชนสองมิติโดยไม่มีตรีโกณมิติ.
pdf ตัวอย่างการตรวจสอบการชนของลูกบอล 2d: การเพิ่มการตรวจจับการชน


ที่ประสบความสำเร็จ!

ฉันมีระบบตรวจจับการชนของลูกและการตอบสนองที่ยอดเยี่ยม!

รหัสที่เกี่ยวข้อง:

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

for (int i = 0; i < ballCount; i++)  
{  
    for (int j = i + 1; j < ballCount; j++)  
    {  
        if (balls[i].colliding(balls[j]))  
        {
            balls[i].resolveCollision(balls[j]);
        }
    }
}

วิธีนี้จะตรวจสอบการชนกันของลูกบอลทุกลูก แต่ข้ามการตรวจสอบซ้ำซ้อน (ถ้าคุณต้องตรวจสอบว่าบอล 1 ชนกับบอล 2 หรือไม่แล้วคุณไม่จำเป็นต้องตรวจสอบว่าบอล 2 ชนกับบอล 1 หรือไม่ )

จากนั้นในคลาสบอลของฉันฉันมีวิธีการชนกันของฉัน () และ resolCollision ():

public boolean colliding(Ball ball)
{
    float xd = position.getX() - ball.position.getX();
    float yd = position.getY() - ball.position.getY();

    float sumRadius = getRadius() + ball.getRadius();
    float sqrRadius = sumRadius * sumRadius;

    float distSqr = (xd * xd) + (yd * yd);

    if (distSqr <= sqrRadius)
    {
        return true;
    }

    return false;
}

public void resolveCollision(Ball ball)
{
    // get the mtd
    Vector2d delta = (position.subtract(ball.position));
    float d = delta.getLength();
    // minimum translation distance to push balls apart after intersecting
    Vector2d mtd = delta.multiply(((getRadius() + ball.getRadius())-d)/d); 


    // resolve intersection --
    // inverse mass quantities
    float im1 = 1 / getMass(); 
    float im2 = 1 / ball.getMass();

    // push-pull them apart based off their mass
    position = position.add(mtd.multiply(im1 / (im1 + im2)));
    ball.position = ball.position.subtract(mtd.multiply(im2 / (im1 + im2)));

    // impact speed
    Vector2d v = (this.velocity.subtract(ball.velocity));
    float vn = v.dot(mtd.normalize());

    // sphere intersecting but moving away from each other already
    if (vn > 0.0f) return;

    // collision impulse
    float i = (-(1.0f + Constants.restitution) * vn) / (im1 + im2);
    Vector2d impulse = mtd.normalize().multiply(i);

    // change in momentum
    this.velocity = this.velocity.add(impulse.multiply(im1));
    ball.velocity = ball.velocity.subtract(impulse.multiply(im2));

}

รหัสที่มา: แหล่งที่มาที่สมบูรณ์สำหรับลูกเพื่อลูก collider

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


16
ฉันไม่คิดว่าอัลกอริทึมนี้จะดีพอเพราะถ้าลูกของคุณเคลื่อนไหวเร็วเกินไป (เช่นเร็วกว่ารัศมี 2 * ต่อเฟรมลูกหนึ่งสามารถผ่านบอลอีกลูกได้โดยไม่เกิดการชน)
Benji Mizrahi

@Simulcal คุณสามารถอัปโหลดซอร์สโค้ดของคุณได้อีกครั้ง (ลิงก์ทั้งหมด filtropper.com ลิงก์ปรากฏว่าใช้งานไม่ได้) นอกจากนี้คุณยังสามารถวางไฟล์ pdf ที่คุณได้รับจาก [geocities.com/vobarian/2dcollisions/2dcollisions.pdf] เนื่องจาก geocities ได้ออฟไลน์เมื่อไม่นานมานี้
bguiz

1
นี่คือลิงค์ไปยัง BallBounce รุ่นล่าสุดที่ฉันใช้งาน: dl.dropbox.com/u/638285/ballbounce.rar
mmcdole

@ สำหรับทุกคนที่มีส่วนร่วม: คุณช่วยส่องแสงเพื่อเปลี่ยนเอ็นจินนี้เป็น 3D ได้ไหม กลไกที่ยอดเยี่ยมนี้สามารถทำงานใน Java3D ได้อย่างไร
โมฆะคงที่หลัก

2
บรรทัดVector2d impulse = mtd.multiply(i);ควรเป็น i * เวกเตอร์ mtd ปกติ สิ่งที่ต้องการ:Vector2d impulse = mtd.normalize().multiply(i);
klenwell

คำตอบ:


117

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

วิกิพีเดียมีที่ดีงามสรุปของกระบวนการทั้งหมด สำหรับลูกบอลของมวลใด ๆ ความเร็วใหม่สามารถคำนวณได้โดยใช้สมการ (โดยที่ v1 และ v2 เป็นความเร็วหลังจากการชนและ u1, u2 มาก่อน):

v_ {1} = \ frac {u_ {1} (m_ {1} -m_ {2}) + 2m_ {2} u_ {2}} {m_ {1} + m_ {2}}

v_ {2} = \ frac {u_ {2} (m_ {2} -m_ {1}) + 2m_ {1} u_ {1}} {m_ {1} + m_ {2}}

ถ้าลูกบอลมีมวลเท่ากันความเร็วก็จะเปลี่ยนไป นี่คือรหัสที่ฉันเขียนซึ่งทำสิ่งที่คล้ายกัน:

void Simulation::collide(Storage::Iterator a, Storage::Iterator b)
{
    // Check whether there actually was a collision
    if (a == b)
        return;

    Vector collision = a.position() - b.position();
    double distance = collision.length();
    if (distance == 0.0) {              // hack to avoid div by zero
        collision = Vector(1.0, 0.0);
        distance = 1.0;
    }
    if (distance > 1.0)
        return;

    // Get the components of the velocity vectors which are parallel to the collision.
    // The perpendicular component remains the same for both fish
    collision = collision / distance;
    double aci = a.velocity().dot(collision);
    double bci = b.velocity().dot(collision);

    // Solve for the new velocities using the 1-dimensional elastic collision equations.
    // Turns out it's really simple when the masses are the same.
    double acf = bci;
    double bcf = aci;

    // Replace the collision velocity components with the new ones
    a.velocity() += (acf - aci) * collision;
    b.velocity() += (bcf - bci) * collision;
}

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


2
ให้บอกว่ามวลของลูกบอลทั้งสองนั้นไม่เท่ากัน เวกเตอร์นั้นเปลี่ยนไปอย่างไรระหว่างลูกบอล?
mmcdole

3
เป็นเวลานานแล้วตั้งแต่ชั้นประถมศึกษาปีที่ 12 แต่ฉันคิดว่าพวกเขาจะได้อัตราส่วนโมเมนตัมที่สอดคล้องกับอัตราส่วนของมวล
Ryan Fox

6
@Jay เพียงแค่ชี้ให้เห็น .. ภาพหนึ่งสมการที่คุณเพิ่มนั้นมีไว้สำหรับการชนกันของ 1 มิติไม่ใช่ 2 มิติ
mmcdole

@simucal ไม่จริง ... u และ v เป็นเวกเตอร์ในสมการนั้น นั่นคือพวกเขามีส่วนประกอบ x, y (และ z)
Andrew Rollings

2
@Simucal คุณพูดถูกมันเป็นเพียงมิติเดียว สำหรับมิติข้อมูลเพิ่มเติมให้ใช้ส่วนประกอบของความเร็วที่สอดคล้องกับการชนกัน (aci, bci in code) ส่วนประกอบอื่น ๆ เป็นฉากตั้งฉากกับการชนกันและจะไม่เปลี่ยนแปลงดังนั้นคุณไม่จำเป็นต้องกังวลเกี่ยวกับส่วนประกอบเหล่านี้
Jay Conrod

48

เมื่อหลายปีก่อนฉันทำโปรแกรมเหมือนที่คุณนำเสนอที่นี่
มีปัญหาหนึ่งที่ซ่อนอยู่ (หรือหลายปัญหาขึ้นอยู่กับมุมมอง):

  • หากความเร็วของลูกสูงเกินไปคุณสามารถพลาดการชนได้

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

วิธีแก้ปัญหานั้นชัดเจน: คุณต้องแยกการประทับเวลาดังนั้นก่อนอื่นคุณต้องเลื่อนไปยังตำแหน่งที่ถูกต้องแล้วชนกันจากนั้นจึงเลื่อนไปตามเวลาที่เหลืออยู่


หากเลื่อนตำแหน่งเปิดtimeframelength*speed/2ตำแหน่งจะได้รับการแก้ไขทางสถิติ
Nakilon

@Nakilon: ไม่มันช่วยได้เฉพาะในบางกรณี แต่โดยทั่วไปแล้วมันเป็นไปได้ที่จะพลาดการปะทะกัน และความน่าจะเป็นที่จะพลาดการชนเพิ่มขึ้นตามขนาดของช่วงเวลาความยาว โดยวิธีการที่ดูเหมือนว่า Aleph แสดงให้เห็นถึงวิธีการแก้ปัญหาที่ถูกต้อง
avp

1
@avp ฉันไม่ได้เกี่ยวกับถ้าความเร็วของลูกสูงเกินไปคุณสามารถพลาดการชน แต่เกี่ยวกับตำแหน่งใหม่ของคุณจะไม่ถูกต้อง เนื่องจากตรวจพบการชนกันเล็กน้อยในภายหลังกว่าจะชนกันจริง ๆ หาก substract timeframelength*speed/2จากตำแหน่งนั้นความแม่นยำจะเพิ่มขึ้นสองเท่า
Nakilon

20

คุณควรใช้การแบ่งพื้นที่เพื่อแก้ปัญหานี้

อ่านข้อมูลเกี่ยวกับการ แบ่งพาร์ติชันแบบไบนารีของพื้นที่ และ Quadtrees


4
แทนที่จะใช้การแบ่งช่องว่างอัลกอริทึมการกวาดและลูกพรุนจะทำงานได้ดีขึ้นหรือไม่ ลูกบอลกำลังเคลื่อนที่อย่างรวดเร็วดังนั้นการแบ่งจะต้องได้รับการอัพเดทบ่อยครั้ง การกวาดและลูกพรุนสามารถค้นหาคู่ที่ชนกันทั้งหมดใน O (n log n) โดยไม่มีโครงสร้างข้อมูลชั่วคราว นี่คือการสอนที่ดีสำหรับพื้นฐาน
HugoRune

13

เพื่อเป็นการชี้แจงถึงข้อเสนอแนะของ Ryan Fox ที่จะแบ่งหน้าจอออกเป็นภูมิภาคและตรวจสอบการชนภายในพื้นที่เท่านั้น ...

เช่นแยกพื้นที่เล่นออกเป็นตารางสี่เหลี่ยม (ซึ่งจะมีความยาว 1 หน่วยต่อด้านโดยพลการ) และตรวจสอบการชนภายในแต่ละตารางสี่เหลี่ยม

นั่นเป็นทางออกที่ถูกต้องอย่างแน่นอน ปัญหาเดียวกับมัน (ตามที่ผู้โพสต์คนอื่นชี้ให้เห็น) คือการชนข้ามพรมแดนเป็นปัญหา

วิธีแก้ปัญหานี้คือการวางซ้อนกริดที่สองที่ 0.5 หน่วยในแนวตั้งและแนวนอนเพื่อชดเชยกับตารางแรก

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


+1 สำหรับวิธีการแก้ปัญหาที่แม่นยำยิ่งขึ้นและเพื่อตอบโต้คนที่กล้าหาญด้วยการขับรถอย่างกล้าหาญ
Steven A. Lowe

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

10

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


5
การแก้ไข: คุณต้องตรวจสอบการชนกันของส่วนที่เหมือนกันและข้างเคียง
พ.ย.

7

สิ่งหนึ่งที่ฉันเห็นที่นี่เพื่อเพิ่มประสิทธิภาพ

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

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

สำหรับ O (n ^ 2) สิ่งที่คุณทำได้คือลดค่าใช้จ่ายในการปฏิเสธสิ่งที่พลาด:

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

2) บางสิ่งที่อาจคุ้มค่าที่จะทำ: แบ่งหน้าจอออกเป็นหลาย ๆ โซน แต่เส้นควรจะเลือน - ลูกที่อยู่บริเวณขอบของโซนแสดงว่าอยู่ในโซนที่เกี่ยวข้องทั้งหมด (อาจเป็น 4) ฉันจะใช้กริด 4x4 จัดเก็บโซนเป็นบิต ถ้า AND ของโซนของสองบอลโซนส่งกลับค่าศูนย์สิ้นสุดการทดสอบ

3) อย่างที่ฉันพูดไปแล้วอย่าทำสแควร์รูท


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

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

1
ฉันไม่เห็นด้วยกับการหาลูกโดดเดี่ยว นั่นแพงมากเท่ากับการตรวจจับการชน เพื่อปรับปรุงสิ่งที่คุณต้องการบางสิ่งบางอย่างที่น้อยกว่า O (n) สำหรับลูกบอลที่สงสัย
Loren Pechtel

7

ฉันพบเพจที่ยอดเยี่ยมพร้อมข้อมูลเกี่ยวกับการตรวจจับการชนและการตอบสนองในแบบ 2D

http://www.metanetsoftware.com/technique.html

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

แก้ไข:ลิงก์ที่อัปเดต


3

คุณมีสองวิธีง่ายๆในการทำเช่นนี้ เจย์ได้ครอบคลุมวิธีการตรวจสอบที่ถูกต้องจากศูนย์กลางของลูกบอล

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

เพิ่มวิธีการในระดับลูกของคุณ:

public Rectangle getBoundingRect()
{
   int ballHeight = (int)Ball.Height * 0.80f;
   int ballWidth = (int)Ball.Width * 0.80f;
   int x = Ball.X - ballWidth / 2;
   int y = Ball.Y - ballHeight / 2;

   return new Rectangle(x,y,ballHeight,ballWidth);
}

จากนั้นในวงของคุณ:

// Checks every ball against every other ball. 
// For best results, split it into quadrants like Ryan suggested. 
// I didn't do that for simplicity here.
for (int i = 0; i < balls.count; i++)
{
    Rectangle r1 = balls[i].getBoundingRect();

    for (int k = 0; k < balls.count; k++)
    {

        if (balls[i] != balls[k])
        {
            Rectangle r2 = balls[k].getBoundingRect();

            if (r1.Intersects(r2))
            {
                 // balls[i] collided with balls[k]
            }
        }
    }
}

1
สิ่งนี้จะทำให้ลูกบอลเข้ากัน 20% จากการชนในแนวนอนและแนวตั้ง อาจใช้กล่องขอบวงกลมเป็นส่วนต่างของประสิทธิภาพเล็กน้อย นอกจากนี้ควรจะเป็น(x-width)/2 x-width/2
Markus Jarderot

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

คุณสามารถทำกล่องสี่เหลี่ยมมุมฉากจากนั้นถ้ามันมีการตรวจสอบตีกล่องวงกลมขอบเขต
Brad Gilbert

1
@ โจนาธานฮอลแลนด์วงในของคุณควรเป็น (int k = i + 1; ... ) นี่จะกำจัดเช็คซ้ำซ้อนทั้งหมด (เช่นการตรวจสอบด้วยการชนกันของตัวเองและการตรวจสอบการชนกันของลูกบอล 1 กับ ball2 แล้ว Ball2 กับ ball1)
mmcdole

4
ที่จริงแล้วกล่องสี่เหลี่ยมจตุรัสน่าจะมีประสิทธิภาพแย่กว่ากล่องขอบวงกลม (สมมติว่าคุณเพิ่มประสิทธิภาพรากที่สองแล้ว)
Ponkadoodle

3

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

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

ใช่มันเป็นการทดสอบสองแบบ แต่มันจะเร็วขึ้นโดยรวม


6
คุณไม่ต้องการตรีโกณมิติ bool is_overlapping(int x1, int y1, int r1, int x2, int y2, int r2) { return (x2-x1)*(x2-x1)+(y2-y1)*(y2-y1)<(r1+r2)*(r1+r2); }
Ponkadoodle


2

ฉันใช้โค้ดนี้ใน JavaScript โดยใช้องค์ประกอบ HTML Canvas และสร้างการจำลองที่ยอดเยี่ยมที่ 60 เฟรมต่อวินาที ฉันเริ่มต้นการจำลองด้วยชุดของลูกบอลโหลที่ตำแหน่งสุ่มและความเร็ว ฉันพบว่าที่ความเร็วสูงกว่าการปะทะกันระหว่างวินาศภัยลูกบอลขนาดเล็กและขนาดใหญ่กว่าหนึ่งเกิดจากลูกเล็กจะปรากฏSTICKไปที่ขอบของลูกที่มีขนาดใหญ่และขยับขึ้นไปอยู่ที่ประมาณ 90 องศารอบลูกบอลขนาดใหญ่ก่อนที่จะแยก (ฉันสงสัยว่ามีคนอื่นสังเกตพฤติกรรมนี้หรือไม่)

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

dot_velocity = ball_1.velocity.dot(ball_2.velocity);
mtd_factor = 1. + 0.5 * Math.abs(dot_velocity * Math.sin(collision_angle));
mtd.multplyScalar(mtd_factor);

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

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


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