نظام شخصيات الألعاب
جزء من قسم البرمجة كائنية التوجه في رحلة GO على Coddy. الدرس 107 من 107.
التحدي
سهللنبنِ نظام شخصيات للعبة يجمع بين تضمين البنى، والواجهات، ونمط الاستراتيجية لإنشاء آليات قتال مرنة! ستصمم أنواعًا مختلفة من الشخصيات يمكنها الهجوم والدفاع بطرق فريدة، مع قدرات يمكن تبديلها أثناء التشغيل.
ستنظم شيفرتك عبر خمسة ملفات:
character.go: عرّف بنيةCharacterأساسية تحتوي على الحقول common:Name(string)، وHealth(int)، وMaxHealth(int). أدرج مُنشئًا باسمNewCharacterوالطرائق التالية:IsAlive() bool: تُرجع true إذا كانت الصحة أكبر من 0Status() string: تُرجع التنسيق:[Name]: [Health]/[MaxHealth] HP
interfaces.go: عرّف واجهات القتال التي تتيح تعدد الأشكال:- واجهة
AttackerمعAttack() int: تُرجع مقدار الضرر dealt - واجهة
DefenderمعTakeDamage(damage int) int: تطبّق تقليل الضرر وتُرجع مقدار الضرر actual المتلقّى - واجهة
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: طبّق نمط الاستراتيجية باستخدام قدرات قابلة للتبديل:بنية
FireSpellمعPower(int). تُرجعUse()الخاصة بها:Casts fireball for [Power] damageبنية
IceSpellمعPower(int). تُرجعUse()الخاصة بها:Casts ice shard for [Power] damageبنية
PowerStrikeمعMultiplier(int). تُرجعUse()الخاصة بها:Performs power strike at [Multiplier]x damageأضف حقل
ability(من النوعAbility) إلى بنية Mage، مع الطريقتينSetAbility(a Ability)وUseAbility() string. تُرجع UseAbility القيمةNo ability setإذا كانت ability تساوي nil.main.go: أنشئ الشخصيات واستعرض آليات القتال.اقرأ نوع الشخصية (
warriorأوmageأوarcher)، ثم اقرأ اسم الشخصية وصحتها وإحصاءاتها الخاصة بالنوع. أنشئ الشخصية appropriate.ثم اقرأ عدد actions. كل action هو أحد الخيارات التالية:
status: اطبع حالة الشخصيةattack: اطبع[Name] attacks for [damage] damagedefend [amount]: طبّق الضرر واطبع[Name] takes [actual] damagesetability [type] [power]: بالنسبة إلى السحرة فقط، عيّن القدرة (النوع هوfireأوiceأوstrike)useability: بالنسبة إلى السحرة فقط، اطبع نتيجة Use() الخاصة بالقدرة
ستُقدَّم المدخلات التالية:
- نوع الشخصية واسمها وصحتها وإحصاءاتها الخاصة بالنوع
- عدد actions، ثم كل action في سطر منفصل
على سبيل المثال، عند إعطاء:
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لاحظ كيف يتيح تضمين البنى لكل نوع من الشخصيات مشاركة حقول وطرائق Character الأساسية، بينما تتيح الواجهات تعدد الأشكال في القتال، حيث يمكن لأي Attacker إلحاق الضرر ويمكن لأي Defender تلقّيه. ويتألق نمط الاستراتيجية مع قدرات 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
// بالنسبة لـ warrior: stat1 = Strength, stat2 = Armor
// بالنسبة لـ mage: stat1 = Intelligence, stat2 = MagicResist
// بالنسبة لـ archer: 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, or strike)
// - "useability": للسحرة، اطبع نتيجة UseAbility()
_ = action // احذف هذا السطر عند التنفيذ
_ = fmt.Println // احذف هذا السطر عند التنفيذ
}
}
جميع دروس البرمجة كائنية التوجه
1أساسيات الـ OOP في Go
الملفات الخارجيةمساحة العمل والـ Modules في Goالـ Packages والـ Importsالأسماء المصدرة مقابل غير المصدرةمقدمة إلى الـ OOP في Goالـ Structs كـ Classesتعريف الـ Methods في الـ Structsالـ Pointer Receivers مقابل الـ Value Receiversتهيئة الـ Structدوال الـ Constructorمراجعة - آلة حاسبة بسيطة4الواجهات
مقدمة في الواجهاتالتنفيذ الضمنيالواجهة كعقدالواجهة الفارغة (any)تأكيد النوع (Type Assertion)تبديل النوع (Type Switch)تركيب الواجهاتواجهات Stringer و Errorمراجعة - حاسبة الأشكال7التغليف
الحقول المصدرة وغير المصدرةالتغليف على مستوى الحزمةدوال الـ Getter والـ Setterإخفاء المعلومات في Goملخص - سجلات الطلاب10الأنواع العامة (Go 1.18+)
مقدمة في الأنواع العامةمعاملات الأنواعقيود الأنواعالـ Structs العامةحل بديل للـ Methods العامةملخص - المجموعات العامة13أنماط التصميم - الجزء الأول
مقدمة في أنماط التصميمنمط Singletonنمط Factoryنمط Abstract Factoryنمط Observerنمط Strategy2تعمق في الأنواع و Structs
الأنواع الأساسية والمركبةتعريفات الأنواع المخصصةStruct TagsStructs مجهولةStructs متداخلةالقيم الصفرية والافتراضيةمراجعة - دفتر العناوين5التركيب بدلاً من الوراثة
لماذا لا تدعم Go الوراثةأساسيات تضمين الـ Structترقية الـ Methodتضمين عدة Structsالتضمين مقابل التجميعحجب الـ Methods المضمنةملخص - الهيكل الهرمي للموظفين8معالجة الأخطاء و OOP
واجهة errorأنواع الأخطاء المخصصةتغليف الأخطاء (fmt.Errorf)أخطاء Sentinelerrors.Is() و errors.As()Panic و Defer و Recoverملخص - File Parser11المكتبة القياسية والبرمجة كائنية التوجه (OOP)
io.Reader و io.Writerواجهة sort.Interfaceواجهة fmt.Stringerencoding/json مع Structsواجهة http.Handlerمراجعة - نماذج REST API14أنماط التصميم - الجزء الثاني
نمط الأمرنمط المحولنمط المزيننمط طريقة القالبنمط الحالةنمط التركيبMiddleware كنمط مزين3المؤشرات والذاكرة
أساسيات المؤشرات في Goالمؤشرات إلى الـ Structsالتمرير بالقيمة مقابل التمرير بالمرجعدالة ()newGarbage Collection في Goملخص - بناء Linked List6تعدد الأشكال في Go
تعدد الأشكال عبر InterfacesDuck Typing في Goقواعد استيفاء الـ Interfaceمجموعات متعددة الأشكالحقن التبعيةمراجعة - معالج الدفع9التزامن والبرمجة كائنية التوجه (OOP)
أساسيات الـ Goroutinesالقنوات (Channels) والاتصالالقنوات المخزنة (Buffered) مقابل غير المخزنةجملة Selectsync.Mutex و sync.RWMutexsync.WaitGroupتصميم الـ Structs الآمنة للخيوط (Thread-Safe)مراجعة - Worker Poolتدرّب بنفسك: مترجم Go عبر الإنترنت