Menu
Coddy logo textTech

מערכת דמויות במשחק

חלק מהיחידה תכנות מונחה עצמים במסלול ה-GO של Coddy. שיעור 107 מתוך 107.

challenge icon

אתגר

קל

בואו נבנה מערכת דמויות למשחק שמשלבת הטמעת struct, ממשקים ותבנית Strategy ליצירת מכניקות לחימה גמישות! תתכננו סוגי דמויות שונים שיכולים לתקוף ולהגן בדרכים ייחודיות, עם יכולות שאפשר להחליף בזמן הריצה.

תארגנו את הקוד בחמישה קבצים:

  • character.go: הגדירו struct בסיסי בשם Character עם שדות משותפים: Name (string), Health (int) ו-MaxHealth (int). כללו בנאי NewCharacter ואת המתודות:
    • IsAlive() bool: מחזירה true אם הבריאות גדולה מ-0
    • Status() string: מחזירה את הפורמט: [Name]: [Health]/[MaxHealth] HP
  • interfaces.go: הגדירו את ממשקי הלחימה שמאפשרים פולימורפיזם:
    • הממשק Attacker עם Attack() int: מחזיר את הנזק שנגרם
    • הממשק Defender עם TakeDamage(damage int) int: מפחית את הנזק ומחזיר את הנזק שנגרם בפועל
    • הממשק Ability עם Use() string: מחזיר תיאור של השפעת היכולת
  • characters.go: צרו שלושה סוגי דמויות שמטמיעים את Character ומממשים גם את Attacker וגם את Defender:

    Warrior: יש לו שדות Strength (int) ו-Armor (int). Attack מחזירה את Strength כנזק. TakeDamage מפחיתה את הנזק הנכנס ב-Armor (הנזק שנגרם בפועל יהיה לפחות 1).

    Mage: יש לו שדות Intelligence (int) ו-MagicResist (int). Attack מחזירה את Intelligence * 2 כנזק. TakeDamage מפחיתה את הנזק הנכנס ב-MagicResist / 2 (הנזק שנגרם בפועל יהיה לפחות 1).

    Archer: יש לו שדות Agility (int) ו-Evasion (int). Attack מחזירה את Agility + 5 כנזק. TakeDamage: אם Evasion > 10, מפחיתה את הנזק ב-25% (חלוקה של מספרים שלמים), אחרת נגרם מלוא הנזק (לפחות 1).

    כללו בנאים לכל סוג: NewWarrior, NewMage, NewArcher, שמקבלים שם, בריאות וערכים של המאפיינים הספציפיים לסוג.

  • abilities.go: ממשו את תבנית Strategy עם יכולות שניתן להחליף:

    struct בשם FireSpell עם Power (int). המתודה Use() שלו מחזירה: Casts fireball for [Power] damage

    struct בשם IceSpell עם Power (int). המתודה Use() שלו מחזירה: Casts ice shard for [Power] damage

    struct בשם PowerStrike עם Multiplier (int). המתודה Use() שלו מחזירה: Performs power strike at [Multiplier]x damage

    הוסיפו למבנה Mage שדה ability מסוג Ability, יחד עם המתודות SetAbility(a Ability) ו-UseAbility() string. אם ability הוא nil, UseAbility מחזירה No ability set.

  • main.go: צרו דמויות והדגימו את מכניקות הלחימה.

    קראו את סוג הדמות (warrior, mage או archer), ואז קראו את שם הדמות, את הבריאות ואת המאפיינים הספציפיים לסוג. צרו את הדמות המתאימה.

    לאחר מכן קראו את מספר הפעולות. כל פעולה היא אחת מהבאות:

    • status: הדפיסו את מצב הדמות
    • attack: הדפיסו [Name] attacks for [damage] damage
    • defend [amount]: החילו נזק והדפיסו [Name] takes [actual] damage
    • setability [type] [power]: עבור קוסמים בלבד, הגדירו יכולת (הסוג הוא fire, ice או strike)
    • useability: עבור קוסמים בלבד, הדפיסו את התוצאה של Use() עבור היכולת

הקלט שיסופק:

  • סוג הדמות, שמה, הבריאות שלה והמאפיינים הספציפיים לסוג
  • מספר הפעולות, ולאחר מכן כל פעולה בשורה נפרדת

לדוגמה, עבור הקלט:

warrior
Thorin
100
15
8
5
status
attack
defend 20
status
defend 100

הפלט שלכם צריך להיות:

Thorin: 100/100 HP
Thorin attacks for 15 damage
Thorin takes 12 damage
Thorin: 88/100 HP
Thorin takes 92 damage

ועבור הקלט:

mage
Gandalf
80
25
10
6
attack
useability
setability fire 30
useability
setability ice 25
useability
defend 40

הפלט שלכם צריך להיות:

Gandalf attacks for 50 damage
No ability set
Casts fireball for 30 damage
Casts ice shard for 25 damage
Gandalf takes 35 damage

ועבור הקלט:

archer
Legolas
90
18
15
4
status
attack
defend 24
status

הפלט שלכם צריך להיות:

Legolas: 90/90 HP
Legolas attacks for 23 damage
Legolas takes 18 damage
Legolas: 72/90 HP

שימו לב איך הטמעת struct מאפשרת לכל סוגי הדמויות לשתף את השדות והמתודות של Character הבסיסי, בעוד שממשקים מאפשרים לחימה פולימורפית שבה כל Attacker יכול לגרום נזק וכל Defender יכול לספוג אותו. תבנית Strategy באה לידי ביטוי ביכולות הניתנות להחלפה של Mage: אותה דמות יכולה להשתמש בכשפים שונים בלי לשנות את המימוש הבסיסי שלה!

נסו בעצמכם

package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
	"strings"
)

func main() {
	scanner := bufio.NewScanner(os.Stdin)

	// קרא את סוג הדמות
	scanner.Scan()
	charType := strings.TrimSpace(scanner.Text())

	// קרא את שם הדמות
	scanner.Scan()
	name := strings.TrimSpace(scanner.Text())

	// קרא את ערך הבריאות
	scanner.Scan()
	health, _ := strconv.Atoi(strings.TrimSpace(scanner.Text()))

	// קרא את הנתון הראשון הייחודי לסוג הדמות
	scanner.Scan()
	stat1, _ := strconv.Atoi(strings.TrimSpace(scanner.Text()))

	// קרא את הנתון השני הייחודי לסוג הדמות
	scanner.Scan()
	stat2, _ := strconv.Atoi(strings.TrimSpace(scanner.Text()))

	// TODO: צור את הדמות המתאימה לפי charType
	// השתמש במשתנים: charType, name, health, stat1, stat2
	// עבור לוחם: stat1 = Strength, stat2 = Armor
	// עבור קוסם: stat1 = Intelligence, stat2 = MagicResist
	// עבור קשת: stat1 = Agility, stat2 = Evasion

	// קרא את מספר הפעולות
	scanner.Scan()
	numActions, _ := strconv.Atoi(strings.TrimSpace(scanner.Text()))

	// עבד כל פעולה
	for i := 0; i < numActions; i++ {
		scanner.Scan()
		line := strings.TrimSpace(scanner.Text())
		parts := strings.Fields(line)
		action := parts[0]

		// TODO: טפלו בכל סוג פעולה:
		// - "status": הדפיסו את Status() של הדמות
		// - "attack": הדפיסו "[Name] attacks for [damage] damage"
		// - "defend [amount]": החילו נזק והדפיסו "[Name] takes [actual] damage"
		// - "setability [type] [power]": עבור קוסמים, הגדירו יכולת (fire, ice או strike)
		// - "useability": עבור קוסמים, הדפיסו את התוצאה של UseAbility()

		_ = action // הסירו את השורה הזו בעת המימוש
		_ = fmt.Println // הסירו את השורה הזו בעת המימוש
	}
}

כל השיעורים ביחידה תכנות מונחה עצמים

תרגלו בעצמכם: קומפיילר Go אונליין