פיתוח דמות במשחק
חלק מהיחידה תכנות מונחה עצמים במסלול ה-Java של Coddy. שיעור 86 מתוך 87.
באתגר הזה תבנה מערכת לפיתוח דמויות במשחק שמדגימה את העוצמה של OOP במידול ישויות מורכבות ואינטראקטיביות.
תכנן היררכיית דמויות שבה מחלקת בסיס Character מגדירה מאפיינים משותפים כמו בריאות, שם ורמה. מחלקות ייעודיות כגון Warrior, Mage ו־Archer מרחיבות את מחלקת הבסיס באמצעות יכולות ייחודיות וסגנונות לחימה.
ממשקים משתלבים כאן באופן טבעי: Attackable יכול להגדיר שיטות לחימה, בעוד Healable מטפל ביכולות ריפוי. Mage עשוי לממש את שניהם, ואילו Warrior מתמקד רק בהתקפה.
כדאי לשקול להשתמש בתבנית Strategy להתנהגויות תקיפה שונות, ולאפשר לדמויות להחליף סגנונות לחימה באופן דינמי. תבנית State יכולה לנהל מצבים של דמויות, כמו מצב רגיל, מורעל או המום, שמשפיעים על ההתנהגות.
חשוב על הרכבה במערכות ציוד, שבהן לדמויות "יש" כלי נשק ושריון במקום לרשת מהם. השתמש במנייה עבור מחלקות דמויות או סוגי נזק, והחל כימוס כדי להגן על הנתונים הסטטיסטיים מפני שינויים לא תקינים.
אתגר
קלבואו נבנה מערכת לפיתוח דמויות משחק שמדגימה את העוצמה של OOP במידול ישויות משחק מורכבות ואינטראקטיביות! תיצרו היררכיית דמויות עם סגנונות לחימה שונים, יכולות מיוחדות ומערכת מצבים שמשפיעה על התנהגות הדמויות.
תארגנו את הקוד שלכם בשבעה קבצים:
DamageType.java: צרו enum שמייצג סוגי נזק שונים במשחק. כללו את הערכים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. כללו getters לכל השדות. הוסיפו מתודה מוגנת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)כך שתרפא את הדמות (באמצעות 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), enums עם מתודות (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: פצלו את input לפי פסיקים כדי לקבל פקודות נפרדות
// 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 ---"
// לאחר מכן הדפס את המצב של כל דמות
}
}
השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.
כל השיעורים ביחידה תכנות מונחה עצמים
1יסודות התכנות מונחה העצמים
קבצים חיצונייםמבוא לתכנות מונחה עצמיםמחלקות לעומת אובייקטיםמילת המפתח thisמתודותשדות (מאפיינים)מתודת הבנאיהעמסת בנאיםחזרה – מחשבון פשוט4ירושה
ירושה בסיסית (extends)מילת המפתח superדריסת מתודות (@Override)שרשור בנאיםהמחלקה Objectירושה יחידה ורב־רמתיתלמה אין ירושה מרובה של מחלקותסיכום – היררכיית עובדים7מתודות מיוחדות ומחלקת Object
מתודת toString()equals() ו-hashCode()מתודת clone()compareTo() ו-Comparableהממשק Comparatorסיכום – מיון מותאם אישית2מצייני גישה ואנקפסולציה
סקירה של רמות הגישהמתודות Getter ו-Setterהסתרת מידעמילת המפתח finalסיכום – מנהל חשבון בנק5פולימורפיזם
יסודות העמסת מתודותדריסת מתודות (בזמן ריצה)המרה כלפי מעלה וכלפי מטההאופרטור instanceofמחלקות ומתודות מופשטותסיכום – מחשבון צורות8מושגי OOP מתקדמים
הרכבה מול ירושהאגרגציה מול הרכבהמחלקות פנימיות, מקוננות ואנונימיותEnums ומתודות של EnumRecords (Java 16+)מחלקות חתומות (Java 17+)3מאפייני מחלקה וחברים סטטיים
משתני מופע לעומת משתנים סטטייםמתודות סטטיותבלוקים סטטייםקבועים (static final)סיכום — מונה ומחלקת עזר6ממשקים ומחלקות מופשטות
מבוא לממשקיםמימוש ממשקיםמימוש של כמה ממשקיםמתודות ברירת מחדל וסטטיות בממשקמחלקות מופשטות לעומת ממשקיםממשקים פונקציונלייםסיכום – מערכת תשלומיםתרגלו בעצמכם: קומפיילר Java אונליין