جعل لعبة مستوحاة من لعبة Megaman في Construct 2
Arabic (العربية/عربي) translation by Rahmat Hidayat (you can also view the original English article)
قم بعمل لعبة مستوحاة من لعبة Megaman-Inspired في Construct 2I سوف أذهب لك من خلال إنشاء لعبة مطلق النار / لعبة مستوحاة من Megaman. في هذا البرنامج التعليمي سوف أستخدم Construct 2 كأداة لجعل اللعبة ، ولكن سأشرح منطق استخدام pseudocode بحيث يمكنك اتباع هذا البرنامج التعليمي بأي لغة أو محرك من اختيارك.
من أجل التركيز على تطبيق اللعب ، لن أشرح كل ميزة Construct 2 ؛ سأفترض أنك تعرف الأساسيات مثل تحميل الرموز المتحركة أو الاصطدام الأساسي أو تشغيل الأصوات. مع ذلك ، دعونا نبدأ في صنع اللعبة.
تحضير العمل الفني
أول الأشياء أولاً ، نحن بحاجة إلى الحصول على النقوش المتحركة في لعبتنا. لحسن الحظ ، لقد غطينا opengameart مع مجموعتهم الرائعة من فن اللعب القانوني. نحن بحاجة إلى أربع مجموعات من العفاريت. بطل واحد ، عدو واحد ، والبلاط لمنصات.
- البطل (بواسطة Redshrike):
http://opengameart.org/content/xeon-ultimate-smash-friends - العدو (أيضا عن طريق Redshrike):
http://opengameart.org/content/fighting-robot-for-ultimate-smash-friends - بلاط (عن طريق الروبوت):
http://opengameart.org/content/prototyping-2d-pixelart-tilesets
لاستخدامها في Construct 2 ، أقسم sprites البطل إلى إطارات فردية باستخدام GIMP.
الحركة الأساسية
سوف أستخدم سلوك منصة Construct 2 لباقي البرنامج التعليمي حتى أتمكن من التركيز على الجزء الخاص بالتصوير و AI من اللعبة ، والذي كان المحور الرئيسي لهذا البرنامج التعليمي.

الحركة الأساسية شرح
إذا كنت تعمل بلغة أخرى ، أو تريد تنفيذ حركة platformer الأساسية الخاصة بك بدلاً من استخدام السلوك المضمّن. ما عليك سوى استخدام الشفرة في هذا القسم إذا كنت لن تستخدم السلوك المدمج لـ Construct 2.
للبدء ، نحتاج إلى النظر في ثلاث طرق يمكن للبطل أن يتحرك بها ؛ يمشي يمشي ، يمشي يسارا ، أو يقفز. كل إطار ، نقوم بتحديث محاكاة اللعبة.
number moveSpeed = 50; function update() { moveHero(); }
لتحديث شخصية اللاعب ، نقوم بتنفيذ حركة أساسية مثل هذا:
function moveHero() { // player is pressing this key down if (keyDown(KEY_LEFT)) { hero.x -= moveSpeed * deltaTime; hero.animate("walkLeft"); } if (keyDown(KEY_RIGHT)) { hero.x += moveSpeed * deltaTime; hero.animate("walkRight"); } if (keyDown(KEY_UP)) { hero.jump(); hero.animate("jump"); } // a key that was just unpressed if (keyReleased(KEY_LEFT)) { hero.animate("standLeft"); } if (keyReleased(KEY_RIGHT)) { hero.animate("standRight"); } }
أنا استخدم وظيفة أخرى للقيام بالقفز لأن القفز ليس مجرد مسألة تغيير قيمة ذ ولكن أيضا حساب الجاذبية. سيكون لدينا أيضًا وظيفة تستمع إلى ما إذا كان مفتاحًا قد تم إصداره للتو أم لا ، لإعادة الرسوم المتحركة إلى بطل الرسوم المتحركة الدائمة.
دعونا نتحدث عن كيفية جعل اللاعب يقفز. سيحتاج البطل إلى معرفة ما إذا كان يقفز حالياً أم لا ، وأيضاً ما إذا كان يسقط حالياً أم لا. لذا سنعلن عن متغيرين جديدين: isJumping و isFalling. بشكل افتراضي ، كلاهما غير صحيح ، مما يعني أن البطل يقف على النظام الأساسي.
لإجراء قفزة يجب أولاً التحقق من كون القيمتين غير صحيحتين ، ومن ثم جعل isJump صحيحًا.
Function jump() { if (!isJumping && !isFalling) { isJumping = True } }
لكي يتمكن البطل من القفز ، نحتاج إلى متغير يسمى jumpPower والجاذبية. القيمة الافتراضية لـ jumpPower هي -20 والجاذبية هي 1. المنطق هو إضافة قيمة القدرة على القفز إلى موقف Y للبطل ولإضافة الجاذبية لقفزة قيمة الطاقة.
نفعل كل هذا القراد. ربما لم تكن هذه هي الفيزياء الأكثر واقعية في الواقع ، ولكن الألعاب لا تحتاج إلى أن تكون واقعية ، ولكنها تحتاج فقط إلى أن تكون قابلة للتصديق ، وهذا هو السبب في أن بعض الألعاب لديها قفزة بشرية فائقة وقفزة مزدوجة. ينتمي الرمز أدناه إلى وظيفة التحديث.
If (isJumping || isFalling) { hero.y += hero.jumpPower; hero.jumpPower += hero.gravity; } // eventually jump power will be greater than zero, and that means the hero is falling if (hero.jumpPower >= 0) { isJumping = False; isFalling = True; } // to make a stop to the fall, do something when the hero overlaps the platform if (hero.isOverlapping(platform1)) { // platform1 is the platform that our hero can step on // resets the variable to their default value isJumping = False; isFalling = False; hero.jumpPower = -20; } // and then there's the free fall, when player falls over the edge of a platform if (!hero.isOverlapping(platform1) && hero.jumpPower < 0 && !isJumping) { // !hero.isOverlapping(platform1) checks whether or not our hero is standing on a platform // and if jumpPower is less than zero and the player is not currently jumping, then that means // he's falling // setting these two values like this will make the player fall. hero.jumpPower = 0; isFalling = true; }
ينسخ سلوك إنشاء النظام الأساسي المضمن 2 رمز المثال أعلاه الذي يتم تقديمه فقط لمساعدة أولئك الذين يعملون بلغة أخرى.
تنفيذ اطلاق النار
الآن يأتي الجزء الرماية من اللعبة. في سلسلة Megaman هناك ثلاثة أنواع من اللقطات: اللقطات العادية ، الطلقات المشحونة ، ولقطات طاقة المدير.
اللقطات العادية هي تفسيرية. تعتبر اللقطات المشحونة هي اللقطات التي يتم شحنها قبل إطلاقها ، وتأتي هذه اللقطات المشحونة في نوعين: نصف مشحونة ، ومشحونة بالكامل. هذه الهجمات المشحونة هي أقوى من اللقطات العادية ، مع المشحونة بالكامل تصبح الأقوى.
تعتبر لقطات الطاقة من بوس هي اللقطات التي حصل عليها اللاعب بعد هزيمة كل رؤساء. الضرر هو نفسه كالمعتاد ولكن لديهم خصائص خاصة لا تحتوي على لقطات عادية.
الآن بعد أن عرفنا نوع كل لقطة ، فلنبدأ في صنعها. دعونا أولا نرى المنطق الكامن وراء كيفية استخدام كل لقطة. هنا نفترض أن يتم استخدام زر Z على لوحة المفاتيح لاطلاق النار. سننفذ اثنين من السلوكيات المختلفة:
- لقطات عادية: يقوم اللاعب بالضغط على z ثم يقوم بإطلاقه على الفور (اضغط على الزر). سيتم اطلاق النار رصاصة مرة واحدة كل الصنبور. ستتغير الرسوم المتحركة لتصوير الرسوم المتحركة قبل التبديل الفوري إلى الرسوم المتحركة الدائمة.
- الطلقات المشحونة: اللاعب يضغط على Z. وسيتم إطلاق الرصاصة الأولى العادية. ستتغير الرسوم المتحركة لتصويرها قبل التبديل الفوري إلى الرسوم المتحركة الدائمة. إذا استمر الضغط على Z ، فسيتم إضافة تأثير فرض رسوم على الرسوم المتحركة (الوقوف ، المشي). إذا تم إصدار الزر Z في أقل من 5 ثوانٍ منذ الشحن الأول ، فسيتم إطلاق رصاصة نصف مشحونة. إذا تم إطلاق الزر Z بعد 5 ثوانٍ ، سيتم إطلاق رصاصة مشحونة بالكامل.
- طلقات الطاقة بوس: يجب على بطلنا أولا تجهيز الرصاصة التي حصل عليها بعد هزيمة رئيسه. بعد التجهيز ، سيضغط اللاعب على زر آخر لإطلاق هذا الرمز. يختلف سلوك التعداد النقطي هذا ، ويجب أن يتم ترميزه بشكل فريد لكل رمز نقطي.
الآن ، دعونا نبدأ في التعليمة البرمجية. نظرًا لأن بطلنا يمكنه تصوير اليسار واليمين ، نحتاج إلى معرفة الاتجاه الذي يواجهه حاليًا. دعونا نعلن عن متغير جديد يسمى يواجه يخزن قيمة سلسلة ما إذا كان البطل يواجه اليمين أو اليسار.
String facing = "right"; // which direction the hero is currently facing function moveHero() { // player is pressing this key down if (keyDown(KEY_LEFT)) { hero.x -= moveSpeed * deltaTime; hero.animate("walkLeft"); facing = "left"; } if (keyDown(KEY_RIGHT)) { hero.x += moveSpeed * deltaTime; hero.animate("walkRight"); facing = "right"; } // ... the continuation of moveHero() function goes here } function update() { // ...the update code that we previously wrote goes here... // player press this key once if (keyPressed(KEY_Z)) { if (facing == "right") { hero.animate("Shoot"); // we will add shooting function here later } else if (facing == "left") { hero.animate("Shoot"); hero.mirrorSprite(); // this function flips the sprite horizontally } } if (keyReleased(KEY_Z)) { if (facing == "right") { hero.animate("standRight"); } else if (facing == "left") { hero.animate("standLeft"); hero.mirrorSprite(); // we need to call this again because the sprite was mirrored // if we don't mirror the sprite again, standLeft will look like standRight } } }



قبل أن نطلق رصاصة ، نحتاج إلى النظر إلى الخصائص التي تحتوي عليها الرصاصة:
- السلطة: قوة الهجوم من الرصاصة ، والضرر الذي ستتعرض له للعدو
- السرعة: مدى السرعة التي تسير بها الرصاصة
- زاوية: زاوية التصوير ، تحدد الاتجاه الذي تسلكه النقطة.
هذه الخصائص سوف تختلف لكل رصاصة. على وجه الخصوص ، فإن خاصية الطاقة ستكون مختلفة. تكون خاصية الزاوية عادة واحدة من قيمتين فقط ؛ ما إذا تم إطلاق الرصاص على اليمين أو اليسار ، ما لم تكن رصاصة طاقة رئيسة قد تطلق في زاوية فريدة.
ستتم مناقشة الاختلافات في اللقطات لاحقًا ، لذلك سأقوم الآن بتغطية اللقطات الأساسية فقط. ما يلي هو جزء من التعليمات البرمجية التي يطلق النار على رمز نقطي.
// first, we create a function that creates a new bullet Function shoot(string pathToSprite, number bulletPower, number bulletSpeed, number bulletAngle) { myBullet = new Bullet(pathToSprite); myBullet.power = bulletPower; // the bullet class or object has two private variables that moves it according to its angle // more explanation to these two lines need more math, so I choose not to explain // I assume your engine of choice have a way to move an object according to its angle ySpeed = Math.sin(bulletAngle) * bulletSpeed; xSpeed = Math.cos(bulletAngle) * bulletSpeed; } // this is Bullet class' function that's called every frame, this moves the bullet according to its angle function moveBullet() { x += xSpeed * deltaTime; y += ySpeed * deltaTime; } // and this is the modification to our previous update() function function update() { // ...the update code that we previously wrote goes here... // player press this key once if (keyPressed(KEY_Z)) { if (facing == "right") { hero.animate("Shoot"); hero.shoot("path/to/sprite.png", 10, 400, 0); } else if (facing == "left") { hero.animate("Shoot"); hero.mirrorSprite(); // this function flips the sprite horizontally hero.shoot("path/to/sprite.png", 10, 400, 180); // the angle is 180 so that the bullet goes left } } // .. the continuation of update code goes here... }




لقطات مشحونة
بعض الرصاصات يمكن أن تكون أقوى من غيرها. لإنشاء لقطة مشحونة ، نحتاج إلى متغير يحمل الاسم charTime ، والذي سيزداد كل ثانية يحمل اللاعب Z لأسفل ، وسيعود إلى الصفر عند إطلاق الرصاصة. التغييرات على رمز التحديث كالتالي:
// player just released z key if (keyReleased(KEY_Z)) { if (chargedTime > 0 && chargedTime <= 5) { if (facing == "right") { hero.animate("Shoot"); hero.shoot("path/to/halfChargedBullet.png", 20, 400, 0); chargedTime = 0; } else if (facing == "left") { hero.animate("Shoot"); hero.mirrorSprite(); // this function flips the sprite horizontally hero.shoot("path/to/halfChargedBullet.png", 20, 400, 180); chargedTime = 0; } } else if (chargedTime > 5) { if (facing == "right") { hero.animate("Shoot"); hero.shoot("path/to/fullChargedBullet.png", 40, 400, 0); chargedTime = 0; } else if (facing == "left") { hero.animate("Shoot"); hero.mirrorSprite(); // this function flips the sprite horizontally hero.shoot("path/to/fullChargedBullet.png", 40, 400, 180); chargedTime = 0; } } if (facing == "right") { hero.animate("standRight"); } else if (facing == "left") { hero.animate("standLeft"); hero.mirrorSprite(); // we need to call this again because the sprite was mirrored // if we don't mirror the sprite again, standLeft will look like standRight } } // player is pressing this key down if (keyDown(KEY_Z)) { // this is the function that adds the value of chargedTime every second // this keyDown block of code will be run every frame, which is less than a second // your engine of choice should have a way to tell whether a second has passed or not addChargedTime(); }









تتحرك شخصية بطلنا الجديدة لليسار ، واليمين ، والقفزات وفقًا لمدخلاتنا ، وتطلق أيضًا الرصاص ، سواء كان طبيعيًا أو نصف مشحونًا أو مشحونًا بالكامل.
تنفيذ الاعداء
لدينا الآن بطل يمكن السيطرة عليه. دعونا نسميها زيون من أجل البساطة. لدينا الآن بطل يمكن السيطرة عليه. دعونا نسميها زيون من أجل بساطة. يمكنه أداء بعض الحركات الأساسية مثل المشي والقفز والرماية. هذا جيد! ولكن ما هو جيد هو القدرة على تبادل لاطلاق النار دون شيء لاطلاق النار عليه ، أليس كذلك؟ هذا هو السبب في أننا سنصنع عدونا الأول هذه المرة.
دعونا نصمم سمات عدونا قبل البدء في ترميزها.
-
الصحة: كم عدد الصحة التي حددها عدونا كم عدد اللقطات (وأي نوع) مطلوب لتدميرها.
-
السلطة: قوة الهجوم العدو ، وكم من الضرر لا تعامل لاعبنا.
-
ShotAngle: إلى أي اتجاه يطلق النار على العدو ، يمكن تركه أو حقه أو في أي مكان نريد.
هذا إلى حد كبير ما نحتاجه لعدونا ، والآن دعونا نجعل فئة العدو / الكائن.
فئة / عدو العدو يشبه إلى حد كبير فئة / كائن اللاعب ، باستثناء أن العدو لا يستمع إلى مدخلات اللاعب. وبسبب ذلك نحتاج إلى استبدال الأجزاء التي يستمع فيها البطل إلى مدخلات اللاعب ، إلى AI / log العدو.
عدو هجوم AI
بالنسبة للمبتدئين ، دعونا نتعامل مع منظمة العفو الدولية الأساسية اطلاق النار. العدو سوف يطلق النار على اللاعب عندما يرى اللاعب.
لتحديد ما إذا كان العدو "يرى" اللاعب ، سنحتاج إلى تحديد متغير لعدو يدعى وجهه وهو عبارة عن سلسلة تخزن واحدة من قيمتين ، "اليسار" أو "اليمين".
يحتاج العدو أيضًا إلى نوع من الرؤية ، وهذا هو السبب في أننا سنجعل متغيرًا آخر يسمى النطاق. إذا كان اللاعب ضمن هذا النطاق ، فهذا يعني أن العدو "يرى" اللاعب. الشفرة الزائفة هي كما يلي:
function boolean checkSees() { if (facing == "left" && hero.x >= enemy.x -- range) { return true; } if (facing == "right" && hero.x <= enemy.x + range) { return true; } return false; }



checkSees () وظيفة
ربما كنت قد لاحظت شيئًا في هذا الكود الزائف: فهو لا يعتبر موقف البطل ، لذا سيظل العدو يطلق النار على البطل حتى لو كان في منصات ذات ارتفاعات مختلفة.
في الوقت الحالي ، سيكون هذا كافياً ، لأن وضع خوارزمية خط البصر يقع خارج نطاق هذا البرنامج التعليمي. في اللعبة الخاصة بك ، قد ترغب في إضافة تسامح Y في تلك الوظيفة أعلاه التي ستتحقق ما إذا كان موقف البطل y بين نقطتين تحددان ارتفاع العدو.
مما يجعل الأعداء تبادل لاطلاق النار
شفرة pseudocode لتصوير العدو هي كما يلي:
// can be in update() or somewhere else that's executed every frame function update() { if (checkSees()) { shoot("path/to/bulletSprite.png", enemyPower, 400, shotAngle); } }
كما ترون ، وظيفة تبادل لاطلاق النار العدو () مماثلة لتلك التي لاعب. يستغرق مسار السبرايت ، قوة الهجوم ، سرعة الرصاص ، وزاوية التصوير كمعلمات.
حركة العدو منظمة العفو الدولية
متى يتحول العدو من مواجهة اليسار إلى مواجهة اليمين؟ بالنسبة إلى بطلنا ، نستخدم إدخال اللاعبين لتغيير الاتجاه الذي يواجهه بطلنا. بالنسبة لعدونا ، لدينا خياران: استخدام نوع من الموقت للتحويل إلى الاتجاه المواجه كل بضع ثوانٍ مع وجود العدو للوقوف بلا حراك ، أو جعل العدو يسير إلى بقعة معينة ثم تبديل اتجاهه المواجه ثم السير إلى مكان آخر لتبديل اتجاهها المواجه مرة أخرى.
يمكن استخدام هذه الطريقة الثانية كدورية منظمة العفو الدولية. بالطبع ، يمكننا فقط أن نجعل العدو يسير في اتجاه واحد ولن نعود أبدا.
يكون pseudocode للطريقة الأولى كما يلي:
function switchingAI() { // elapsedTime() is a function that counts how many seconds has passed since its value is reset // I assume your engine of choice have this kind of functionality if (elapsedTime() > 4.0) { if (facing == "left") { facing = "right"; shotAngle = 0; } if (facing == "right") { facing = "left"; shotAngle = 180; } enemy.mirrorSprite(); // also flip the sprite horizontally resetTime(); // resets the time that counts in elapsedTime() } }



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



الآن دعونا نكتب pseudocode لدينا للذكاء الاصطناعى للعدو AI:
function patrollingAI() { if (facing == "right") { walkRight(); // the same as the one in player object / class if (collidesWith(rightPatrolBorder)) { facing = "left"; enemy.mirrorSprite(); } } if (facing == "left") { walkLeft(); if (collidesWith(leftPatrolBorder)) { facing = "right"; enemy.mirrorSprite(); } } }



بعد ذلك ، سوف يقوم العدو بدوريات بين نقطتين كما نريد.
ولإعداد الذكاء الاصطناعي الذي يستخدمه العدو ، سنقوم بإضافة متغير واحد آخر بنوع سلسلة لعدونا: العدو AI. سيحدد هذا ما تستخدمه منظمة العفو الدولية لكل إطار ، مثل:
if (enemyAI == "switching") { switchingAI(); } else if (enemyAI == "patrolling") { patrollingAI(); }
بالطبع يمكنك إضافة المزيد من نوع AI العدو إذا كنت تريد.
تغيير بالرصاص
دعونا نذهب حول كيف يمكننا أن نجعل الاختلافات بالرصاص لكل من اللاعب والعدو. نحن بصدد صنع الاختلافات بالرصاص من خلال تغيير شيئين: زاوية التصوير ، وعدد الرصاصة.
بهذه الطريقة يمكننا أن نطلق رصاصة واحدة بسيطة ، أو رصاصة ثلاثية موجهة. قبل أن نفعل ذلك سنجعل متغير آخر إلى كائن / فئة العدو المسمى shotAI ، وهو عبارة عن سلسلة. سنستخدم هذا في checkSees () لدينا التحقق من كتلة ، حيث يطلق النار على العدو. ستكون التغييرات التي يتم إجراؤها على كتلة الرموز هذه كما يلي:
// can be in update() or somewhere else that's executed every frame function update() { if (checkSees()) { if (shotAI == "simple") { shoot("path/to/bulletSprite.png", enemyPower, 400, shotAngle); } if (shotAI == "threeBullets") { shootThreeBullets(); } } }



بالطبع ، اسم الذكاء الاصطناعي ونوع النار الذي يطلقه العدو متروك لكم ، هذا مجرد مثال.
الآن ، دعونا نعمق في ما هو داخل وظيفة shootThreeBullets ().
Function shootThreeBullets() { if (facing == "right") { shoot("path/to/bulletSprite.png", enemyPower, 400, 0); // this bullet goes straight to the right shoot("path/to/bulletSprite.png", enemyPower, 400, 330); // this goes up by 30 degrees shoot("path/to/bulletSprite.png", enemyPower, 400, 30); // this goes down by 30 degrees } if (facing == "left") { shoot("path/to/bulletSprite.png", enemyPower, 400, 180); // this bullet goes straight to the left shoot("path/to/bulletSprite.png", enemyPower, 400, 210); // this goes up by 30 degrees shoot("path/to/bulletSprite.png", enemyPower, 400, 150); // this goes down by 30 degrees } }
إذا لم تكن متأكدًا من سبب انتقال 0 إلى اليمين و 180 من اليسار إلى اليسار ، فذلك لأن اتجاه 0 درجة يسير بشكل مستقيم إلى الجانب الأيمن من الشاشة ، و 90 درجة تذهب إلى الجانب السفلي من الشاشة ، وهكذا حتى تصل إلى 360 درجة. بمجرد معرفة القيمة التي تذهب إليها ، يمكنك إنشاء صيغة اللقطة الخاصة بك.
يمكننا أيضًا عمل متغير shotAI للاعب ، لكنني أفضل أن نقوم بتسميته selectShot ، لأن لاعبنا سيختار الرمز النقطي بدلاً من مبرمج من البداية. تي
هو المنطق في الميجيمان في كل مرة يهزم فيها الميجوران رئيسه ، يحصل على قوة المدير كطلقة جديدة. سأحاول إعادة إنشاء هذا المنطق. للقيام بذلك ، نحتاج إلى مصفوفة تحتوي على لقطات اللاعب ، بما في ذلك لقطات عادية. الشفرة الزائفة هي كالتالي:
var shotArr = ["normalShot", "boss1", "boss2"]; var shotIndex = 0; var selectedShot = "normalShot"; function update() { // this is the block of code in player's update function where the player shoots a bullet // this function changes the bullet that the player shoots changeBullet(); // player press this key once if (keyPressed(KEY_Z)) { if (facing == "right") { hero.animate("Shoot"); if ( selectedShot == "normalShot") { hero.shoot("path/to/sprite.png", 10, 400, 0); } else if ( selectedShot == "boss1") { // add codes to shoot the kind of shot that the player received after defeating boss 1 } } } } function changeBullet() { // changes shotIndex based on button pressed if (keyPressed(KEY_E)) { shotIndex += 1; } if (keyPressed(KEY_Q)) { shotIndex -= 1; } // fix shotIndex if it's out of array's length if (shotIndex == shotArr.length) { shotIndex = 0; } if (shotIndex < 0) { shotIndex = shotArr.length -- 1; } selectedShot = shotArr[shotIndex]; }

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






سنقوم بدفع عناصر جديدة لقتل arr عندما يهزم اللاعب رئيسه.
ترقية لاعب الرصاص
تمامًا مثل تصوير ثلاث عيارات نارية () ، يمكنك أن تكون مبدعًا وأن تصنع أشكالًا مختلفة من اللقطات. بما أن هذا هو رصاصة البطل ، دعنا نعطيه شيئًا مميزًا.
لنجعل نوعًا واحدًا من اللقطات فعالة ضد رئيس معين ، بحيث يتسبب في المزيد من الضرر. للقيام بذلك ، سنقوم بإنشاء متغير لكائن التعداد النقطي المسمى strongAgainst وهو متغير نوع سلسلة آخر يحتوي على اسم المدير الذي يكون هذا الرمز النشط فعالاً. سنضيف هذه الصفقات المزيد من وظائف الضرر عندما نناقش الجزء الرئيسي من اللعبة
الصحة والموت
هذا هو المكان الذي تبدأ فيه جميع الاختلافات في اللقطات التي نتناولها في الواقع. هذا هو المكان الذي يدمر فيه بطلنا ويقتل العدو ، والعكس الآخر.
لنبدأ ، لنصنع متغيرًا لكلٍّ من البطل وجسم العدو ، المسمى بالصحة وهو int ، ومتغير آخر للبطل المسمى بالأرواح. دعونا نلقي نظرة على pseudocode:
if (bullet.collidesWith(hero)) { hero.health -= bullet.power; createExplosion(); // for now we don't have an explosion sprite, so this will act as a reminder } // check if the hero is dead if (hero.health <= 0) { hero.lives -= 1; // decreases hero's total number of lives. destroyHero(); }
سنجعل نفس pseudocode لأعداء مدمرين ، مثل:
if (bullet.collidesWith(enemy)) { enemy.health -= bullet.power; createExplosion(); } if (enemy.health <= 0) { destroyEnemy(); }



شريط الصحة واجهة المستخدم الرسومية
الآن ، إذا تركتها عندها لن تكون مثيرة للاهتمام. لذلك سأقوم بعمل عجينة مستطيلة في أعلى الزاوية اليسرى من الشاشة التي تعمل بمثابة شريط صحي لبطلنا.
هذا الشريط الصحي سيتغير تبعا لصحة بطلنا الحالية. الصيغة لتغيير طول الشريط الصحي هي:
// this is in the update function healthBar.width = (hero.health / hero.maxHealth) * 100;



نحن بحاجة إلى متغير واحد آخر لبطلنا يسمى maxHealth؛ القيمة الصحية الكاملة للبطل لدينا. في الوقت الحالي لا يمكن تغيير هذه القيمة ولكن ربما في المستقبل يمكننا إنشاء عنصر يزيد من حجم maxHealth في Hero.
خلق لعبة العالم
الآن بعد أن أنشأنا بطلنا وعدواتنا وإطلاق النار ، نحتاج إلى إنشاء مستويات متعددة ورؤساء.
للحصول على مستويات متعددة يعني أنه في مرحلة ما في اللعبة ، سيصل اللاعب إلى نقطة تفتيش واحدة أو أكثر ، والتي تحول اللعبة من المستوى 1-1 إلى المستوى 1-2 إلى المستوى 1-3 وما إلى ذلك حتى تصل إلى المدير.
عندما يموت اللاعب في مكان ما في المستوى 1-2 ، فهو لا يحتاج إلى إعادة اللعب من البداية إلى المستوى 1-1. كيف أفعل هذا؟ سنقوم أولاً بتكوين المستوى ، ولن أشرح الكثير عن مستوى التصميم ، ولكن هنا هو مستوى المثال في Construct 2



الصورة في أعلى الزاوية اليسرى هي طبقة HUD. سيتم التمرير ، تتبع البطل عند لعب اللعبة.
الأبواب ونقاط التفتيش
أحد الرموز المتحركة التي يجب الانتباه إليها هي السبرايت الأخضر في الجزء العلوي الأيمن من المستوى. إنها نقطة التفتيش في هذا المستوى عندما يصطدم بها البطل سننقل اللعبة إلى المستوى 1-2.
للتعامل مع مستويات متعددة ، نحتاج إلى ثلاثة متغيرات: currentLevel ، و levelName ، و nextLevel.
يتم إنشاء متغير currentLevel في كائن / فئة البطل. يتم إنشاء اسم المستوى في كائن المشهد (المستوى) اللعبة لكل مستوى. يتم إنشاء متغير nextLevel في كائن sprite الأخضر.
المنطق هو كما يلي: عندما يصطدم البطل بالعفريت الأخضر (أسميها greenDoor) ، سوف نغير مستوىنا إلى مشهد اللعبة الذي يكون فيه levelName هو نفس المتغير nextLevel. بعد أن نغير المستوى ، سنغير قيمة المتغير currentLelo الخاص بالبطلة إلى نفس مستوى مستوى اسم لعبة المشهد. هنا هو pseudocode:
// this is inside game's update function if (hero.collidesWith(greenDoor)) { changeLevelTo(greenDoor.nextLevel); }



تهيئة مستوى جديد
هنا هو pseudocode للتعامل مع عندما يتم تحميل المستوى التالي وجاهزة للعب.
// this is the function that's triggered when the new level is loaded function onStart() { hero.currentLevel = scene.LevelName; hero.x = startPos.x; hero.y = startPos.y; }



مشغلات بدء المشغل
الآن بعد أن تغيرنا إلى مستوى جديد سأشرح السبورة البرتقالية خلف بطلنا في صورة تصميم المستوى أعلاه. هذا العفريت البرتقالي هو كائن أسميه startPos. يتم استخدامه لوضع علامة على بداية كل مستوى.
نشير إلى هذا الكائن عندما تغير البطل مستوياته أو مات ، حتى نعرف أين نفرغه.
هنا هو pseudocode للتعامل عندما يموت البطل:
// the function that's triggered when the hero is destroyed Function onDestroyed() { // revives hero if the hero still have lives. If (hero.lives > 0) { var newHero = new Hero(); newHero.x = startPos.x; newHero.y = startPos.y; } }
الآن يمكننا أن نحصل على مستويات متعددة ويمكننا أيضًا أن نسلّم البطل بعد موته.
يمكنك إنشاء العديد من المستويات كما تريد ، أو حتى إنشاء جهازي greenDoor في المستوى الذي يعود أحدهما إلى المستوى 1-1 إذا حل اللاعب لغزًا بطريقة خاطئة.
معارك بوس
لقد حان الوقت لتنفيذ الرئيس نفسه. لجعل مستوى رئيسه هو بسيط مثل جعل مستوى آخر الذي سوف تفرخ رئيسه بدلا من الأعداء العادية.
الجزء الصعب هو خلق الذكاء الاصطناعي للمدرب لأن كل رئيس سيكون لديه منظمة العفو الدولية الفريدة. ولكي نجعل من السهل فهم وتكرار الكثير من الرؤساء ، فإنني سأجعل من رئيس منظمة العفو الدولية يعتمد على الوقت بعد أن يتم إنتاجه. مما يعني أنها ستفعل A لـ x ثانية ، ثم تتغير إلى B لـ y ثانية ثم تقوم بـ C لمدة دقيقة z قبل أن تعود إلى A. سيبدو pseudocode كما يلي:
// this code is inside the boss update function, so it's executed every frame if (elapsedTime() > 2.0) { // this if block is executed for 3 seconds, because the difference in time with the if block below // is three seconds. BossShot1(); // shot variation to be executed this time } else if (elapsedTime() > 5.0) { bossShot2(); } else if (elapsedTime() > 6.0) { bossShot3(); } if (elapsedTime() > 7.0) { // reset the time so the boss executes the first action again resetsTime(); }



تعريف وظائف اطلاق النار رئيس أدناه. لا تتردد في تغييره ليتناسب مع ما تريده لمحاربة قتال:
function bossShot1() { // a simple straight shot bossEnemy.shoot("path/to/bullet/sprite.png", bossPower, 400, shotAngle); // shotAngle is 180 } function bossShot2() { // a three direction bullets shot bossEnemy.shoot("path/to/bullet/sprite.png", bossPower, 400, shotAngle); bossEnemy.shoot("path/to/bullet/sprite.png", bossPower, 400, shotAngle + 30); bossEnemy.shoot("path/to/bullet/sprite.png", bossPower, 400, shotAngle - 30); } function bossShot3() { // for this one, I'm going to make a circle shot, so the bullets will form a circle for (var i = 0; i <= 9; i++) { bossEnemy.shoot("path/to/bullet/sprite.png", bossPower, 400, 36 * i); // change the shotAngle } }
الأمر متروك لك لإضافة الاختلافات بالرصاص لروتين رئيسه. المتغيرات المستخدمة من قبل كائن العدو الرئيسي هي نفسها ككائن العدو ، باستثناء أن الرؤساء لا يستخدمون العدو ومتغيرات shotAI ، حيث سيتم التعامل مع كليهما في حالة وجود كتلة فوق حسب الوقت المنقضي.
أعداء بوس لديهم أيضا متغير أن كائنات العدو لا. يطلق عليه rewardShot ، وهي سلسلة. يحمل هذا المتغير اسم اللقطة التي سيحصل عليها اللاعب بعد هزيمة رئيسه (تلك الموجودة في متغير مصفوفة shotArr).
هذا سيسمح للاعب "بتعلم" هجوم المدير كما هو موضح في وقت سابق. لإضافة هذا النوع من اللقطات إلى مجموعة لقطات اللاعبين ، سنحتاج إلى إضافة الشفرة التالية بعد وفاة العدو:
function bossShot1() { // a simple straight shot bossEnemy.shoot("path/to/bullet/sprite.png", bossPower, 400, shotAngle); // shotAngle is 180 } function bossShot2() { // a three direction bullets shot bossEnemy.shoot("path/to/bullet/sprite.png", bossPower, 400, shotAngle); bossEnemy.shoot("path/to/bullet/sprite.png", bossPower, 400, shotAngle + 30); bossEnemy.shoot("path/to/bullet/sprite.png", bossPower, 400, shotAngle - 30); } function bossShot3() { // for this one, I'm going to make a circle shot, so the bullets will form a circle for (var i = 0; i <= 9; i++) { bossEnemy.shoot("path/to/bullet/sprite.png", bossPower, 400, 36 * i); // change the shotAngle } }
لتنفيذ أنواع إضافية من التذييلات لكي يستمتع بها لاعبوكم ، كل ما عليك فعله هو إضافة الشفرة المناسبة لكل مكافأة إطلاقها ، تمامًا كما فعلنا سابقًا.
تهانينا!
لقد قمت بإكمال البرنامج التعليمي الخاص بي حول كيفية إنشاء لعبة platformer تشبه لعبة Metroidvania تشبه لعبة Megaman في Construct 2. والبقية هي بناء اللعبة بناءً على ما تعلمته هنا لجعله ملكًا لك. إضافة المزيد من أنواع العدو ، والمزيد من المستويات ، بووروبس وأكثر من ذلك. حظا جيدا و استمتع.