برمجة تغيير سلس للدفع من متجه السرعة الحالي إلى متجه مستهدف

StackOverflow https://stackoverflow.com/questions/1365415

سؤال

TL ؛ DR: "لست متأكدًا من كيفية حساب انتقال سلس من التوجه بين ناقل واحد وآخر."

أقوم ببرمجة لعبة بسيطة حيث يطارد العدو بعد اللاعب في مساحة مفتوحة (بدون جدران). كنت أحسب سرعات X&Y للعدو بشكل مستقل ، وأسرعها إذا كانوا يأخذونهم في اتجاه اللاعب وسرعان ما يبطئونهم إذا كانوا في الاتجاه الخطأ (على سبيل المثال العدو ، ثم العدو

في حين أن اللعبة ممتعة بشكل لائق في محاولة لتفادي العدو ، فإن رغبتي في أن يكون العدو يتصرف باستخدام الفيزياء المناسبة. ما أقوم به حاليًا هو أن يقوم العدو بتعيين دفعه (فكر في سفينة فضاء) استنادًا إلى الزاوية بينهم وبين اللاعب ، وتسريع دفعه إلى سرعة الحد الأقصى (حساب الجانب C من مثلث العدو). بمجرد حدوث ذلك ، لست متأكدًا من أفضل طريقة لضبط الدفع نفسه. إذا لم أترك أي سرعة ماكس ، فإن العدو يتسارع بشكل جيد ولكنه سيتجاوز اللاعب بسهولة ، ثم يستغرق وقتًا طويلاً للحصول على زخم كافٍ في اتجاه اللاعب.

ما أرغب في حدوثه هو جعل العدو يعدل باستمرار سرعته في الطريق إلى اللاعب ، ويستهدف أينما كانوا (لا أريدهم أن يتنبأوا بمكان وجودك). بعد ذلك ، عندما يفوتون اللاعب ، أود أن يعيد صياغة الدفع والتسارع نفسه سرعتهم وإرسالها مرة أخرى إلى اللاعب.

أظن أن هذا سيشمل متجهين: أحدهما يسافر فيه العدو حاليًا ، وواحد حيث يريد العدو السفر (المتجه الذي سيأخذهم مباشرة إلى اللاعب). لست متأكدًا من كيفية حساب انتقال سلس من التوجه بين ناقل واحد وآخر.

أي نصائح أو صيغ أو أسئلة ستكون موضع تقدير كبير! شكرا لك مكدس فائض.

هل كانت مفيدة؟

المحلول

كل ذلك يعود إلى معادلات نيوتن:

F = m * a
s = s_o + v * t + a * t^2 / 2
v = v_o + a * t

في هذه الحالة F هي القوة (الدفع) ، a هو التسارع ، و m هي كتلة السفينة. s هو الموقع الحالي ، s_o هو الموقع الأصلي ، v هي السرعة ، و t هو الوقت الحالي.

بالطبع هذا على طول خط مستقيم ، لذلك إذا كنت ترغب في التحويل إلى بعدين أو ثلاثة أبعاد ، فسوف يتعين عليك القيام ببعض الرياضيات. F, s, v, ، و a كلها متجهات ، وهذا يعني أن اتجاهها مهم بنفس القدر. من الناحية الفنية t هو أيضًا متجه ، ولكن بما أن الوقت يذهب بشكل عام فقط اتجاه واحد ، لا داعي للقلق بشأن ذلك.

2d:
F^2 = F_x^2 + F_y^2 (use Pythagorean theorem to split force into components)
F_x = m * a_x
F_y = m * a_y
s_x = s_o_x + v_x * t + a_x * t^2 / 2
s_y = s_o_y + v_y * t + a_y * t^2 / 2
v_x = v_o_x + a_x * t
v_y = v_o_y + a_y * t

3d:
F^2 = F_x^2 + F_y^2 + F_z^2 (surprisingly, this works)
F_x = m * a_x
F_y = m * a_y
F_z = m * a_z
s_x = s_o_x + v_x * t + a_x * t^2 / 2
s_y = s_o_y + v_y * t + a_y * t^2 / 2
s_z = s_o_z + v_z * t + a_z * t^2 / 2
v_x = v_o_x + a_x * t
v_y = v_o_y + a_y * t
v_z = v_o_z + a_z * t

الآن لضبط سرعتك في اتجاه اللاعب ، لديك قوة كاملة ثابتة (F) من أجل تغيير سرعتك الحالية تجاه اللاعب. في الفيزياء ، لا تحدث الأمور على الفور ، ولكن يجب أن يكون هدفك هو تقليل الوقت الذي يحدث فيه التغيير ("T").

هذا يمنحك معادلة من حيث موقعك الحالي ((s_o_x,s_o_y) أو (s_o_x,s_o_y,s_o_z)) وموقع خصمك الحالي أو موقعك المستهدف ((s_x,s_y) أو (s_x,s_y,s_z)) ، للسرعة المستهدفة الخاصة بك (يتجاهل التسارع).

v_x = (s_x - s_o_x) / t
v_y = (s_y - s_o_y) / t

v_x = (s_x - s_o_x) / t
v_y = (s_y - s_o_y) / t
v_z = (s_z - z_o_y) / t

يمكننا استبدال هذا بمعادلةنا الأخرى:

(s_x - s_o_x) / t = v_o_x + a_x * t
(s_y - s_o_y) / t = v_o_y + a_y * t

(s_x - s_o_x) / t = v_o_x + a_x * t
(s_y - s_o_y) / t = v_o_y + a_y * t
(s_z - z_o_y) / t = v_o_z + a_z * t

ثم نقوم بحل التسارع (وهذا مرتبط بالقوة ، وهو ما نحاول حسابه).

(s_x - s_o_x) / t^2 - v_o_x / t = a_x
(s_y - s_o_y) / t^2 - v_o_y / t = a_y

(s_x - s_o_x) / t^2 - v_o_x / t = a_x
(s_y - s_o_y) / t^2 - v_o_y / t = a_y
(s_z - z_o_y) / t^2 - v_o_z / t = a_z

قم بتوصيل هذا في معادلة القوة:

F_x = m * (s_x - s_o_x) / t^2 - m * v_o_x / t
F_y = m * (s_y - s_o_y) / t^2 - m * v_o_y / t

F_x = m * (s_x - s_o_x) / t^2 - m * v_o_x / t
F_y = m * (s_y - s_o_y) / t^2 - m * v_o_y / t
F_z = m * (s_z - z_o_y) / t^2 - m * v_o_z / t

الآن حل ل t:

t = (-m * v_o_x +/- sqrt(m^2 * v_o_x^2 - 4 * F_x * m * (s_x - s_o_x))) / 2 / F_x
t = (-m * v_o_y +/- sqrt(m^2 * v_o_y^2 - 4 * F_y * m * (s_y - s_o_y))) / 2 / F_y

t = (-m * v_o_x +/- sqrt(m^2 * v_o_x^2 - 4 * F_x * m * (s_x - s_o_x))) / 2 / F_x
t = (-m * v_o_y +/- sqrt(m^2 * v_o_y^2 - 4 * F_y * m * (s_y - s_o_y))) / 2 / F_y
t = (-m * v_o_z +/- sqrt(m^2 * v_o_z^2 - 4 * F_z * m * (s_z - s_o_z))) / 2 / F_z

يجب أن تتقارب الأوقات ، لذلك ستكون الأوقات متساوية! هذا يعطينا نظام المعادلات لكل إحداثيات (الطائرة والكوال). لاحظ أن هناك قيمًا محتملة متعددة ، لكن بعضها ينطوي على أرقام وهمية ، لذا سيتعين عليك القضاء على هذه الحلول:

(-m * v_o_x +/- sqrt(m^2 * v_o_x^2 - 4 * F_x * m * (s_x - s_o_x))) / 2 / F_x
= (-m * v_o_y +/- sqrt(m^2 * v_o_y^2 - 4 * F_y * m * (s_y - s_o_y))) / 2 / F_y
F^2 = F_x^2 + F_y^2

(-m * v_o_x +/- sqrt(m^2 * v_o_x^2 - 4 * F_x * m * (s_x - s_o_x))) / 2 / F_x
= (-m * v_o_y +/- sqrt(m^2 * v_o_y^2 - 4 * F_y * m * (s_y - s_o_y))) / 2 / F_y
= (-m * v_o_z +/- sqrt(m^2 * v_o_z^2 - 4 * F_z * m * (s_z - s_o_z))) / 2 / F_z
F^2 = F_x^2 + F_y^2 + F_z^2

حل ل (F_x,F_y) أو (F_x,F_y,F_z) الإحداثيات وستحصل على القوة التي تحتاجها.

اسمحوا لي أن أعرف إذا كان لديك أي أسئلة أو إذا وجدت أخطاء في الرياضيات الخاصة بي.

نصائح أخرى

قد تحصل على التأثير الذي تريده من خلال ضمان تغيير سلس في ● السرعة, ، عوضا عن دفع. وبهذه الطريقة ، إذا كان العدو يتجاوز اللاعب ، فيمكنه عكس تسارعه على الفور ، مما سيؤدي إلى إبطاءه ويعكس اتجاه السفر في النهاية.

يمكنك تحقيق ذلك عن طريق تغيير السرعة أثناء كل تكرار ، بمقدار صغير يعتمد على المسافة من العدو إلى اللاعب:

while (game_in_progress)
{
    // Distance from enemy to player.  The larger the
    // distance, the greater the acceleration will be.
    delta.x = player.x - enemy.x
    delta.y = player.y - enemy.y

    // Accelerate by changing velocity based on distance,
    // where 'scale' is sufficiently small. (Limit v to
    // some maximum if you choose; likely to be unnecessary.)
    v.x += delta.x * scale
    v.y += delta.y * scale

    // Update the enemy's position.
    enemy.x += v.x
    enemy.y += v.y
}

عن طريق حساب x و y القيم بشكل مستقل ، يمكنك أن تنقذ نفسك صداع التعامل مع المتجهات والزوايا والمواوات المتزامنة.

وبالمثل ، من خلال إدراك أن التسارع (الدفع) هو مجرد تغيير في السرعة ، وهو ما بدوره هو تغيير في الموضع ، يمكنك إنشاء محاكاة وقت منفصل باستخدام الجبر البسيط فقط بدلاً من حساب التفاضل والتكامل.

استمتع!

تحتاج إلى التفكير في شروط الفيزياء المناسبة. لديك سرعة ، وتريد إضافة تسارع. هذا كل ما في الأمر - التسارع هو تغيير تدريجي في السرعة التي ستجذب العدو نحو اللاعب ، والسماح له بالتجاوز ، والتباطؤ (أو الدوران) ثم العودة نحو اللاعب.

يتم قياس التسارع على أنه D (السرعة)/الوقت. تريد أن تتسارع نحو اللاعب في أي وقت من الأوقات ، لذلك كل فاصل (ثانية ، مائة من الثانية أو أي شيء تختاره) تحتاج إلى إضافة المتجه بين العدو واللاعب ، مضروب من قبل بعض ، إلى سرعتك.

Velocity = Velocity + c * (Player-Enemy vector)

سيعتمد Constant C على مدى السرعة التي تريد تسريعها نحو المشغل ، وعدد المرات التي تقوم فيها بتحديث سرعتك.

إذا كنت ترغب في "الحد الأقصى لسرعة العدو" حتى لا تستمر في زيادة حجم سرعته إلى أجل غير مسمى ، فيمكنك أيضًا القيام بذلك.

Velocity = Velocity * (Maximum magniture / |Velocity|)

تحرير: لتوضيح المزيد ، فإن إضافة السرعة تعني ببساطة إضافة متجهات المكون. لذا

Vx = Vx + c * Ax
Vy = Vy + c * Ay

حيث v هي السرعة و A هي تسارع. يتم قياس الحجم على أنه sqrt(Vx^2 + Vy^2), ، أي انتقالي من المثلث الأيمن. لذلك إذا كنت تريد أن تكون السرعة القصوى للعدو م ،

Vx = Vx * ( m / sqrt(Vx^2 + Vy^2)
Vy = Vy * ( m / sqrt(Vx^2 + Vy^2)

لقد كتبت لعبة كويكبات بسيطة منذ فترة كانت لها سفينة "حليف" من شأنها أن تصطاد الكويكبات وتطلق النار عليك. في الأساس ، وجدت أقرب كويكب ثم بدأت تتحول بسلاسة نحوه وتلأى ذلك. للأسف ، لم أعد أمتلك الرمز بعد الآن ، لكن إذا كانت الذاكرة تخدم ، فأعتقد أنني كنت أتعامل مع السفينة قليلاً في كل منعطف ، ثم إذا كان الكويكب بعيدًا ، فقد تسارعت ، لكن إذا كانت قريبة ، فقد حاولت مطابقة سرعة الكويكب. كان رائعا في الواقع ، والحد الأدنى من الجبر المعنية.

أفضل طريقة للقيام بذلك هي أخذ قيمتين من الراديان و LERP بينهما ، والتعامل مع التفاف. (ربما عن طريق إضافة أو طرح 2PI عند الضرورة). ثم تحويله إلى ناقل وحدة. ثم اضرب ذلك بالسرعة التي تريد أن تتسارعها السفينة ، وهناك تذهب!

تتمثل إحدى الطرق البسيطة (وليس الفيزياء المناسبة) في حساب "السرعة المطلوبة" للعدو الخاص بك ، ثم ضبط سرعة العدو حاليًا تجاه ذلك ، مع الأخذ في الاعتبار أي حدود في الأعلى ، أو الحد الأدنى للسرعة التي يتمتع بها.

على سبيل المثال ، في لعبة ثنائية الأبعاد كتبت (http://wordwarvi.sourceforge.net) هناك "الصواريخ البحث عن الحرارة". يبدو غريبًا جدًا إذا توقفت الصواريخ في الجو لتستدير. إذن ما فعلته هو على النحو التالي: أحسب "السرعة المطلوبة" التي تتجه نحو اللاعب. يتم ذلك للتو من خلال "مثلثات مماثلة". أجد المسافة إلى اللاعب في X ، وفي Y ، وأكثر من ذلك ، أقوم بعمل "السرعة المطلوبة (x أو y) تكون الحد الأقصى الممكن ، ثم قم بتوسيع نطاق آخر لتناسب" المثلث المماثل ". ملاحظة. ، هذه مجرد "السرعة المطلوبة" ، وليس السرعة الحالية. أتناول السرعة الحالية وضبطها ببطء (بقليل لكل إطار) نحو السرعة "المطلوبة" (على الرغم من أن السرعة المطلوبة يتم إعادة حسابها لكل إطار أيضًا ،) Mindimg الحد الأدنى على VX و VY لمنعهم من وقف الهواء.

خوارزمية غبية ، لكنها تعمل بشكل جيد (لا أحد يشتكي من أنها سهلة للغاية أو صعبة للغاية أو غير واقعية للغاية.)

تحرير: عند إعادة قراءة السؤال ، ربما لا تكون إجابتي ما بعد ذلك.

لقد قمت بحل مشكلات مثل هذا بشكل احترافي ، وأنصحك بالبدء بالإصدارات البسيطة والعمل. تأكد من حصولك على السلوك المتوقع في كل خطوة قبل تجربة التالي.

  1. يسعى الباحث عن هدف ثابت في الأصل في بُعد واحد. هذا صحيح ، بعد واحد. يمكن أن يدفع ذهابًا وإيابًا على المحور السيني ويحاول الوصول إلى x = 0. لا يوجد لديه خانق (مثل صاروخ صلب) ولكن يمكن للباحث توجيهه في أي من الاتجاهين. إذا قمت برمجة هذا بشكل صحيح ، فسيتأرجح الباحث حول x = 0 ، ويتجاوز كل مرة.
  2. الشيء نفسه ، ولكن الهدف ثابت في مكان آخر غير x = 0. فقط اجعل x نسبيًا ، وليس مطلقًا (أي أن الباحث يهتم ب فرق في x ، وليس الهدف x).
  3. الآن الهدف يتحرك (أو القفز). يجب أن يكون الباحث قادرًا على متابعته. سوف ينمو التذبذب أو يتقلص اعتمادًا على كيفية تحرك الهدف - سترى ما أعنيه.
  4. الآن بعدين. يدفع الباحث دائمًا مباشرة نحو الهدف ، مما يعني أنه يجب عليك تقسيم التوجه إلى مكونات X و Y بواسطة Trig البسيطة. إذا قمت بنقل الهدف ، فقد يذهب الباحث إلى المدار حوله.
  5. العودة إلى بُعد واحد وهدف ثابت ، لكن الباحث الآن يحاول إرجاعًا ، وليس ذبابة. هذا هو الجزء الصعب. والهدف من ذلك هو أن تصبح المسافة والسرعة صفرًا في نفس الوقت ، ولا يوجد تجاوز ، لذلك يجب أن يعرف الباحث قدرته على الكبح. عندما يكون x أقل من v^2/2a ، يجب أن يعكس الباحث التوجه ، ويتوجه بعيدا من الهدف من أجل إبطاء وتلبية. من الجيد السماح للباحث بالتوقف عن الدفع عندما يكون قريبًا جدًا من الهدف.
  6. يبدأ الهدف في التحرك مرة أخرى. هذا سهل. فقط اجعل x و v نسبيًا ، وليس مطلقًا.
  7. أبعاد متعددة. هذا سهل بشكل ملحوظ. أجزاء X و Y و Z مستقلة.

الآن إذا كنت تريد طالبًا لا يمكن أن يدير عشرة سنتات ، ولكن يجب منحنى حولها ، تصبح الأمور صعبة ...

هناك فقط عدد قليل من المؤشرات للحصول على هذا صحيح وسهل. 1) من الأسهل والأكثر عمومية العمل مع المتجهات بدلاً من كتابة كل شيء مرتين أو ثلاث مرات. 2) ستبدو الأمور بشكل صحيح إذا كنت تتحكم في القوة (التي هي بالتسارع بشكل فعال منذ A = f/mass) ثم تطور السرعة والموضع ديناميكيًا.

تبدو حلقةك الأساسية للحركة الواقعية (حيث تكون القبعات متجهات و DT هي Timestep):

while (motion) {
   A = get_acceleration(X, V, A, X_target, V_targer, A_target)
   V += A*dt       // V is the integral of A
   X += V*dt       // X is the integral of V
}

وحقا ، هذا يتعلق بذلك لأنك تطور ديناميكي.

ثم تحتاج إلى تحديد كيفية تحديد تسارعك ، أي الكتابة get_acceleration. هناك عدد من الخيارات هنا التي تعتمد على عوامل متعددة ، ويستخدم المطاردون في الحياة الواقعية استراتيجيات متعددة. على سبيل المثال ، إذا كان لديك الكثير من الدفع بالنسبة إلى كتلتك (أي التسارع العالي) ، فربما تريد فقط التوجه مباشرة إلى الهدف ؛ ولكن إذا كان لديك الكثير من الكتلة بالنسبة إلى دفعك ، فربما ترغب في إجراء دورة اعتراض. إذا كنت تريد التباطؤ مع اقتراب الهدف ، فيمكنك عكس التسارع عندما |X-X_target| يصبح صغيرًا (أي يقتربون) و/أو سرعاتها قريبة. أيضًا ، يمكن أن يساعد التخميد على الأشياء التي لا تتأرجح ، ولهذا السبب ، إضافة مصطلح إلى التسارع شيء ما -c*(V-V_target). أقترح عليك أن تلعب مع هذه حتى تحصل على شيء يطابق المظهر الجسدي وتشعر بأنك تهدف إليه.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top