نقوم بربط اللاعبين المتعددين بلعبة "اصنع كلمات من الكلمات" على نظام iOS و Android المكتوب بلغة C ++

في وقت سابق ، كتبت عن تجربتي في تطوير لعبة كلمات متنقلة على Android و iOS ، والتي تحظى بشعبية كبيرة ، وقررت تثبيت وضع اللاعبين المتعددين عليها ، عندما يتنافس لاعبان فيما بينهما ، ويكتبان الكلمات بدورها ، باعتبارها الجولة الأخيرة من بث سيرجي سوبونيف. ساعة. "



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

قليلا من التاريخ


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



السؤال الذي يطرح نفسه حول ما هو المنفذ للمشاريع الحالية لمزيد من الدعم.

لماذا لا cocos2d-x




تعد Cocos2d-x واحدة من أكثر محركات تطوير الألعاب المحمولة C ++ انتشارًا. على ما يبدو ، بسبب الكود الحر والمفتوح المصدر. المحرك موثق بشكل سيئ. يغطي الوصف الجزء الضئيل من المحرك ومعظم المواد قديمة.

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

سقط خياري على SDL




SDL ، مثل Marmalade SDL ، ليس محركًا ، بل هو منصة. يوفر واجهة برمجة تطبيقات ذات مستوى منخفض ، يمكنني من خلالها بناء مستويات تجريد ملائمة لي. كل هذا مكتوب في C ، شفرة المصدر مفتوحة.

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

لحسن الحظ أو لسوء الحظ ، لا توفر SDL أدوات لمهمة ضيقة مثل تطوير لاعبين متعددين لنظامي iOS و Android ، لذلك كان علي الاندماج مع الخدمات المقابلة بنفسي.

بنية تطبيق متعددة مؤشرات الترابط


يتم تطبيق منطق التطبيق وكل العمل مع الرسومات في الخيط الرئيسي ، وهو عبارة عن دورة معالجة الرسائل ويبدأ في الوظيفة الرئيسية. استدعاء هذا الدفق SDL الموضوع. بدورها ، تلقي مؤشرات الترابط الأخرى الأحداث (SDL_PushEvent) للمعالجة في قائمة الانتظار ، ويقرأ منها باستخدام SDL_WaitEvent و SDL_PollEvent. هذه هي إما أحداث النظام التي يتم طرحها بواسطة النظام والتي يتم دعمها بالفعل في SDL ، أو مكالمات Callbacks و Listener-s ، والتي نقوم بتنفيذها بالفعل خارج وظائف SDL.



تتم كتابة كل منطق اللعبة بلغة C ++. يحتوي دليل المشروع على مجموعة من ملفات * .cpp التي يمكن تقسيمها إلى ثلاث مجموعات:

  • عبر الأنظمة الأساسية - تلك الملفات المضمنة في تجميع كافة الأنظمة الأساسية (منطق اللعبة) ؛
  • monoplatform ، أي يتم تضمينها في تطبيق أي منصة واحدة لتنفيذ معالمه.

وفقًا لذلك ، هناك ثلاثة أدلة منفصلة لتصميم كل منصة:

  • proj.win32 - مشروع VS2017 Community Edition ؛
  • proj.android - مشروع أندرويد باستخدام Gradle؛
  • proj.ios - مشروع Xcode لنظام التشغيل iOS.

التكامل مع الخدمات متعددة اللاعبين


نحتاج الآن إلى لصق طبقة منفصلة ، ستكون مسؤولة عن وظائف مثل:

  • البحث عن الخصم ، اتصال اللعبة ؛
  • الرسائل بين المنافسين.
  • الخروج من غرفة اللعبة ؛
  • تحديد نقاط اللاعب في المتصدرين.

تدعم كل من أنظمة التشغيل iOS و Android نظام التشغيل المتعدد في الوقت الفعلي (RTMP). في حالة Android ، ندمج مع Google Play Services (GPS) ، في حالة iOS ، مركز الألعاب. في السابق ، كانت Google تدعم التكامل مع نظام التشغيل iOS ، لكن هذا العام قررت التخلي عنه.

في هذه المقالة ، لن أصف الإجراءات التي تحتاج إلى تنفيذها في Google Play Console و AppStoreConnect لتكوين اللاعبين المتعددين ، ولن أصف مواصفات الفئات وطرق التكامل - كل هذا موصوف في مواقع البائعين.

بعد ذلك ، سوف أصف بإيجاز التغييرات التي يجب إجراؤها في المشروع لكل من الأنظمة الأساسية.

أندرويد


كيف؟ أنا لم أقل هذا حتى الآن؟ يستخدم Android NDK في ترجمة رمز C ++. على الرغم من أنك إذا كنت مطور Android ، فأنت تعلم بالفعل.

تم توضيح التعليمات العامة لدمج خدمات Google Play في مشروع Android على الموقع لمطوري Android. في مشروعي أستخدم التبعيات التالية:

implementation 'com.google.android.gms:play-services-games:16.0.0' implementation 'com.google.android.gms:play-services-nearby:16.0.0' implementation 'com.google.android.gms:play-services-auth:16.0.1' 

في البداية ، كانت الفكرة هي استخدام واجهة برمجة تطبيقات C ++ ، والتي تأتي في شكل مكتبات ثابتة مجمعة بدون مصادر. نظرًا لعدم وجود تجميع لنظام التشغيل x86_64 في قائمة المكتبات ، قررت أن الرجال من Google لا يراقبون حقًا أهمية SDK وقررت اختراع دراجتهم لكتابة هذه الطبقة في Java ، لفها بأغلفة JNI . ثم ، لماذا أحتاج إلى تبعية إضافية في شكل libs بدون رموز مصدر لا تزال داخل Java تسحب Java؟ بالإضافة إلى أهمية فئات Java ، ستحتاج أيضًا إلى مراقبة أهمية هذه السجلات.

كدليل ، استخدمت مثالًا جيدًا من نماذج Google . شكرا لجوجل لهذا. أبل ، خذ مثالاً من Google!

iOS


للتكامل مع Game Center ، يجب عليك توصيل إطار عمل GameKit. نحن نصف طبقة التكامل بأكملها مع Game Center في ملف واحد * .m ونوفر الواجهة له من خلال ملف * .h منفصل. نظرًا لأن لغة C ++ هي مجموعة فرعية من لغة الهدف - C ، فلن تكون هناك مشكلات في تجميع ملفات * .cpp و * .m في مشروع واحد.

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

مبدأ العمل مع طبقة متعددة اللاعبين


نقطة دخول واحدة


بعد أن درست ميزات العمل مع لاعبين متعددين على كلا النظامين الأساسيين ، قمت بإنشائه عملية تجريد C ++ واحدة للتطبيق الخاص بي وفي وقت التجميع ، فإن التطبيق المقابل "يناسب" ، وفقًا للنظام الأساسي المحدد. أي أن تطبيقي لا يعرف أي من خدمات Google Play و Game Center وميزاتها. تعرف فقط واجهة برمجة تطبيقات C ++ المقدمة إليها ، حيث يوجد على سبيل المثال طرق مثل:

 SignIn() //     SignOut() //     LeaveRoom() //    SendMessage(...) //    ShowLeaderboards() //    SubmitScore(...) //    ... 

البحث عن الخصم


يمكن للاعب دعوة صديق من قائمة جهات الاتصال الخاصة به ، أو بدء اللعبة مع خصم عشوائي. يمكن للاعب الذي تلقى الدعوة قبولها أو رفضها. بالنسبة لجميع هذه السيناريوهات ، أستخدم الواجهة القياسية للخدمة المستخدمة. أود أن أشير إلى أن مكوّن Google يبدو أجمل من نظام iOS iOS. ربما في يوم من الأيام ستصل يدي إلى هناك وسأكتب واجهتي مع الدومينو والسيدات الشابات.

اتصال بغرفة اللعبة


عندما يتصل لاعبان بغرفة الألعاب الافتراضية ، يتلقيان رد الاتصال المقابل. الآن تحتاج إلى اختيار من سيكون المضيف.

اختيار المضيف


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



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

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

اختيار مضيف Android

لم أجد أي نصيحة بشأن اختيار مضيف في google dock. إنهم صامتون ، أنصار. لكن أهل الخير على stackoverflow.com ألقوا رابطًا إلى الفيديو ، وهو ما يفسر المبدأ التالي بالتفصيل:

  • يقوم كل مشارك بفرز قائمة المشاركين (تصاعدي أو تنازلي - لا يهم ، الشيء الرئيسي هو أن كل شخص يفعل بنفس الترتيب) ؛
  • يقارن كل مشارك مشاركته بمعرف المشترك الأول من القائمة ؛
  • في حالة تطابقها ، يتم منح اللاعب الحالي الحق في اختيار من سيكون المضيف. يرمي عملة معدنية ، ويسحب عشوائيًا () ، وبالتالي يختار مضيفًا من المشاركين الحاليين ، ويخبر كل من هو المضيف.

اختيار المضيف على دائرة الرقابة الداخلية

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

المراسلة بين اللاعبين


ما هي الرسالة؟ الرسالة عبارة عن صفيف من وحدات البايت.

  • في جافا ، هذا نوع:
     byte[] 
  • في الهدف- C ، هذا:
     NSData * 
  • في C ++ ، أرسم كل ما سبق إلى
     std::vector<Uint8> 

هناك نوعان من إرسال الرسائل:

  • موثوق - تسليم مضمون من خلال قائمة الانتظار. تستخدم لتسليم الرسائل الحرجة.
  • غير موثوق - التسليم غير مضمون. الرسائل المستخدمة التي يمكن إهمال نجاح التسليم.

عادة ما يتم تسليم غير موثوق بها أسرع من موثوق. يمكنك قراءة المزيد على موقع البائعين:


كيف سنستخدم هذه المجموعة؟ بسيط جدا:

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

لذلك ، نحدد التعداد بأنواع الرسائل التي سيتبادلها اللاعبون فيما بينهم أثناء اللعبة:

  • أنا المختار من قبل المضيف. أنقل الحالة الأولية. الآن جاء دوري (المعلمات: رقم إصدار بروتوكول المراسلة ، الكلمة المصدر) ؛
  • يتم اختيارك بواسطة المضيف. إنني أتطلع إلى الاستماع منك ؛
  • أفتح (الكلمة) الكلمة. حان دورك الآن (المعلمة: الكلمة المسماة) ؛
  • أنا أستسلم. لقد فزت
  • لم أستطع كتابة كلمة أثناء الانتقال. لقد فزت
  • أوافق على الانتقام ؛
  • لقد تركت اللعبة
  • خطأ في تحليل الرسالة. غير متصل
  • إصدار بروتوكول المراسلة لديك قديم. تحقق من تحديث التطبيق. غير متصل
  • الإصدار الخاص بي من بروتوكول المراسلة قديم. تحتاج إلى التحقق من التحديث. غير متصل
  • بينغ (رسالة النظام) ؛

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

مراقبة الاتصال


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

  • يرسل كل لاعب رسالة ping إلى الخصم كل ثانية ؛
  • يتحقق كل لاعب: إذا لم تكن هناك رسالة من الخصم لأكثر من 5 ثوانٍ ، عندها يتم فقد الاتصال ، نخرج من اللعبة.

النتيجة


كنتيجة للعمل المنجز ، حصلت على لعبة ألعبها مع أصدقائي وعائلتي. ألعب على كل من iOS و Android.

صحيح ، هناك فارق بسيط على نظام التشغيل iOS - لسبب ما ، النظارات غير مثبتة في ألواح المتصدرين ، والتي أتحدث عنها حاليًا مع دعم Apple.

آمل أن تكون هذه المقالة مفيدة لكل من أعضاء فريقي وأولئك المهتمين بتطوير تطبيقات الهاتف المحمول. شكرا لاهتمامكم

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


All Articles