تطوير شخصيات الألعاب
جزء من قسم البرمجة كائنية التوجه في رحلة Java على Coddy. الدرس 86 من 87.
يتحداك هذا التمرين لبناء نظام تطوير شخصيات اللعبة يبرز قوة البرمجة كائنية التوجه في نمذجة الكيانات المعقدة والتفاعلية.
صمّم تسلسلًا هرميًا للشخصيات، حيث تحدد الفئة الأساسية Character سمات مشتركة مثل الصحة والاسم والمستوى. وتمتد الفئات المتخصصة مثل Warrior وMage وArcher من هذه الفئة الأساسية بقدرات وأساليب قتال فريدة.
تعمل الواجهات بشكل طبيعي هنا: يمكن أن تحدد Attackable أساليب القتال، بينما تتولى Healable قدرات الاستعادة. وقد تطبّق Mage كليهما، في حين يركّز Warrior على الهجوم فقط.
فكّر في استخدام نمط Strategy لسلوكيات الهجوم المختلفة، مما يتيح للشخصيات تبديل أساليب combat ديناميكيًا. ويمكن لنمط State إدارة حالات character مثل الحالات العادية أو المسمومة أو المذهولة التي تؤثر في السلوك.
فكّر في التركيب لأنظمة المعدات، حيث تمتلك characters الأسلحة والدروع بدلًا من أن ترث منها. استخدم التعدادات لفئات class أو أنواع الضرر، وطبّق التغليف لحماية الإحصاءات من التعديلات غير الصالحة.
التحدي
سهللنُنشئ نظامًا لتطوير شخصيات لعبة يستعرض قوة البرمجة كائنية التوجه في نمذجة كيانات الألعاب المعقدة والتفاعلية! ستنشئ تسلسلًا هرميًا للشخصيات يتضمن أساليب قتال مختلفة، وقدرات خاصة، ونظام حالات يؤثر في كيفية تصرف الشخصيات.
ستنظم شيفرتك عبر سبعة ملفات:
DamageType.java: أنشئ تعدادًا يمثل أنواع الضرر المختلفة في اللعبة. ضمّن القيمPHYSICALوMAGICALوRANGED. أضف حقلًا خاصًاdescriptionمن النوع String، وبانيًا يعيّنه، ودالة getter. يجب أن يكون وصف الضرر الجسدي "brute force"، والضرر السحري "arcane energy"، والضرر بعيد المدى "precision strike".Attackable.java: أنشئ واجهةً تحدد إمكانات القتال. صرّح بالدوالattack(Character target)التي تُرجع قيمة String تصف الهجوم، وgetDamageType()التي تُرجع قيمة من النوع DamageType.Healable.java: أنشئ واجهةً لقدرات الاستعادة. صرّح بدالةheal(int amount)التي تُرجع قيمة String تصف إجراء التعافي.Character.java: أنشئ فئةً أساسيةً مجردةً لجميع شخصيات اللعبة. ضمّن حقولًا خاصةً لـnameمن النوع String، وhealthمن النوع int، وmaxHealthمن النوع int، وlevelمن النوع int. يأخذ الباني name وmaxHealth وlevel، ويضبط health مساويًا لـ maxHealth مبدئيًا. ضمّن دوال getter لجميع الحقول. أضف دالة محميةsetHealth(int health)تضمن بقاء health بين 0 وmaxHealth. أنشئ دالةtakeDamage(int damage)تقلل health وتُرجع"[name] takes [damage] damage! Health: [health]/[maxHealth]". أضف دالةisAlive()تُرجع true إذا كانت health أكبر من 0. صرّح بدالة مجردةgetCharacterClass()تُرجع قيمة من النوع String.Warrior.java: أنشئ فئةً تمتد من Character وتطبّق Attackable. أضف حقلًا خاصًاstrengthمن النوع int. يأخذ الباني name وmaxHealth وlevel وstrength. طبّقgetCharacterClass()لإرجاع"Warrior". طبّقgetDamageType()لإرجاعPHYSICAL. طبّقattack(Character target)لإحداث ضرر يساوي strength زائد level، واستدعاء دالة takeDamage الخاصة بالهدف، وإرجاع"[name] strikes with [damageType description] for [damage] damage!"متبوعًا بسطر جديد ونتيجة takeDamage.Mage.java: أنشئ فئةً تمتد من Character وتطبّق كلًا من Attackable وHealable. أضف حقلين خاصين هماintelligenceمن النوع int وmanaمن النوع int. يأخذ الباني name وmaxHealth وlevel وintelligence وmana. طبّقgetCharacterClass()لإرجاع"Mage". طبّقgetDamageType()لإرجاعMAGICAL. طبّقattack(Character target)لإحداث ضرر يساوي intelligence مضروبًا في 2، باتباع التنسيق نفسه الخاص بـ Warrior. طبّقheal(int amount)لاستعادة health (باستخدام setHealth) وإرجاع"[name] channels healing magic! Health: [health]/[maxHealth]".Main.java: اجعل نظام لعبتك ينبض بالحياة! ستتلقى سلسلة أوامر مفصولة بفواصل:CREATE_WARRIOR:name:maxHealth:level:strengthCREATE_MAGE:name:maxHealth:level:intelligence:manaATTACK:attackerName:targetNameHEAL:mageName:amountSTATUS:characterName
احتفظ بمجموعة من الشخصيات. عالج كل أمر واطبع:
- CREATE_WARRIOR/CREATE_MAGE:
[name] the [characterClass] joins the battle! - ATTACK: اطبع نتيجة الهجوم (قيمة String المُرجعة من دالة attack)
- HEAL: اطبع نتيجة التعافي، أو
[name] cannot healإذا لم تكن الشخصية Mage - STATUS:
[name] ([characterClass]) - Level [level] - Health: [health]/[maxHealth] - [Alive/Defeated]
بعد جميع الأوامر، اطبع
--- Battle Summary ---متبوعًا بحالة كل شخصية.
ستتلقى إدخالًا واحدًا: سلسلة الأوامر المفصولة بفواصل.
على سبيل المثال، عند استخدام الإدخال CREATE_WARRIOR:Thorin:100:5:15,CREATE_MAGE:Gandalf:80:7:12:50,ATTACK:Thorin:Gandalf,HEAL:Gandalf:25,STATUS:Gandalf، سيكون ناتجك:
Thorin the Warrior joins the battle!
Gandalf the Mage joins the battle!
Thorin strikes with brute force for 20 damage!
Gandalf takes 20 damage! Health: 60/80
Gandalf channels healing magic! Health: 80/80
Gandalf (Mage) - Level 7 - Health: 80/80 - Alive
--- Battle Summary ---
Thorin (Warrior) - Level 5 - Health: 100/100 - Alive
Gandalf (Mage) - Level 7 - Health: 80/80 - Aliveيجمع هذا التحدي بين الوراثة (التسلسل الهرمي لـ Character)، وتطبيق واجهات متعددة (يطبّق Mage كلًا من Attackable وHealable)، والتعدادات ذات الدوال (DamageType)، وتعدد الأشكال (التعامل مع أنواع الشخصيات المختلفة بطريقة موحّدة)، والتغليف (إدارة health المحمية) ضمن نظام لعبة تفاعلي!
جرّب بنفسك
import java.util.Scanner;
import java.util.HashMap;
import java.util.Map;
import java.util.LinkedHashMap;
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
String input = scanner.nextLine();
// TODO: استخدم مجموعة لإدارة الشخصيات (يحافظ LinkedHashMap على ترتيب الإدخال)
// LinkedHashMap<String, Character> characters = new LinkedHashMap<>();
// TODO: قم بتقسيم الإدخال بواسطة الفاصلة للحصول على الأوامر الفردية
// String[] commands = input.split(",");
// TODO: معالجة كل أمر:
// - CREATE_WARRIOR:name:maxHealth:level:strength
// Print: "[name] the Warrior joins the battle!"
//
// - CREATE_MAGE:name:maxHealth:level:intelligence:mana
// Print: "[name] the Mage joins the battle!"
//
// - ATTACK:attackerName:targetName
// اطبع نتيجة الهجوم من دالة attack
//
// - HEAL:mageName:amount
// اطبع نتيجة العلاج، أو "[name] cannot heal" إذا لم يكن Mage
//
// - STATUS:characterName
// اطبع: "[name] ([characterClass]) - Level [level] - Health: [health]/[maxHealth] - [Alive/Defeated]"
// TODO: After all commands, print "--- Battle Summary ---"
// ثم اطبع حالة كل شخصية
}
}
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس البرمجة كائنية التوجه
1أساسيات الـ OOP
الملفات الخارجيةمقدمة في الـ OOPالفئات (Classes) مقابل الكائنات (Objects)الكلمة المفتاحية thisالدوال (Methods)الحقول (Attributes)دالة البناء (Constructor)تعدد أشكال دالة البناء (Constructor Overloading)مراجعة - آلة حاسبة بسيطة4الوراثة
الوراثة الأساسية (extends)الكلمة المفتاحية superإعادة تعريف الدوال (@Override)تسلسل المنشئات (Constructor Chaining)كلاس Objectالوراثة الأحادية ومتعددة المستوياتلماذا لا توجد وراثة متعددة للكلاساتمراجعة - هيكلية الموظفين7الدوال الخاصة وفئة Object
دالة toString()equals() و hashCode()دالة clone()compareTo() و Comparableواجهة Comparatorمراجعة - الترتيب المخصص2محددات الوصول والتغليف
نظرة عامة على مستويات الوصولدوال Getter و Setterإخفاء البياناتالكلمة المفتاحية finalمراجعة - مدير الحساب البنكي5تعدد الأشكال (Polymorphism)
أساسيات الـ Method Overloadingالـ Method Overriding (في وقت التشغيل)الـ Upcasting والـ Downcastingمعامل الـ instanceofالفئات والدوال المجردة (Abstract)مراجعة - حاسبة الأشكال8مفاهيم OOP المتقدمة
الـ Composition مقابل الـ Inheritanceالـ Aggregation مقابل الـ Compositionالفئات الداخلية والمتداخلة والمجهولة (Inner, Nested & Anonymous)الـ Enums ودوالهاالـ Records (Java 16+)الـ Sealed Classes (Java 17+)11أنماط التصميم - الجزء الأول
مقدمة في أنماط التصميمنمط Singletonنمط Factoryنمط Builderنمط Observerنمط Strategy3خصائص الفئة والعضو الساكن
المتغيرات الخاصة بالكائن مقابل المتغيرات الساكنةالدوال الساكنةالكتل البرمجية الساكنةالثوابت (static final)ملخص - العداد والأدوات المساعدة6الواجهات (Interfaces) والأصناف المجردة (Abstract Classes)
مقدمة في الواجهات (Interfaces)تنفيذ الواجهات (Interfaces)تنفيذ واجهات متعددةالـ Default و Static في الواجهاتالأصناف المجردة مقابل الواجهاتالواجهات الوظيفية (Functional Interfaces)مراجعة - نظام الدفع9الأنواع العامة (Generics)
مقدمة في الأنواع العامةالأصناف العامة (Generic Classes)الدوال العامة (Generic Methods)معاملات الأنواع المقيدةالرموز البديلة (?, extends, super)مراجعة - الحاويات العامة12أنماط التصميم - الجزء الثاني
نمط الأمرنمط المحولنمط المزيننمط طريقة القالبنمط الحالةنمط التركيبنمط المكررتدرّب بنفسك: مترجم Java عبر الإنترنت