مزيد من المعلومات عن العرض في حلقات الألعاب

تظهر طريقة شائعة جدًا لتنفيذ حلقة الألعاب على النحو التالي:

while (playing) {
    advance state by one frame
    render the new frame
    sleep until it’s time to do the next frame
}

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

ما عليك سوى مطابقة عدد اللقطات في الثانية على الشاشة وتغيير حالة اللعبة. وفقًا للوقت المنقضي منذ عرض الإطار السابق. هناك العديد من حول هذا الأمر:

  • استخدام مكتبة Android Frame Pacing (يُنصَح به)
  • امتلاء قائمة الانتظار المؤقت والاعتماد على "الموارد الاحتياطية للمقايضة" الضغط الخلفي
  • استخدام مصمّم الرقصات (API 16+ )

مكتبة إعدادات سرعة إطارات Android

راجِع تحقيق سرعة عرض الإطارات المناسبة للحصول على المعلومات. على استخدام هذه المكتبة.

ازدحام في قائمة الانتظار

هذا سهل التنفيذ للغاية: ما عليك سوى تبديل الموارد الاحتياطية بأسرع ما يمكن. في وقت مبكر من Android، فقد يؤدي ذلك بالفعل إلى فرض عقوبة قد يسمح لك "SurfaceView#lockCanvas()" بالنوم لمدة 100 ملي ثانية. الْآنْ يتم إجرائها من خلال قائمة الانتظار المؤقت، ويتم إفراغ Bufferقائمة الانتظار في أسرع وقت ممكن بإمكان SurfaceFlinger.

يمكنك الاطّلاع على مثال واحد على هذا الأسلوب في Android Breakout. أُنشأها جون هنتر، الذي كان متخصصًا GLSurfaceView، والتي تعمل في حلقة تكرار تستدعي استدعاء الدوال البرمجية onDrawFrame() ثم تبديل المخزن المؤقت. إذا كانت قائمة الانتظار ممتلئة، سينتظر استدعاء eglSwapBuffers() حتى يتوفّر مورد احتياطي. تصبح المخازن المؤقتة متاحة عند إصدار SurfaceFlinger، ويتم تفعيلها بعد والحصول على شاشة جديدة للعرض. ولأن هذا يحدث في VSYNC، فإن حلقة الرسم مع معدل التحديث. في الغالب.

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

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

مُصمِّم رقصات

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

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

"تطبيق Record GL" فإن النشاط في Grafika مثال على ذلك. في بعض الأجهزة (مثل Nexus 4 وNexus 5)، سيبدأ النشاط في إسقاط الإطارات إذا ما عليك سوى الجلوس والمشاهدة. يعد عرض GL تافهًا، ولكن في بعض الأحيان إعادة رسم العناصر، وقد تستغرق تصريح القياس/التنسيق وقتًا طويلاً جدًا إذا دخول الجهاز في وضع الطاقة المخفّضة. (وفقًا لـ systrace، يستغرق 28 ملي ثانية بدلاً من 6 ملي ثانية بعد بطء الساعات في نظام Android 4.4. إذا قمت بسحب بإصبعك حول الشاشة، فإنه يعتقد أنك تتفاعل مع النشاط، وبالتالي تظل سرعات الساعة مرتفعة ولن تسقط أي إطار أبدًا).

كان الإصلاح البسيط هو إسقاط إطار في معاودة الاتصال بمصمم الرقصات إذا كان الوقت أكبر من N مللي ثانية بعد وقت VSYNC. عادة ما تكون قيمة N بناءً على فواصل VSYNC المرصودة سابقًا. على سبيل المثال، إذا كانت قيمة فترة إعادة التحميل هي 16.7 ملي ثانية (60 لقطة في الثانية)، وقد يتم إغفال إطار إذا شغّلت اللعبة لمدة أطول. تأخر أكثر من 15 ملي ثانية.

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

إدارة سلاسل المحادثات

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

Breakout و"Record GL app" تستخدم سلاسل عارض مخصصة، كما يمكنها أيضًا لتعديل حالة الصورة المتحركة في سلسلة المحادثات هذه يعد هذا نهجًا معقولاً طالما يمكن تحديث حالة اللعبة بسرعة.

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

run() {
    Thread.sleep(100);
    synchronized (mLock) {
        moveBlock();
    }
}

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

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

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