ปัญหาการชนกันของลูกบอล 2d: ไม่มีการอนุรักษ์พลังงาน

ฉันกำลังพยายามเขียนการจำลองทางฟิสิกส์ง่ายๆ โดยที่ลูกบอลที่มีรัศมีและมวลต่างกันจะกระเด้งไปรอบๆ ในสภาพแวดล้อมที่ยืดหยุ่นและไม่มีแรงเสียดทานอย่างสมบูรณ์แบบ ฉันเขียนโค้ดของตัวเองตามแหล่งข้อมูลนี้: http://www.vobarian.com/collisions/2dcollisions2.pdf และฉันได้ทดสอบโค้ดจากที่นี่ด้วย: Ball to Ball การชน - การตรวจจับและการจัดการ

แก้ไขคำถามแล้ว

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

นี่คืออัลกอริธึมการทำงานที่ฉันเขียน (แม้ว่าฉันจะใช้บิตแรกจากแหล่งอื่นนั้นก็ตาม) มันอยู่ในคลาส Bubble:

public void resolveCollision(Bubble b)
{
    // get the minimum translation distance
    Vector2 delta = (position.subtract(b.position));
    float d = delta.getMagnitude();
    // minimum translation distance to push balls apart after intersecting
    Vector2 mtd = delta.multiply(((getRadius() + b.getRadius())-d)/d); 

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

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

    //get the unit normal and unit tanget vectors
    Vector2 uN = b.position.subtract(this.position).normalize();
    Vector2 uT = new Vector2(-uN.Y, uN.X);

    //project ball 1 & 2 's velocities onto the collision axis
    float v1n = uN.dot(this.velocity);
    float v1t = uT.dot(this.velocity);
    float v2n = uN.dot(b.velocity);
    float v2t = uT.dot(b.velocity);

    //calculate the post collision normal velocities (tangent velocities don't change)
    float v1nPost = (v1n*(this.mass-b.mass) + 2*b.mass*v2n)/(this.mass+b.mass);
    float v2nPost = (v2n*(b.mass-this.mass) + 2*this.mass*v1n)/(this.mass+b.mass);

    //convert scalar velocities to vectors
    Vector2 postV1N = uN.multiply(v1nPost);
    Vector2 postV1T = uT.multiply(v1t);
    Vector2 postV2N = uN.multiply(v2nPost);
    Vector2 postV2T = uT.multiply(v2t);

    //change the balls velocities
    this.velocity = postV1N.add(postV1T);
    b.velocity = postV2N.add(postV2T);
}

และนี่คืออันที่ไม่ได้ผล

public void resolveCollision(Bubble b)
{
    // get the minimum translation distance
    Vector2 delta = (position.subtract(b.position));
    float d = delta.getMagnitude();
    // minimum translation distance to push balls apart after intersecting
    Vector2 mtd = delta.multiply(((getRadius() + b.getRadius())-d)/d); 

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

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

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

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

    // collision impulse (1f is the coefficient of restitution)
    float i = (-(1.0f + 1f) * vn) / (im1 + im2);
    Vector2 impulse = mtd.multiply(i);

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

แจ้งให้เราทราบหากคุณพบสิ่งใด ขอบคุณ


person Cbas    schedule 15.02.2011    source แหล่งที่มา


คำตอบ (3)


มีการพิมพ์ผิดในบรรทัดที่ตั้งค่า v1nPost หรือไม่ ดูเหมือนว่าตัวส่วนควรเป็น this.mass + b.mass ไม่ใช่ this.mass * b.mass

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

person Rick Goldstein    schedule 15.02.2011
comment
ใช่แล้ว พิมพ์ผิด... ฉันคงอยู่หน้าคอมพิวเตอร์นานเกินไป ฉันตรวจคณิตหลายครั้ง ขอบคุณ. ลองดูการแก้ไขของฉัน ฉันต้องการให้อัลกอริทึมอื่นทำงานได้จริง ๆ และแน่นอนว่าไม่ใช่ปัญหาเดียวกัน - person Cbas; 16.02.2011
comment
สำหรับประเด็นที่สองของคุณ ฉันไม่ได้ตรวจสอบเพื่อดูว่าฉันได้แก้ไขการชนกันในลูปผ่านฟองอากาศแล้วหรือไม่ แต่ฉันไม่คิดว่ามันสำคัญเนื่องจากสิ่งแรกที่ฉันทำในวิธี solveCollision นี้คือการดึงพวกมันออกจากกัน เมื่อลูปพบลูกบอลอีกลูกในการชนกัน มันจะไม่ชนกันอีกต่อไป และจะไม่เรียกเมธอดนี้อีก - person Cbas; 16.02.2011
comment
เมื่อคุณคำนวณ mtd.normalize() mtd จะคงอยู่ในสถานะปกติหรือไม่ ถ้าไม่เช่นนั้น ฉันคิดว่าคุณต้องเปลี่ยนการคำนวณจาก impulse เป็น mtd.normalize().multiply(i) นอกเหนือจากนั้น คุณอาจกำลังดูข้อผิดพลาดในการปัดเศษที่สะสม โดยเฉพาะจากการแปลลงในหน้าต่างอ้างอิงที่ b (การคำนวณ 'ความเร็วกระแทก') - person Rick Goldstein; 17.02.2011
comment
แค่นั้นแหละ! ใช้งานได้ดีตอนนี้ ฉันไม่เข้าใจคณิตศาสตร์ทั้งหมดที่อยู่เบื้องหลังสิ่งนี้จริงๆ เนื่องจากฉันเพิ่งดึงมันมาจากแหล่งอื่น ดังนั้นขอบคุณมากที่ช่วยฉันจัดเรียงมัน ฉันจะแน่ใจว่าได้เขียนความคิดเห็นในกระทู้อื่นนั้นเพื่อแจ้งให้ทราบว่ามีข้อผิดพลาดในโค้ด - person Cbas; 17.02.2011
comment
ดีใจที่ได้ช่วย เมื่อก่อนฉันเป็นนักฟิสิกส์ ดังนั้นฉันจึงเคยทำปัญหาการชนกันครั้งหรือสองครั้ง ไชโย! - person Rick Goldstein; 17.02.2011

ฉันเดาก่อน: getMass() ส่งคืนจำนวนเต็ม (หรือ int) (และไม่ใช่ทศนิยมหรือสองเท่า)

หากสิ่งนี้เป็นจริง ปัญหามากกว่าที่คุณคิดคือ 1 / getMass() จะให้ผลลัพธ์เป็นค่าจำนวนเต็ม (และสามารถเป็นได้เพียง 1 หรือเวลาส่วนใหญ่ 0) หากต้องการแก้ไขปัญหานี้ ให้แทนที่ 1 ด้วย 1.0 หรือ 1.0f

เนื่องจากกฎทั่วไปนั้นง่าย: หากคุณมีการดำเนินการทางคณิตศาสตร์ (+,-,*,/) ประเภทผลลัพธ์จะเป็นจำนวนเต็ม หากไม่มีตัวดำเนินการทั้งสองตัวที่เป็นโครงสร้างข้อมูลจุดลอยตัว (สองเท่าหรือลอย)

อย่างไรก็ตาม: อาจมีปัญหาที่สองเกิดขึ้น ขอให้การคำนวณของคุณไม่แม่นยำเพียงพอ จากนั้นคุณควรใช้ double แทน float

person Ralph    schedule 15.02.2011
comment
นั่นเป็นจุดที่ดีมาก getMass() ส่งคืนไบต์จริง ๆ และในโค้ดของฉันจนถึงตอนนี้ ฟองอากาศทั้งหมดมีมวล 1 หากมวลต่างกัน ฉันเห็นว่าฉันจะมีปัญหา จะเปลี่ยนครับ ขอบคุณมากครับ - person Cbas; 16.02.2011

มีส่วนที่ดูแปลกๆ คือ

การคำนวณทั้งสอง:

float v1nPost = (v1n*(this.mass-b.mass) + 2*b.mass*v2n)/(this.mass*b.mass);
float v2nPost = (v2n*(b.mass-this.mass) + 2*this.mass*v1n)/(this.mass+b.mass);

มีความสมมาตร ยกเว้นการดำเนินการครั้งสุดท้าย โดยครั้งแรกคือ * ในวินาทีคือ +

person Ralph    schedule 15.02.2011
comment
แค่นั้นแหละ.. ขอบคุณมาก. ลองดูการแก้ไขของฉัน ตอนนี้ฉันกำลังพยายามทำให้อัลกอริทึมอื่นทำงานได้ - person Cbas; 16.02.2011