ฉันจะใช้แรงโน้มถ่วงได้อย่างไร ไม่ใช่สำหรับภาษาใดภาษาหนึ่งโดยเฉพาะ pseudocode ...
ฉันจะใช้แรงโน้มถ่วงได้อย่างไร ไม่ใช่สำหรับภาษาใดภาษาหนึ่งโดยเฉพาะ pseudocode ...
คำตอบ:
ดังที่คนอื่น ๆ ได้ระบุไว้ในความคิดเห็นวิธีการรวมขั้นพื้นฐานของออยเลอร์ที่อธิบายไว้ในคำตอบของ tenpn ได้รับผลกระทบจากปัญหาเล็กน้อย:
แม้สำหรับการเคลื่อนไหวอย่างง่ายเช่นการกระโดดด้วยขีปนาวุธภายใต้แรงโน้มถ่วงคงที่มันก็จะแนะนำระบบที่ผิดพลาด
ข้อผิดพลาดขึ้นอยู่กับการประทับเวลาซึ่งหมายความว่าการเปลี่ยนการประทับเวลาจะเปลี่ยนเส้นทางการเคลื่อนที่ของวัตถุด้วยวิธีที่เป็นระบบซึ่งอาจสังเกตได้โดยผู้เล่นหากเกมใช้การประทับเวลาแบบแปรผัน แม้แต่เกมที่มีการกำหนดเวลาแบบฟิสิกส์คงที่การเปลี่ยนการประทับเวลาในระหว่างการพัฒนาอาจส่งผลกระทบต่อฟิสิกส์ของเกมอย่างชัดเจนเช่นระยะทางที่วัตถุที่เปิดตัวด้วยแรงที่กำหนดจะโบยบินไป
มันไม่อนุรักษ์พลังงานแม้ว่าฟิสิกส์พื้นฐานควร โดยเฉพาะอย่างยิ่งวัตถุที่ควรแกว่งไปมาอย่างมั่นคง (เช่นลูกตุ้ม, สปริง, ดาวเคราะห์ที่โคจรรอบ ฯลฯ ) อาจสะสมพลังงานอย่างต่อเนื่องจนกว่าระบบทั้งหมดจะระเบิดออกจากกัน
โชคดีที่มันไม่ยากที่จะบูรณาการแทนที่ออยเลอร์กับสิ่งที่เกือบจะเป็นที่เรียบง่าย แต่มีไม่มีปัญหาเหล่านี้ - โดยเฉพาะที่สองเพื่อ symplectic บูรณาการเช่นการรวมเกมเสือข้ามห้วยหรือที่เกี่ยวข้องอย่างใกล้ชิดความเร็ววิธี Verlet โดยเฉพาะอย่างยิ่งที่การรวมออยเลอร์ขั้นพื้นฐานจะอัปเดตความเร็วและตำแหน่งเป็น:
ความเร่ง = แรง (เวลาตำแหน่ง) / มวล เวลา + = การประทับเวลา; ตำแหน่ง + = การประทับเวลา * ความเร็ว; velocity + = timestep * การเร่งความเร็ว;
วิธี vellet Verlet ใช้วิธีนี้:
ความเร่ง = แรง (เวลาตำแหน่ง) / มวล เวลา + = การประทับเวลา; ตำแหน่ง + = การประทับเวลา * (ความเร็ว+ การประทับเวลา * การเร่งความเร็ว / 2) ; newAcceleration = แรง (เวลา, ตำแหน่ง) / มวล; velocity + = timestep * (การเร่งความเร็ว+ newAcceleration) / 2 ;
หากคุณมีวัตถุที่มีปฏิสัมพันธ์หลายตัวคุณควรอัปเดตตำแหน่งทั้งหมดก่อนที่จะคำนวณกองกำลังและอัปเดตความเร็ว การเร่งความเร็วใหม่นั้นจะสามารถบันทึกและใช้เพื่ออัปเดตตำแหน่งในการประทับเวลาครั้งต่อไปลดจำนวนการโทรforce()
ลงเหลือหนึ่ง (ต่อวัตถุ) ต่อการประทับเวลาเช่นเดียวกับวิธีการออยเลอร์
นอกจากนี้หากการเร่งความเร็วเป็นค่าคงที่ (เช่นแรงโน้มถ่วงในระหว่างการกระโดดด้วยขีปนาวุธ) เราสามารถลดความซับซ้อนของด้านบนเป็นเพียง:
เวลา + = การประทับเวลา; ตำแหน่ง + = การประทับเวลา * (ความเร็ว+ การประทับเวลา * การเร่งความเร็ว / 2) ; velocity + = timestep * การเร่งความเร็ว;
โดยที่คำพิเศษเป็นตัวหนานั้นเป็นการเปลี่ยนแปลงเพียงอย่างเดียวเมื่อเทียบกับการรวมออยเลอร์ขั้นพื้นฐาน
เปรียบเทียบกับการรวมออยเลอร์วิธีการความเร็ว Verlet และ leapfrog มีคุณสมบัติที่ดีหลายประการ:
สำหรับการเร่งความเร็วคงที่พวกเขาให้ผลลัพธ์ที่แน่นอน (มากถึงข้อผิดพลาดจุดลอยตัว roundoff อย่างไรก็ตาม) หมายความว่าวิถีกระสุน ballistic jump ยังคงเหมือนเดิมแม้ว่าเวลาจะเปลี่ยนไปก็ตาม
พวกเขาเป็นผู้รวบรวมลำดับที่สองซึ่งหมายความว่าแม้จะมีการเร่งความเร็วที่แตกต่างกันข้อผิดพลาดการรวมเฉลี่ยก็เป็นเพียงสัดส่วนกับกำลังสองของการประทับเวลา วิธีนี้ช่วยให้สามารถตั้งเวลาได้มากขึ้นโดยไม่ลดความแม่นยำลง
พวกมันเป็นsymplecticซึ่งหมายความว่าพวกมันจะอนุรักษ์พลังงานถ้าฟิสิกส์พื้นฐานทำ (อย่างน้อยตราบใดที่การประทับเวลาคงที่) โดยเฉพาะอย่างยิ่งนี่หมายความว่าคุณจะไม่ได้สิ่งต่าง ๆ เช่นดาวเคราะห์ที่บินออกจากวงโคจรของตนเองหรือวัตถุที่ยึดติดกันด้วยสปริงค่อย ๆ โยกเยกมากขึ้นเรื่อย ๆ จนกระทั่งสิ่งทั้งปวงระเบิดขึ้น
แต่ความเร็ว Verlet วิธีการ / เสือข้ามห้วยเกือบเป็นง่ายๆและรวดเร็วเป็นการบูรณาการออยเลอร์ขั้นพื้นฐานและแน่นอนมากง่ายกว่าทางเลือกเช่นสี่เพื่อบูรณาการ Runge-Kutta (ซึ่งในขณะที่โดยทั่วไปเป็นบูรณาการที่ดีมากขาดคุณสมบัติ symplectic และต้องสี่การประเมินผล ของforce()
ฟังก์ชั่นต่อขั้นตอนเวลา) ดังนั้นฉันขอแนะนำให้พวกเขาสำหรับทุกคนที่เขียนโค้ดเกมฟิสิกส์แม้ว่ามันจะง่ายเหมือนการกระโดดจากแพลตฟอร์มหนึ่งไปอีกแพลตฟอร์มหนึ่ง
แก้ไข:ในขณะที่มาอย่างเป็นทางการของความเร็ววิธีการ Verlet จะใช้ได้เฉพาะเมื่อกองกำลังมีความเป็นอิสระของความเร็วในทางปฏิบัติคุณสามารถใช้มันได้ดีแม้จะมีกองกำลังความเร็วขึ้นเช่นการลากของเหลว เพื่อผลลัพธ์ที่ดีที่สุดคุณควรใช้ค่าความเร่งเริ่มต้นเพื่อประมาณความเร็วใหม่สำหรับการเรียกครั้งที่สองforce()
ดังนี้:
ความเร่ง = แรง (เวลา, ตำแหน่ง, ความเร็ว) / มวล เวลา + = การประทับเวลา; ตำแหน่ง + = การประทับเวลา * (ความเร็ว+ การประทับเวลา * การเร่งความเร็ว / 2) ; velocity + = timestep * การเร่งความเร็ว; newAcceleration = แรง (เวลา, ตำแหน่ง, ความเร็ว) / มวล velocity + = timestep * (newAcceleration - ความเร่ง) / 2 ;
ฉันไม่แน่ใจว่าวิธีแปรผันความเร็วพิเศษนี้มีชื่อเฉพาะหรือไม่ แต่ฉันได้ทดสอบแล้วและดูเหมือนว่าจะทำงานได้ดีมาก มันไม่แม่นยำเท่า Runge-Kutta (เท่าที่คาดหวังจากวิธีการอันดับสอง) แต่ดีกว่า Euler หรือnaïve vellet Verlet โดยไม่ต้องประมาณความเร็วปานกลางและยังคงคุณสมบัติ symplectic ของปกติ vellet Verlet สำหรับแรงอนุรักษ์ที่ไม่ขึ้นอยู่กับความเร็ว
แก้ไข 2:อัลกอริทึมที่คล้ายกันมากอธิบายโดยGroot & Warren ( J. Chem. Phys. 1997)แม้ว่าการอ่านระหว่างบรรทัดดูเหมือนว่าพวกเขาเสียสละความถูกต้องบางอย่างสำหรับความเร็วพิเศษโดยการบันทึกnewAcceleration
ค่าที่คำนวณโดยใช้ความเร็วโดยประมาณ และนำมันกลับมาใช้ใหม่acceleration
สำหรับการประทับเวลาครั้งต่อไป พวกเขายังแนะนำพารามิเตอร์ 0 ≤λ≤ 1 ซึ่งคูณด้วยacceleration
ในการประมาณความเร็วเริ่มต้น ด้วยเหตุผลบางอย่างพวกเขาแนะนำλ = 0.5 แม้ว่าการทดสอบทั้งหมดของฉันแนะนำว่าλ= 1 (ซึ่งมีประสิทธิภาพสิ่งที่ฉันใช้ด้านบน) ทำงานได้ดีหรือดีกว่าโดยมีหรือไม่มีการเร่งความเร็วซ้ำ บางทีมันอาจจะเกี่ยวข้องกับความจริงที่ว่ากองกำลังของพวกเขามีส่วนประกอบการเคลื่อนไหวบราวน์แบบสุ่ม
force(time, position, velocity)
ในคำตอบของฉันข้างต้นเป็นเพียงการจดชวเลขสำหรับ "แรงที่กระทำต่อวัตถุที่position
เคลื่อนที่ด้วยvelocity
ที่time
" โดยปกติแล้วแรงจะขึ้นอยู่กับสิ่งต่าง ๆ เช่นว่าวัตถุอยู่ในเหวหรือนั่งบนพื้นผิวที่เป็นของแข็งไม่ว่าวัตถุอื่น ๆ ที่อยู่ใกล้เคียงกำลังออกแรงบังคับให้มันเคลื่อนที่เร็วแค่ไหนบนพื้นผิว (แรงเสียดทาน) และ / หรือผ่านของเหลว หรือแก๊ส (ลาก) ฯลฯ
ทุกการอัปเดตวนซ้ำในเกมของคุณให้ทำดังนี้
if (collidingBelow())
gravity = 0;
else gravity = [insert gravity value here];
velocity.y += gravity;
ตัวอย่างเช่นใน platformer เมื่อคุณกระโดดแรงโน้มถ่วงจะเปิดใช้งาน (collidingBelow จะบอกคุณว่ามีพื้นดินอยู่ด้านล่างหรือไม่) และเมื่อคุณกระแทกพื้นดินมันจะถูกปิดการใช้งาน
นอกจากนี้เพื่อใช้การข้ามจากนั้นทำสิ่งนี้:
if (pressingJumpButton() && collidingBelow())
velocity.y = [insert jump speed here]; // the jump speed should be negative
และแน่นอนว่าในการอัปเดตลูปคุณต้องอัปเดตตำแหน่งของคุณด้วย:
position += velocity;
อัตราเฟรมที่เหมาะสมที่เป็นอิสระ * การรวมฟิสิกส์ของนิวตัน:
Vector forces = 0.0f;
// gravity
forces += down * m_gravityConstant; // 9.8m/s/s on earth
// left/right movement
forces += right * m_movementConstant * controlInput; // where input is scaled -1..1
// add other forces in for taste - usual suspects include air resistence
// proportional to the square of velocity, against the direction of movement.
// this has the effect of capping max speed.
Vector acceleration = forces / m_massConstant;
m_velocity += acceleration * timeStep;
m_position += velocity * timeStep;
ปรับแต่งแรงโน้มถ่วงคงเคลื่อนไหวและมวลคงที่จนกว่าจะรู้สึกถูกต้อง มันเป็นสิ่งที่ใช้งานง่ายและสามารถใช้เวลาสักครู่เพื่อรับความรู้สึกที่ดี
มันง่ายที่จะขยายเวกเตอร์แรงเพื่อเพิ่มรูปแบบการเล่นใหม่ - ตัวอย่างเช่นเพิ่มแรงจากการระเบิดบริเวณใกล้เคียงหรือไปสู่หลุมดำ
* แก้ไข: ผลลัพธ์เหล่านี้จะผิดตลอดเวลา แต่อาจ "ดีพอ" สำหรับความซื่อสัตย์หรือความถนัดของคุณ ดูลิงค์นี้http://lol.zoy.org/blog/2011/12/14/understanding-motion-in-gamesสำหรับข้อมูลเพิ่มเติม
position += velocity * timestep
ด้านบนด้วยposition += (velocity - acceleration * timestep / 2) * timestep
(ซึ่งvelocity - acceleration * timestep / 2
เป็นเพียงค่าเฉลี่ยของความเร็วเก่าและใหม่) โดยเฉพาะอย่างยิ่งผู้รวบรวมนี้จะให้ผลลัพธ์ที่แน่นอนหากการเร่งความเร็วเป็นค่าคงที่ เพื่อความถูกต้องดีขึ้นภายใต้การเร่งความเร็วที่แตกต่างกันคุณสามารถเพิ่มการแก้ไขที่คล้ายกับการปรับปรุงความเร็วที่จะได้รับความเร็วบูรณาการ Verlet
หากคุณต้องการใช้แรงโน้มถ่วงในระดับที่ใหญ่ขึ้นเล็กน้อยคุณสามารถใช้การคำนวณแบบนี้ในแต่ละลูป:
for each object in the scene
for each other_object in the scene not equal to object
if object.mass * other_object.mass / object.distanceSquaredBetweenCenterOfMasses(other_object) < epsilon
abort the calculation for this pair
if object.mass is much, much bigger than other_object.mass
abort the calculation for this pair
force = gravitational_constant
* object.mass * other_object.mass
/ object.distanceSquaredBetweenCenterOfMasses(other_object)
object.addForceAtCenterOfMass(force * object.normalizedDirectionalVectorTo(other_object))
end for loop
end for loop
สำหรับเครื่องชั่งขนาดใหญ่กว่ากาแลคซีแรงโน้มถ่วงเพียงอย่างเดียวไม่เพียงพอที่จะสร้างการเคลื่อนไหว "ของจริง" ได้ การทำงานร่วมกันของระบบดาวนั้นมีความสำคัญและชัดเจนมากโดยสมการเนเวียร์สโตกส์สำหรับการเคลื่อนที่ของของไหลและคุณจะต้องรักษาความเร็วของแสงที่ จำกัด ซึ่งก็คือแรงโน้มถ่วงด้วย
รหัสที่จัดทำโดย Ilmari Karonen นั้นเกือบจะถูกต้อง แต่มีข้อผิดพลาดเล็กน้อย คุณคำนวณการเร่งความเร็ว 2 ครั้งต่อหนึ่งขีดซึ่งไม่เป็นไปตามสมการตำรา
acceleration = force(time, position) / mass; // Here
time += timestep;
position += timestep * (velocity + timestep * acceleration / 2);
newAcceleration = force(time, position) / mass;
velocity += timestep * (acceleration + newAcceleration) / 2;
mod ต่อไปนี้ถูกต้อง:
time += timestep;
position += timestep * (velocity + timestep * acceleration / 2);
oldAcceletation = acceleration; // Store it
acceleration = force(time, position) / mass;
velocity += timestep * (acceleration + oldAcceleration) / 2;
ไชโย
ผู้ตอบของ Pecant ละเว้นเวลาเฟรมและนั่นทำให้พฤติกรรมทางฟิสิกส์ของคุณแตกต่างกันเป็นครั้งคราว
หากคุณกำลังจะสร้างเกมที่ง่ายมากคุณสามารถสร้างเอนจิ้นฟิสิกส์เล็ก ๆ น้อย ๆ ของคุณเอง - กำหนดค่ามวลและพารามิเตอร์ทางฟิสิกส์ทุกชนิดสำหรับวัตถุที่เคลื่อนไหวทุกตัวและทำการตรวจจับการชนจากนั้นอัปเดตตำแหน่งและความเร็วของทุกเฟรม ในการเร่งความคืบหน้านี้คุณต้องลดความซับซ้อนของการชนลดการเรียกการตรวจจับการชน ฯลฯ ในกรณีส่วนใหญ่นั่นคือความเจ็บปวด
จะดีกว่าถ้าใช้เครื่องยนต์ฟิสิกส์เช่น physix, ODE และ bullet ใด ๆ ของพวกเขาจะมีเสถียรภาพและมีประสิทธิภาพเพียงพอสำหรับคุณ