Компонент игрового персонажа
Часть раздела Объектно-ориентированное программирование путешествия по C# на Coddy — урок 69 из 70.
Задание
ЛегкоДавайте создадим систему игровых персонажей, используя паттерн компонентной архитектуры! Вместо создания жестких иерархий наследования, вы будете составлять персонажей, прикрепляя независимые компоненты, которые управляют определенным поведением — это гибкий подход, широко используемый в разработке игр.
Вы организуете свой код в пяти файлах:
IComponent.cs: Определите интерфейсIComponentв пространстве именGame.Components. Каждый компонент должен иметь методUpdate(), который выводит информацию о том, что делает компонент.HealthComponent.cs: Создайте классHealthComponent, реализующийIComponent. Он должен отслеживать текущие и максимальные очки здоровья, имея методыTakeDamage(int amount)иHeal(int amount). Здоровье никогда не должно опускаться ниже 0 или превышать максимум. Включите событиеevent Action<int> HealthChanged, которое срабатывает при каждом изменении здоровья, передавая новое значение здоровья. МетодUpdate()должен выводить текущий статус здоровья.InventoryComponent.cs: Создайте классInventoryComponent, реализующийIComponent. Он управляет списком названий предметов с помощью методовAddItem(string item)иRemoveItem(string item)(возвращаетbool, указывающий на успех операции). МетодUpdate()должен выводить содержимое инвентаря.Character.cs: Создайте классCharacterв пространстве именGame, который содержит коллекцию компонентов. Вашему персонажу требуется свойствоNameи следующие методы:AddComponent(IComponent component)— прикрепляет компонент к персонажуGetComponent<T>()где T : IComponent — возвращает первый компонент типа T или null, если он не найденUpdateAll()— вызываетUpdate()для каждого прикрепленного компонента
Program.cs: Объедините все вместе, создав персонажа, прикрепив компоненты и обработав команды.
Вы получите следующие входные данные:
- Имя персонажа
- Начальное максимальное здоровье для HealthComponent
- Количество команд для обработки
- Для каждой команды: действие, за которым следуют соответствующие данные
Форматы команд:
damage
{amount}
heal
{amount}
add_item
{itemName}
remove_item
{itemName}
updateФорматы вывода:
- При изменении здоровья (через событие):
Health changed: {newHealth} - При добавлении предмета:
Added: {itemName} - При удалении предмета:
Removed: {itemName}илиItem not found: {itemName} - Метод
Update()у HealthComponent:Health: {current}/{max} - Метод
Update()у InventoryComponent:Inventory: {item1}, {item2}, ...илиInventory: empty
Например, если входные данные следующие:
Hero
100
7
add_item
Sword
add_item
Shield
damage
30
heal
10
remove_item
Potion
remove_item
Sword
updateВывод должен быть таким:
Added: Sword
Added: Shield
Health changed: 70
Health changed: 80
Item not found: Potion
Removed: Sword
Health: 80/100
Inventory: ShieldОбратите внимание, что персонаж не наследуется от чего-то особенного — он просто содержит компоненты, обеспечивающие поведение. Метод GetComponent<T>() использует дженерики для типобезопасного извлечения, а события уведомляют об изменении здоровья. Такой подход на основе композиции позволяет свободно комбинировать компоненты!
Попробуйте сами
using System;
using Game;
using Game.Components;
class Program
{
public static void Main(string[] args)
{
// Считать имя персонажа
string characterName = Console.ReadLine();
// Считать начальное максимальное здоровье
int maxHealth = Convert.ToInt32(Console.ReadLine());
// Считать количество команд
int numCommands = Convert.ToInt32(Console.ReadLine());
// TODO: Создать персонажа (Character) с заданным именем
// TODO: Создать HealthComponent с maxHealth и InventoryComponent
// TODO: Подписаться на событие HealthChanged для вывода: Health changed: {newHealth}
// TODO: Добавить оба компонента персонажу
// TODO: Обработать каждую команду
for (int i = 0; i < numCommands; i++)
{
string command = Console.ReadLine();
// TODO: Обработать каждый тип команды:
// - "damage": считать количество, вызвать TakeDamage у компонента здоровья
// - "heal": считать количество, вызвать Heal у компонента здоровья
// - "add_item": считать название предмета, вызвать AddItem у компонента инвентаря
// - "remove_item": считать название предмета, вызвать RemoveItem у компонента инвентаря
// - "update": вызвать UpdateAll у персонажа
}
}
}
Все уроки раздела Объектно-ориентированное программирование
1Основы ООП
Внешние файлыПространства имен и директивыВведение в классы и объектыКлючевое слово 'this'Методы и параметрыПоля и свойстваКонструкторыИнициализаторы объектовИтоги — Простой калькулятор4Наследование
Основы синтаксиса наследования (:)Ключевое слово 'base'Ключевые слова Virtual и OverrideЗапечатанные классыБазовый класс 'object'Итоги — Иерархия сотрудников7Продвинутые возможности
Перегрузка операторовИндексаторы (this[])Переопределение ToString()Методы расширенияПовторение — Пользовательский список10Паттерны проектирования. Часть 1
Введение в паттерны проектированияПотокобезопасный SingletonПаттерн FactoryПаттерн Observer (События)Паттерн Strategy13Финальные испытания
Платформа для онлайн-обученияКомпонент игрового персонажаСервис аренды транспорта2Свойства и статические члены
Автоматически реализуемые свойстваСвойства только для чтения и записиСтатические поля и методыСтатические классыЧлены в виде выражений5Полиморфизм и интерфейсы
Полиморфизм: компиляция vs выполнениеИнтерфейс vs абстрактный классМножественные интерфейсыЯвные интерфейсыUpcasting и DowncastingИтоги: Калькулятор фигур8Продвинутые концепции ООП
Композиция вместо наследованияGenerics (классы и методы)Делегаты и событияАтрибуты и рефлексияIDisposable и оператор usingОсновы Dependency Injection11Паттерны проектирования. Часть 2
Паттерн КомандаПаттерн АдаптерПаттерн ДекораторПаттерн Шаблонный методПаттерн СостояниеПаттерн Компоновщик3Архитектура классов
Экземплярные и статические данныеКлючевые слова 'readonly' и 'const'Опорные поляПовторение — Менеджер банковских счетов6Инкапсуляция
Модификаторы доступаСвойства для инкапсуляцииРеализация скрытия данныхПаттерны неизменяемостиИтоги — Записи студентов9Переменное количество аргументов
Ключевое слово 'params'Необязательные параметрыИменованные аргументыПерегрузка методов