كيفية بناء عداء لانهائي على iOS: Cocos2D والأتمتة والمزيد

نشرت: 2022-03-11

يمكن أن يكون تطوير ألعاب iOS تجربة ثرية من حيث النمو الشخصي والمالي. في وقت سابق من هذا العام ، قمت بنشر لعبة قائمة على Cocos2D ، Bee Race ، في متجر التطبيقات. طريقة لعبها بسيطة: عداء لانهائي يقوم فيه اللاعبون (في هذه الحالة ، النحل) بجمع النقاط وتجنب العقبات. انظر هنا للحصول على عرض توضيحي.

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

  • العفاريت والأشياء المادية
  • مقدمة موجزة لـ Cocos2D
  • استخدام Cocos2D مع القصص المصورة
  • طريقة اللعب ووصف (موجز) للمشروع
  • أتمتة الوظائف. استخدم الأدوات. كون لطيف.
  • الفواتير داخل التطبيق
  • لعبة متعددة اللاعبين مع Game Center
  • مجال للتحسين
  • خاتمة

العفاريت والأشياء المادية

قبل أن ندخل في التفاصيل الدقيقة ، سيكون من المفيد فهم التمييز بين العفاريت والأشياء المادية.

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

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

في ألعاب الركض اللانهائي التي تعمل بنظام iOS ، تتعايش الكائنات الحية والعفاريت.

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

مع أخذ ذلك في الاعتبار…

برنامج تعليمي موجز عن تطوير لعبة Cocos2D iOS

Cocos2D-iphone هو إطار عمل مفتوح المصدر لنظام iOS يستخدم OpenGL لتسريع رسومات الأجهزة ويدعم محركات الفيزياء Chipmunk و Box2D.

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

ولعل الأهم من ذلك ، أن تطوير ألعاب Cocos2D يستخدم تسريع أجهزة الرسومات . بدون مثل هذا التسريع ، ستعمل أي لعبة عداء لا نهائية لنظام iOS مع عدد معتدل من النقوش المتحركة بأداء ضعيف بشكل ملحوظ. إذا حاولنا إنشاء تطبيق أكثر تعقيدًا ، فسنبدأ على الأرجح في رؤية تأثير "bullet-time" على الشاشة ، أي نسخ متعددة من كل كائن أثناء محاولة تحريكه.

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

استخدام Cocos2D مع القصص المصورة

بعد كل الثناء على Cocos2D ، قد يبدو من غير المنطقي اقتراح استخدام القصص المصورة. لماذا لا تتلاعب فقط بأشياءك باستخدام Cocos2D ، وما إلى ذلك؟ حسنًا ، لكي نكون صادقين ، بالنسبة للنوافذ الثابتة ، غالبًا ما يكون استخدام Xcode's Interface Builder وآلية Storyboard الخاصة به أكثر ملاءمة.

أولاً ، يسمح لي بسحب جميع العناصر الرسومية الخاصة بي ووضعها في لعبة الركض التي لا نهاية لها باستخدام الماوس. ثانيًا ، واجهة برمجة تطبيقات Storyboard API مفيدة جدًا جدًا. (ونعم ، أعرف عن Cocos Builder).

إليك لمحة سريعة عن لوحة العمل الخاصة بي:

لمعرفة كيفية صنع لعبة ركض لا نهاية لها ، ابدأ بقصة عمل جيدة.

تحتوي وحدة التحكم في العرض الرئيسية للعبة فقط على مشهد Cocos2D مع بعض عناصر HUD في الأعلى:

تكون بداية البرنامج التعليمي Cocos2D الخاص بنا في وحدة التحكم في العرض.

انتبه للخلفية البيضاء: إنه مشهد Cocos2D ، والذي سيحمل كل العناصر الرسومية الضرورية في وقت التشغيل. وجهات النظر الأخرى (المؤشرات الحية ، الهندباء ، الأزرار ، إلخ) كلها عروض Cocoa القياسية ، تمت إضافتها إلى الشاشة باستخدام Interface Builder.

لن أسهب في التفاصيل - إذا كنت مهتمًا ، فيمكن العثور على أمثلة على GitHub.

طريقة اللعب ووصف (موجز) للمشروع

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

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

يحتوي Cocos2D على كائن كاميرا تم تصميمه لتتبع الشخصية ؛ من الناحية العملية ، كان التلاعب في CCLayer الذي يحتوي على عالم اللعبة أقل تعقيدًا.

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

تحتوي طبقة العالم نفسها في الواقع على طبقتين فرعيتين. عند بدء اللعبة ، يتم ملء الطبقة الفرعية الأولى من 0 إلى BUF_LEN ويتم عرضها في البداية. يتم ملء الطبقة الفرعية الثانية مسبقًا من BUF_LEN إلى 2 * BUF_LEN. عندما تصل النحلة إلى BUF_LEN ، يتم تنظيف الطبقة الفرعية الأولى وإعادة ملؤها على الفور من 2 * BUF_LEN إلى 3 * BUF_LEN ، ويتم تقديم الطبقة الفرعية الثانية. بهذه الطريقة ، نتناوب بين الطبقات ، ولا نحتفظ أبدًا بالأشياء القديمة ، وهو جزء مهم من تجنب تسرب الذاكرة.

تتكون لعبة الجري اللانهائي الخاصة بي من عالم متعدد الطبقات.

فيما يتعلق بمحركات الفيزياء ، استخدمت Chipmunk لسببين:

  1. إنه مكتوب في Pure Objective-C.
  2. لقد عملت مع Box2D من قبل ، لذلك أردت المقارنة بين الاثنين.

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

أتمتة الوظائف. استخدم الأدوات. كون لطيف.

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

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

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

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

يمكن أن يكون السعي إلى الكمال قاتلًا لـ Angry Birds. الوقت هو مورد حاسم في تطوير ألعاب iOS.
سقسقة

في بناء هذا العداء اللامتناهي ، كانت الأتمتة أساسية مرة أخرى. على سبيل المثال ، كان فناني يرسل لي رسومات عالية الدقة من خلال مجلد Dropbox خاص. لتوفير الوقت ، كتبت بعض البرامج النصية لإنشاء مجموعات ملفات تلقائيًا لمختلف القرارات المستهدفة التي يتطلبها متجر التطبيقات ، مضيفًا -hd أو @ 2x أيضًا (تستند البرامج النصية المذكورة إلى ImageMagick).

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

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

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

في حالتنا ، نحتاج إلى استخدام نمط plist:

 <real>%.5f</real><real>%.5f</real>

بعد ذلك ، نقوم بإنشاء ملف plist مع أوصاف الكائنات:

 <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>jet_ant</key> <dict> <key>vertices</key> <array> <real>-0.18262</real><real>0.08277</real> <real>-0.14786</real><real>-0.22326</real> <real>0.20242</real><real>-0.55282</real> <real>0.47047</real><real>0.41234</real> <real>0.03823</real><real>0.41234</real> </array> </dict> </dict> </plist>

ومحمل كائن:

 - (void)createBodyAtLocation:(CGPoint)location{ float mass = 1.0; body = cpBodyNew(mass, cpMomentForBox(mass, self.sprite.contentSize.width*self.sprite.scale, self.sprite.contentSize.height*self.sprite.scale)); body->p = location; cpSpaceAddBody(space, body); NSString *path =[[NSBundle mainBundle] pathForResource:@"obj _descriptions" ofType:@"plist"]; // <- load plist NSDictionary *objConfigs = [[[NSDictionary alloc] initWithContentsOfFile:path] autorelease]; NSArray *vertices = [[objConfigs objectForKey:namePrefix] objectForKey:@"vertices"]; shape = [ChipmunkUtil polyShapeWithVertArray:vertices withBody:body width:self.sprite.contentSize.width height:self.sprite.contentSize.height]; shape->e = 0.7; shape->u = 1.0; shape->collision_type = OBJ_COLLISION_TYPE; cpSpaceAddShape(space, shape); }

لاختبار مدى توافق العفاريت مع أجسادهم المادية ، انظر هنا.

أفضل بكثير ، أليس كذلك؟

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

الفواتير داخل التطبيق

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

للمزيد ، لدى Ray Wenderlich البرنامج التعليمي المثالي للفوترة داخل التطبيق.

لعبة متعددة اللاعبين مع Game Center

في ألعاب الهاتف المحمول ، يعتبر التواصل الاجتماعي أكثر من مجرد إضافة زر "أعجبني" على Facebook أو إعداد لوحات الصدارة. لجعل اللعبة أكثر إثارة ، قمت بتطبيق إصدار متعدد اللاعبين.

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

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

هل تتذكر مناقشة جزء من جيل العالم؟ حيث كان لدينا طبقتان فرعيتان ، واحدة من 0 إلى BUF_LEN والأخرى من BUF_LEN إلى 2 * BUF_LEN؟ لم يتم استخدام هذه البنية عن طريق الصدفة - كان من الضروري توفير رسومات سلسة عبر الشبكات المتأخرة. عندما يتم إنشاء جزء من العناصر ، يتم تعبئته في plist وإرساله إلى اللاعب الآخر. المخزن المؤقت كبير بما يكفي للسماح للاعب الثاني باللعب حتى مع تأخير الشبكة. يرسل كلا اللاعبان موقعهما الحالي لبعضهما البعض خلال فترة نصف ثانية ، كما يرسلان حركاتهما التصاعدية على الفور. لتسهيل التجربة ، يتم تصحيح الموضع والسرعة كل 0.5 ثانية باستخدام حركة سلسة ، لذلك يبدو عمليًا أن اللاعب الآخر يتحرك أو يتسارع تدريجيًا.

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

مجال للتحسين

الألعاب لا تنتهي أبدا. من المسلم به أن هناك العديد من المجالات التي أرغب في تحسينها ، وهي:

  1. مشاكل التحكم: النقر غالبًا ما يكون لفتة غير بديهية للاعبين الذين يفضلون الانزلاق.
  2. يتم نقل طبقة العالم باستخدام الإجراء CCMoveBy. كان هذا جيدًا عندما كانت سرعة الطبقة العالمية ثابتة ، حيث تم تدوير CCMoveBy بالعمل باستخدام CCRepeatForever:

     -(void) infiniteMove{ id actionBy = [CCMoveBy actionWithDuration: BUFFER_DURATION position: ccp(-BUFFER_LENGTH, 0)]; id actionCallFunc = [CCCallFunc actionWithTarget:self selector:@selector(requestFillingNextBuffer)]; id actionSequence = [CCSequence actions: actionBy, actionCallFunc, nil]; id repeateForever = [CCRepeatForever actionWithAction:actionSequence]; [self.bufferContainer runAction:repeateForever]; }

    لكن في وقت لاحق ، أضفت زيادة في السرعة العالمية لجعل اللعبة أكثر صعوبة مع استمرارها:

     -(void) infiniteMoveWithAccel { float duration = BUFFER_DURATION-BUFFER_ACCEL*self.lastBufferNumber; duration = max(duration, MIN_BUFFER_DURATION); id actionBy = [CCMoveBy actionWithDuration: duration position: ccp(-BUFFER_LENGTH, 0)]; id restartMove = [CCCallFunc actionWithTarget:self selector:@selector(infiniteMoveWithAccel)]; id fillBuffer = [CCCallFunc actionWithTarget:self selector:@selector(requestFillingNextBuffer)]; id actionSequence = [CCSequence actions: actionBy, restartMove, fillBuffer, nil]; [self.bufferContainer runAction:actionSequence]; }

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

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

خاتمة

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

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

بالإضافة إلى ذلك ، أوصي باستخدام AppAnnie لفحص المعلومات المختلفة حول جميع التطبيقات في App Store ، والتسجيل في بعض خدمات التحليلات مثل Flurry Analytics يمكن أن يكون مفيدًا أيضًا.

وإذا كانت هذه اللعبة قد أثارت اهتمامك ، فتأكد من إطلاعك على Bee Race في المتجر.