ฉันจะใช้แรงโน้มถ่วงได้อย่างไร


44

ฉันจะใช้แรงโน้มถ่วงได้อย่างไร ไม่ใช่สำหรับภาษาใดภาษาหนึ่งโดยเฉพาะ pseudocode ...



2
คำตอบที่ได้รับการยอมรับสำหรับคำถามนี้จะช่วยคุณได้: gamedev.stackexchange.com/questions/13178/…
Tetrad

คำตอบ:


52

ดังที่คนอื่น ๆ ได้ระบุไว้ในความคิดเห็นวิธีการรวมขั้นพื้นฐานของออยเลอร์ที่อธิบายไว้ในคำตอบของ 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 (ซึ่งมีประสิทธิภาพสิ่งที่ฉันใช้ด้านบน) ทำงานได้ดีหรือดีกว่าโดยมีหรือไม่มีการเร่งความเร็วซ้ำ บางทีมันอาจจะเกี่ยวข้องกับความจริงที่ว่ากองกำลังของพวกเขามีส่วนประกอบการเคลื่อนไหวบราวน์แบบสุ่ม


Vellet Verlet เป็นสิ่งที่ดี แต่ไม่สามารถขึ้นอยู่กับความเร็วได้ดังนั้นจึงไม่สามารถใช้แรงเสียดทานได้ ฉันคิดว่า Runge-Kutta 2 ดีที่สุดสำหรับจุดประสงค์ของฉัน;)
Pizzirani Leonardo

1
@PizziraniLeonardo: คุณสามารถใช้ความเร็ว (ตัวแปร) Verlet ได้ดีแม้สำหรับกองกำลังขึ้นอยู่กับความเร็ว; ดูการแก้ไขของฉันด้านบน
Ilmari Karonen

1
วรรณกรรมไม่ได้แปลความหมายของ Velocity Verlet เป็นชื่ออื่น มันอาศัยกลยุทธ์การทำนาย-แก้เป็นยังระบุในบทความนี้fire.nist.gov/bfrlpubs/build99/PDF/b99014.pdf
teodron

3
@ Unit978: ขึ้นอยู่กับเกมและโดยเฉพาะกับโมเดลฟิสิกส์ที่ใช้ force(time, position, velocity)ในคำตอบของฉันข้างต้นเป็นเพียงการจดชวเลขสำหรับ "แรงที่กระทำต่อวัตถุที่positionเคลื่อนที่ด้วยvelocityที่time" โดยปกติแล้วแรงจะขึ้นอยู่กับสิ่งต่าง ๆ เช่นว่าวัตถุอยู่ในเหวหรือนั่งบนพื้นผิวที่เป็นของแข็งไม่ว่าวัตถุอื่น ๆ ที่อยู่ใกล้เคียงกำลังออกแรงบังคับให้มันเคลื่อนที่เร็วแค่ไหนบนพื้นผิว (แรงเสียดทาน) และ / หรือผ่านของเหลว หรือแก๊ส (ลาก) ฯลฯ
Ilmari Karonen

1
นี่คือคำตอบที่ดี แต่ไม่สมบูรณ์โดยไม่ต้องพูดถึงขั้นตอนเวลาที่แน่นอน ( gafferongames.com/game-physics/fix-your-timestep ) ฉันจะเพิ่มคำตอบแยกต่างหาก แต่คนส่วนใหญ่หยุดที่คำตอบที่ยอมรับโดยเฉพาะอย่างยิ่งเมื่อมันได้รับการโหวตมากที่สุดด้วยอัตรากำไรขั้นต้นที่สูงเช่นกรณีที่นี่ ฉันคิดว่าชุมชนจะได้รับบริการที่ดีขึ้นด้วยการเพิ่มชุมชนนี้
Jibb Smart

13

ทุกการอัปเดตวนซ้ำในเกมของคุณให้ทำดังนี้

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;

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

1
ฉันไม่ชอบปิดแรงโน้มถ่วงที่เคย ฉันคิดว่าแรงโน้มถ่วงควรจะคงที่ สิ่งที่ควรเปลี่ยน (imho) คือความสามารถในการกระโดดของคุณ
ultifinitus

2
ถ้ามันช่วยได้ให้คิดว่ามันเป็น 'ล้ม' มากกว่า 'แรงโน้มถ่วง' จริงๆ ฟังก์ชั่นโดยรวมควบคุมว่าวัตถุจะตกลงมาหรือไม่เนื่องจากแรงโน้มถ่วง แรงดึงดูดของโลกนั้นมีอยู่เช่นเดียวกับ [ใส่ค่าความโน้มถ่วงที่นี่] ดังนั้นในแง่นั้นแรงโน้มถ่วงจึงคงที่คุณเพียงแค่ไม่ใช้มันเพื่อสิ่งใดเว้นแต่ว่าวัตถุนั้นอยู่ในอากาศ
Jason Pineo

2
รหัสนี้ขึ้นอยู่กับอัตราเฟรมซึ่งไม่ดี แต่ถ้าคุณมีการปรับปรุงอย่างต่อเนื่องแล้วคุณหัวเราะ
tenpn

1
-1 ขอโทษ การเพิ่มความเร็วและแรงโน้มถ่วงหรือตำแหน่งและความเร็วนั้นไม่สมเหตุสมผล ฉันเข้าใจทางลัดที่คุณกำลังทำอยู่ แต่มันผิด ฉันจะตีนักเรียนหรือผู้ฝึกงานหรือเพื่อนร่วมงานที่ทำอย่างนั้นด้วย cluebat ที่ใหญ่ที่สุดที่ฉันสามารถหาได้ ความสอดคล้องของหน่วยงานมีความสำคัญ
sam hocevar

8

อัตราเฟรมที่เหมาะสมที่เป็นอิสระ * การรวมฟิสิกส์ของนิวตัน:

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สำหรับข้อมูลเพิ่มเติม


4
อย่าใช้การรวมออยเลอร์ ดูบทความนี้โดย Glenn Fiedlerซึ่งอธิบายปัญหาและวิธีแก้ไขปัญหาได้ดีกว่าที่ฉันทำได้ :)
Martin Sojka

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

... ลิงค์ดี ;)
tenpn

4
คุณสามารถแก้ไขปัญหาส่วนใหญ่ด้วยการรวมออยเลอร์ได้ง่ายๆโดยแทนที่position += velocity * timestepด้านบนด้วยposition += (velocity - acceleration * timestep / 2) * timestep(ซึ่งvelocity - acceleration * timestep / 2เป็นเพียงค่าเฉลี่ยของความเร็วเก่าและใหม่) โดยเฉพาะอย่างยิ่งผู้รวบรวมนี้จะให้ผลลัพธ์ที่แน่นอนหากการเร่งความเร็วเป็นค่าคงที่ เพื่อความถูกต้องดีขึ้นภายใต้การเร่งความเร็วที่แตกต่างกันคุณสามารถเพิ่มการแก้ไขที่คล้ายกับการปรับปรุงความเร็วที่จะได้รับความเร็วบูรณาการ Verlet
Ilmari Karonen

ข้อโต้แย้งของคุณสมเหตุสมผลและความไม่ถูกต้องมักไม่ใช่เรื่องใหญ่ แต่คุณไม่ควรอ้างว่ามันเป็นการรวมตัวของ“ อัตราเฟรมอิสระที่เหมาะสม” เพราะมันไม่ใช่ (รวมเป็นอิสระเฟรม)
sam hocevar

3

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

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

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


1

รหัสที่จัดทำโดย 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;

ไชโย


ฉันคิดว่าคุณผิดเพราะความเร่งขึ้นอยู่กับความเร็ว
สุดยอด

-4

ผู้ตอบของ Pecant ละเว้นเวลาเฟรมและนั่นทำให้พฤติกรรมทางฟิสิกส์ของคุณแตกต่างกันเป็นครั้งคราว

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

จะดีกว่าถ้าใช้เครื่องยนต์ฟิสิกส์เช่น physix, ODE และ bullet ใด ๆ ของพวกเขาจะมีเสถียรภาพและมีประสิทธิภาพเพียงพอสำหรับคุณ

http://www.nvidia.com/object/physx_new.html

http://bulletphysics.org/wordpress/


4
-1 การตอบสนองที่ไม่ช่วยเหลือที่ไม่ตอบคำถาม
doppelgreener

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