كيفية جعل اللعبة تعمل في 60fps

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

while(running) { update(); render(); display(); } 

سهل جدا! الآن تعمل اللعبة بسرعة 60 إطارًا في الثانية وكل شيء يسير كالساعة. تم شكرا لقراءة هذا المنصب.


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

قد تعتقد: أحتاج إلى قياس الوقت في مكان ما وتقديم تحديث بالتردد الصحيح. هذا بسيط للغاية - ما عليك سوى تجميع الوقت في كل دورة وتحديثه في كل مرة يتجاوز فيها العتبة بمقدار 1/60 من الثانية.

 while(running) { deltaTime = CurrentTime()-OldTime; oldTime = CurrentTime(); accumulator += deltaTime; while(accumulator > 1.0/60.0){ update(); accumulator -= 1.0/60.0; } render(); display(); } 

تم ، ليس أسهل. في الواقع ، هناك مجموعة من الألعاب التي تبدو الكود عليها بهذه الطريقة. لكن هذا خطأ. هذا مناسب لضبط التوقيت ، ولكنه يؤدي إلى مشاكل في الرجيج (التأتأة) وأوجه عدم التطابق الأخرى. هذه المشكلة شائعة جدًا: لا يتم عرض الإطارات بالضبط 1/60 من الثانية ؛ حتى عند تشغيل vsync ، هناك دائمًا قليل من الضوضاء في الوقت الذي يتم عرضه فيه (وفي دقة مؤقت OS). لذلك ، ستظهر المواقف عند تقديم إطار ، وتعتقد اللعبة أن وقت إعادة التحديث لم يحن بعد (لأن البطارية تتخلف عن جزء ضئيل للغاية) ، لذا فهي تتكرر في نفس الإطار مرة أخرى ، ولكن الآن أصبحت اللعبة متأخرة للإطار ، لذلك تتضاعف تحديث. هنا هو الوخز!

غوغلينغ ، يمكنك العثور على العديد من الحلول الجاهزة للقضاء على هذا الوخز. على سبيل المثال ، يمكن أن تستخدم اللعبة متغيرًا بدلاً من خطوة زمنية ثابتة ، وتترك ببساطة البطاريات بالكامل في رمز التوقيت. أو يمكنك تنفيذ خطوة زمنية ثابتة باستخدام عارض محرف ، كما هو موضح في مقال مشهور إلى حد ما " Fix Your Timestep " بقلم Glenn Fielder. أو يمكنك إعادة صياغة رمز المؤقت بحيث يكون أكثر مرونة بقليل ، كما هو موضح في منشور مشكلات توقيت الإطار في Slick Entertainment (للأسف ، لم تعد هذه المدونة موجودة).



توقيت غامض


كانت طريقة Slick Entertainment مع "توقيت غامض" في محركي الأسهل في التنفيذ ، لأنها لا تتطلب تغييرات في منطق اللعبة وعرضه. حتى في النهاية اقترب منه. كان يكفي فقط لإدخاله في المحرك. في الواقع ، إنها تسمح ببساطة بتحديث اللعبة "قبل ذلك بقليل" لتجنب مشاكل عدم تطابق التوقيت. إذا كانت اللعبة تتضمن vsync ، فهي تتيح لك فقط استخدام vsync كوقت مؤقت للعبة ، وتوفر صورة سلسة.

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

 while(accumulator > 1.0/62.0){ update(); accumulator -= 1.0/60.0; if(accumulator < 0) accumulator = 0; } 

إذا تم تمكين vsync ، فإنه يتيح للعبة العمل بشكل أساسي مع خطوة ثابتة ، والتي تتوافق مع معدل تحديث الشاشة ، وتوفر صورة سلسة. المشكلة الرئيسية هنا هي أنه عندما يتم تعطيل vsync ، ستعمل اللعبة بشكل أسرع قليلاً ، لكن الفرق ضئيل للغاية بحيث لا يلاحظها أحد.

العدائين السرعة. سوف تلاحظ Speedrunners. بعد وقت قصير من إطلاق اللعبة ، لاحظوا أن بعض الأشخاص في قوائم أعلى درجات السرعة في speedran لديهم أوقات سفر أقل ، لكن تبين أنها أفضل من غيرها. والسبب المباشر لذلك هو التوقيت غير الواضح وانقطاع vsync في اللعبة (أو 144 شاشة Hz). لذلك ، أصبح من الواضح أنك تحتاج إلى إيقاف هذا الغموض عند قطع اتصال vsync.

أوه ، لكننا لا نستطيع التحقق مما إذا كان vsync معطلًا. لا توجد مكالمات لهذا في نظام التشغيل ، وعلى الرغم من أننا يمكن أن نطلب من التطبيق لتمكين أو تعطيل vsync ، في الواقع يعتمد كليا على نظام التشغيل وبرنامج تشغيل الرسومات. الشيء الوحيد الذي يمكن القيام به هو تقديم مجموعة من الإطارات ، ومحاولة قياس وقت تنفيذ هذه المهمة ، ثم قارن ما إذا كانت تستغرق حوالي الوقت نفسه. هذا بالضبط ما فعلته من أجل The End هو Nigh . إذا لم تتضمن اللعبة vsync بتردد 60 هرتز ، فستعود إلى موقت الإطار الأصلي "بدقة 60 إطارًا في الثانية". بالإضافة إلى ذلك ، أضفت معلمة إلى ملف التكوين الذي يفرض على اللعبة عدم استخدام الغموض (بشكل أساسي لمهربي السرعة الذين يحتاجون إلى وقت دقيق) وأضفت معالج توقيت دقيق داخل اللعبة لهم ، والذي يسمح باستخدام autosplitter (هذا هو البرنامج النصي الذي يعمل مع مؤقت الوقت الذري).

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

أبحث من خلال رمز المؤقت الخاص بي مؤخرًا ، لاحظت شيئًا غريبًا. تم استبدال البطارية ، استغرق كل إطار وقتًا أطول بقليل من 1/60 من الثانية ، لذلك اعتقدت اللعبة من وقت لآخر أن الوقت قد تأخر عن الإطار وأجرت تحديثًا مزدوجًا. اتضح أن شاشتي تعمل بتردد 59.94 هرتز ، وليس 60 هرتز. هذا يعني أن كل 1000 إطار كان عليه القيام بتحديث مزدوج من أجل "اللحاق بالركب". ومع ذلك ، يكون الإصلاح بسيطًا للغاية - فقط قم بتغيير الفاصل الزمني لترددات الإطارات المسموح بها (ليس من 60 إلى 62 ، ولكن من 59 إلى 61).

 while(accumulator > 1.0/61.0){ update(); accumulator -= 1.0/59.0; if(accumulator < 0) accumulator = 0; } 

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

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

مراقبة جهاز محاكاة


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

مراقب محاكي يظهر على الساحة. هذا هو الرمز "القذر والسريع" الذي كتبته ، ومحاكاة "تشغيل الشاشة" ، ويظهر لي أساسًا مجموعة من الأرقام التي تعطي فكرة عن ثبات كل مؤقت تم اختباره.

على سبيل المثال ، بالنسبة لأبسط مؤقت ، يتم عرض القيم التالية من بداية المقالة:

20211012021011202111020211102012012102012[...]
TOTAL UPDATES: 10001
TOTAL VSYNCS: 10002
TOTAL DOUBLE UPDATES: 2535
TOTAL SKIPPED RENDERS: 0
GAME TIME: 166.683
SYSTEM TIME: 166.7


أولاً ، يعرض الرمز لكل مضاهاة vsync عدد "التحديثات" لدورة اللعبة بعد vsync السابقة. أي قيم غير الصلبة 1 تؤدي إلى صورة مضطربة. في النهاية ، يعرض الرمز الإحصائيات المتراكمة.

عند استخدام "المؤقت الضبابي" (مع فاصل 60-62 إطارًا في الثانية) على شاشة 59.94 هيرتز ، يعرض الرمز ما يلي:

111111111111111111111111111111111111111111111[...]
TOTAL UPDATES: 10000
TOTAL VSYNCS: 9991
TOTAL DOUBLE UPDATES: 10
TOTAL SKIPPED RENDERS: 0
GAME TIME: 166.667
SYSTEM TIME: 166.683


يعد تضايق الإطار نادرًا جدًا ، لذلك قد يكون من الصعب ملاحظة ذلك الرقم 1. لكن الإحصائيات المعروضة توضح بوضوح أن اللعبة أجرت العديد من التحديثات المزدوجة هنا ، مما يؤدي إلى الرجيج. في الإصدار الثابت (بفاصل زمني يتراوح بين 59-61 إطارًا في الثانية) ، هناك 0 تم تخطيها أو تحديثها مرتين.

يمكنك أيضًا تعطيل vsync. تصبح بقية البيانات الإحصائية غير مهمة ، ولكن هذا يوضح لي بوضوح حجم "تحول الوقت" (تحول وقت النظام بالنسبة إلى حيث يجب أن يكون وقت اللعبة).

GAME TIME: 166.667
SYSTEM TIME: 169.102


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

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

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

 while(accumulator >= 1.0/61.0){ simulate_update(); accumulator -= 1.0/60.0; if(accumulator < 1.0/59.01.0/60.0) accumulator = 0; } 

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

لست راضيًا بنسبة 100٪ عن قراري (لا يزال يتطلب اختراقًا باستخدام "التعرف على vsync" ، وقد يحدث الرجفنج بين الحين والآخر أثناء إلغاء التزامن) ، لكنني أعتقد أنه جيد مثل محاولة تنفيذ دورة الألعاب بخطوة ثابتة. ينشأ جزء من هذه المشكلة لأنه من الصعب للغاية تحديد معايير ما يعتبر "مقبولاً" هنا. تكمن الصعوبة الرئيسية في المفاضلة بين الوقت التحول والأطر المزدوجة / تخطي. إذا قمت بتشغيل لعبة 60 هرتز على جهاز عرض 50 هرتز PAL ... فما هو القرار الصحيح؟ هل تريد اللعب الرجيج أو اللعب بشكل أبطأ بشكل ملحوظ؟ كلا الخيارين يبدو سيئاً.

تقديم منفصلة


في الطرق السابقة ، وصفت ما أسميه "تقديم عرض الأقفال". تقوم اللعبة بتحديث حالتها ، ثم عرضها ، وعند عرضها ، تعرض دائمًا أحدث حالة للعبة. يتم التقديم والتحديث معًا.

ولكن يمكنك فصلها. هذا هو بالضبط ما تفعله الطريقة الموضحة في منشور " إصلاح Your Timestep ". لن أكرر نفسي ، يجب عليك بالتأكيد قراءة هذا المنصب. هذا (كما أفهمها) هو "معيار الصناعة" المستخدم في ألعاب ومحركات AAA مثل Unity و Unreal (ومع ذلك ، في الألعاب النشطة المكثفة ثنائية الأبعاد ، يفضلون عادةً استخدام خطوة ثابتة (قفل) ، لأنه في بعض الأحيان الدقة التي تمنحك هذه الطريقة).

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

 while(running){ computeDeltaTimeSomehow(); accumulator += deltaTime; while(accumulator >= 1.0/60.0){ previous_state = current_state; current_state = update(); accumulator -= 1.0/60.0; } render_interpolated_somehow(previous_state, current_state, accumulator/(1.0/60.0)); display(); } 

لذلك ، الابتدائية. تم حل المشكلة.

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

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

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

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

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

في الواقع ، هناك طريقة للتغلب على هذه المشكلة.

تحديثات الخطوة وقت متغير


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

 while(running) { deltaTime = CurrentTime()-OldTime; oldTime = CurrentTime(); update(deltaTime); render(); display(); } 

لا الشذوذ مع توقيت. لا تقديم الاستيفاء غريب. كل شيء بسيط ، كل شيء يعمل.

لذلك ، الابتدائية. تم حل المشكلة. والآن إلى الأبد! من المستحيل تحقيق نتيجة أفضل!

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

 position += speed; 

في هذا:

 position += speed * deltaTime; 

واستبدل الكود التالي:

 speed += acceleration; position += speed; 

في هذا:

 speed += acceleration * deltaTime; position += speed * deltaTime; 

واستبدل الكود التالي:

 speed += acceleration; speed *= friction; position += speed; 

في هذا:

 Vec3D p0 = position; Vec3D v0 = velocity; Vec3D a = acceleration*(1.0/60.0); double f = friction; double n = dt*60; double fN = pow(friction, n); position = p0 + ((f*(a*(f*fN-f*(n+1)+n)+(f-1)*v0*(fN-1)))/((f-1)*(f-1)))*(1.0/60.0); velocity = v0*fN+a*(f*(fN-1)/(f-1)); 

... انتظر

من أين جاء كل هذا؟

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

الآن قد يسألونني لماذا لا يفعلون هذا مثل هذا:

 speed += acceleration * deltaTime; speed *= pow(friction, deltaTime); position += speed * deltaTime; 

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

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

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


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


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

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



الخاتمة


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

Source: https://habr.com/ru/post/ar442528/


All Articles