تطوير شخصيات الألعاب
جزء من قسم Object Oriented Programming في رحلة Java على Coddy — الدرس 86 من 87.
يتطلب منك هذا التحدي بناء نظام تطوير شخصيات الألعاب يستعرض قوة البرمجة كائنية التوجه (OOP) في نمذجة كيانات معقدة وتفاعلية.
صمم تسلسلاً هرمياً للشخصيات حيث تحدد فئة Character الأساسية سمات مشتركة مثل الصحة، والاسم، والمستوى. تقوم فئات متخصصة مثل Warrior و Mage و Archer بتوسيع هذه الفئة الأساسية بقدرات وأساليب قتالية فريدة.
تعمل الواجهات بشكل طبيعي هنا: يمكن لـ Attackable تحديد أساليب القتال، بينما يتولى Healable معالجة قدرات الاستعادة. قد يقوم Mage بتنفيذ كليهما، في حين يركز Warrior فقط على الهجوم.
فكر في استخدام نمط الاستراتيجية (Strategy pattern) لسلوكيات الهجوم المختلفة، مما يسمح للشخصيات بتبديل أساليب القتال بشكل ديناميكي. يمكن لنمط الحالة (State pattern) إدارة حالات الشخصية مثل الحالات الطبيعية، أو المسمومة، أو المذهولة التي تؤثر على السلوك.
فكر في استخدام التركيب (composition) لأنظمة المعدات حيث "تمتلك" الشخصيات أسلحة ودروعاً بدلاً من الوراثة منها. استخدم enums لفئات الشخصيات أو أنواع الضرر، وقم بتطبيق encapsulation لحماية الإحصائيات من التعديلات غير الصالحة.
التحدي
سهللنقم ببناء نظام تطوير شخصيات ألعاب يستعرض قوة البرمجة كائنية التوجه (OOP) في نمذجة كيانات الألعاب المعقدة والتفاعلية! ستقوم بإنشاء تسلسل هرمي للشخصيات بأنماط قتالية مختلفة، وقدرات خاصة، ونظام حالة يؤثر على سلوك الشخصيات.
ستقوم بتنظيم الكود الخاص بك عبر سبعة ملفات:
DamageType.java: أنشئ enum يمثل أنواعاً مختلفة من الضرر في اللعبة. قم بتضمين القيمPHYSICAL، وMAGICAL، وRANGED. أضف حقلاً خاصاًdescription(String)، ومنشئاً (constructor) يقوم بضبطه، وطريقة getter. يجب أن يكون للضرر الجسدي الوصف "brute force"، وللسحري "arcane energy"، وللمدى البعيد "precision strike".Attackable.java: أنشئ واجهة (interface) تحدد القدرات القتالية. صرّح عن الطرقattack(Character target)التي تعيد String يصف الهجوم، وgetDamageType()التي تعيد DamageType.Healable.java: أنشئ واجهة لقدرات الاستشفاء. صرّح عن طريقةheal(int amount)تعيد String يصف إجراء الشفاء.Character.java: أنشئ فئة أساسية مجردة (abstract base class) لجميع شخصيات اللعبة. قم بتضمين حقول خاصة لـname(String)، وhealth(int)، وmaxHealth(int)، وlevel(int). يأخذ المنشئ name، و maxHealth، و level، ويضبط health ليكون مساوياً لـ maxHealth في البداية. قم بتضمين getters لجميع الحقول. أضف طريقة محمية (protected) باسمsetHealth(int health)تضمن بقاء الصحة بين 0 و maxHealth. أنشئ طريقةtakeDamage(int damage)تقلل الصحة وتعيد"[name] takes [damage] damage! Health: [health]/[maxHealth]". أضف طريقةisAlive()تعيد true إذا كانت الصحة أكبر من 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)لاستعادة الصحة (باستخدام 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: اطبع نتيجة الهجوم (السلسلة النصية التي تعيدها طريقة 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)، والـ enums مع الطرق (DamageType)، وتعدد الأشكال (التعامل مع أنواع الشخصيات المختلفة بشكل موحد)، والتغليف (إدارة الصحة المحمية) في نظام لعبة ممتع!
ورقة مرجعية
تسمح البرمجة كائنية التوجه (OOP) بنمذجة الأنظمة المعقدة من خلال الفئات (classes)، والوراثة (inheritance)، والواجهات (interfaces)، وأنماط التصميم (design patterns).
التسلسل الهرمي للفئات
قم بإنشاء فئة أساسية بخصائص مشتركة وقم بتوسيعها بفئات متخصصة:
public abstract class Character {
private String name;
private int health;
private int maxHealth;
private int level;
public Character(String name, int maxHealth, int level) {
this.name = name;
this.maxHealth = maxHealth;
this.health = maxHealth;
this.level = level;
}
protected void setHealth(int health) {
this.health = Math.max(0, Math.min(health, maxHealth));
}
public abstract String getCharacterClass();
}الواجهات (Interfaces)
حدد العقود لسلوكيات معينة يمكن للفئات تنفيذها:
public interface Attackable {
String attack(Character target);
DamageType getDamageType();
}
public interface Healable {
String heal(int amount);
}التعدادات (Enums) مع الحقول
يمكن أن تحتوي التعدادات على حقول، ومنشئات (constructors)، وطرق (methods):
public enum DamageType {
PHYSICAL("brute force"),
MAGICAL("arcane energy"),
RANGED("precision strike");
private String description;
private DamageType(String description) {
this.description = description;
}
public String getDescription() {
return description;
}
}تنفيذ واجهات متعددة
يمكن لفئة واحدة تنفيذ واجهات متعددة:
public class Mage extends Character implements Attackable, Healable {
private int intelligence;
private int mana;
@Override
public String attack(Character target) {
// التنفيذ
}
@Override
public String heal(int amount) {
// التنفيذ
}
}التغليف (Encapsulation)
استخدم الحقول الخاصة (private fields) مع أدوات التعيين المحمية (protected setters) للتحكم في الوصول والتحقق من صحة البيانات:
protected void setHealth(int health) {
this.health = Math.max(0, Math.min(health, maxHealth));
}تعدد الأشكال (Polymorphism)
تعامل مع أنواع الشخصيات المختلفة بشكل موحد من خلال فئتها الأساسية المشتركة أو واجهاتها، مما يسمح بمجموعات مرنة واستدعاءات للطرق.
جرّب بنفسك
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: استخدم مجموعة (collection) للحفاظ على الشخصيات (يحافظ LinkedHashMap على ترتيب الإدخال)
// LinkedHashMap<String, Character> characters = new LinkedHashMap<>();
// TODO: قم بتقسيم المدخلات بواسطة الفاصلة للحصول على الأوامر الفردية
// String[] commands = input.split(",");
// TODO: معالجة كل أمر:
// - CREATE_WARRIOR:name:maxHealth:level:strength
// اطبع: "[name] the Warrior joins the battle!"
//
// - CREATE_MAGE:name:maxHealth:level:intelligence:mana
// اطبع: "[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: بعد كل الأوامر، اطبع "--- Battle Summary ---"
// ثم اطبع حالة كل شخصية
}
}
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس Object Oriented Programming
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أنماط التصميم - الجزء الثاني
نمط الأمرنمط المحولنمط المزيننمط طريقة القالبنمط الحالةنمط التركيبنمط المكرر