Комментарии (2)
Ответ сгенерирован нейросетью и может содержать ошибки
Пример наследования в C# для Unity
Наследование — это один из фундаментальных принципов объектно-ориентированного программирования (ООП), который позволяет создавать новый класс на основе существующего (родительского), заимствуя его свойства и методы, а также расширяя или изменяя его функциональность. В Unity это мощный инструмент для создания иерархий игровых объектов, систем управления и компонентов.
Базовый пример: создание иерархии персонажей
Рассмотрим классический пример с игровыми персонажами. У нас будет базовый класс Character, от которого наследуются более специализированные классы Warrior и Mage.
1. Базовый (родительский) класс Character
Этот класс определяет общие для всех персонажей свойства и поведение.
using UnityEngine;
public class Character : MonoBehaviour
{
// Общие защищенные поля, доступные в классах-
protected string characterName;
protected int health;
protected float moveSpeed;
// Конструктор для базовой инициализации
public Character(string name, int initialHealth, float speed)
{
characterName = name;
health = initialHealth;
moveSpeed = speed;
Debug.Log($"Создан персонаж: {characterName}");
}
// Общий метод для движения
public virtual void Move(Vector3 direction)
{
transform.Translate(direction * moveSpeed * Time.deltaTime);
Debug.Log($"{characterName} движется со скоростью {moveSpeed}");
}
// Общий метод для получения урона
public virtual void TakeDamage(int damage)
{
health -= damage;
Debug.Log($"{characterName} получает {damage} урона. Здоровье: {health}");
if (health <= 0)
{
Die();
}
}
// Общий метод, который могут переопределить потомки
protected virtual void Die()
{
Debug.Log($"{characterName} погибает.");
// Базовая логика уничтожения объекта
Destroy(gameObject);
}
// Общее свойство для чтения здоровья
public int Health => health;
}
2. Производный класс Warrior
Класс-воин наследует всё от Character и добавляет свою специфику.
public class Warrior : Character
{
// Новое уникальное поле
private int armor;
// Конструктор вызывает конструктор базового класса через base()
public Warrior(string name, int initialHealth, float speed, int armorValue)
: base(name, initialHealth, speed)
{
armor = armorValue;
Debug.Log($"Это воин с бронёй {armor}");
}
// Переопределение метода получения урона с учётом брони
public override void TakeDamage(int damage)
{
int reducedDamage = Mathf.Max(damage - armor,总量 1); // Минимум 1 урон
base.TakeDamage(reducedDamage); // Вызов родительской логики
Debug.Log($"Броня поглотила часть урона! Реальный урон: {reducedDamage}");
}
// Новый уникальный метод
public void ShieldBash()
{
Debug.Log($"{characterName} совершает удар щитом!");
// Логика атаки...
}
// Переопределение метода смерти для воина
protected override void Die()
{
Debug.Log($"Воин {characterName} пал в бою с честью!");
base.Die(); // При необходимости вызываем базовый метод
}
}
3. Производный класс Mage
Класс.маг также наследует Character, но реализует магическую специфику.
public class Mage : Character
{
// Новое уникальное поле
private int mana;
public Mage(string name, int initialHealth, float speed, int initialMana)
: base(name, initialHealth, speed)
{
mana = initialMana;
Debug.Log($"Это маг с {mana} единицами маны.");
}
// Переопределение метода движения (маги движутся медленнее при низкой мане)
public override void Move(Vector3 direction)
{
float currentSpeed = mana > 50 ? moveSpeed : moveSpeed * 0.5f;
transform.Translate(direction * currentSpeed * Time.deltaTime);
Debug.Log($"{characterName} движется со скоростью {currentSpeed} (мана: {mana})");
}
// Уникальный метод мага
public void CastSpell(string spellName, int manaCost)
{
if (mana >= manaCost)
{
mana -= manaCost;
Debug.Log($"{characterName} сотворяет заклинание '{spellName}'! Осталось маны: {mana}");
}
else
{
Debug.Log($"Недостаточно маны для заклинания '{spellName}'");
}
}
// Свойство только для чтения для доступа к мане
public int Mana => mana;
}
Как это работает в Unity
// Пример использования в другом скрипте (например, GameManager)
public class GameManager : MonoBehaviour
{
void Start()
{
// Создание экземпляров. В реальном проекте они бы были компонентами на GameObject.
Character genericCharacter = new Character("Базовый", 100, 5f);
Warrior knight = new Warrior("Артур", 150, 4f, 10);
Mage merlin = new Mage("Мерлин", 80, 3.5f, 200);
// Работа с общим интерфейсом родительского класса
Character[] party = new Character[] { knight, merlin };
foreach (Character character in party)
{
character.Move(Vector3.forward); // Вызовется переопределённый метод для каждого типа
character.TakeDamage(30); // Воин получит меньше урона из-за брони
}
// Использование специфичных методов
knight.ShieldBash(); // Только у Warrior есть этот метод
merlin.CastSpell("Огненный шар", 40); // Только у Mage есть этот метод
// Проверка типа (кастинг)
if (merlin is Mage)
{
Mage mageRef = merlin as Mage;
Debug.Log($"Мана мага: {mageRef.Mana}");
}
}
}
Ключевые преимущества наследования в Unity:
- Повторное использование кода (DRY): Общая логика (движение, здоровье) пишется один раз в базовом классе.
- Полиморфизм: Мы можем работать с массивами типа
Character, вызывая методы, которые будут выполнены в зависимости от реального типа объекта (WarriorилиMage). - Расширяемость: Легко добавлять новые типы персонажей (например,
Archer), наследуя отCharacterи добавляя уникальные особенности. - Организация кода: Чёткая иерархия классов упрощает понимание архитектуры проекта.
- Совместимость с Unity: Наследование от
MonoBehaviour— основа для создания компонентов. Вы можете создавать свои базовые компоненты (например,DamageableEntity), от которых наследуют конкретные реализации.
Важное замечание: В Unity часто используют композицию (добавление отдельных компонентов) вместе с наследованием. Например, вместо глубокой иерархии классов для всех типов врагов, вы можете создать базовый класс EnemyAI и добавлять к нему компоненты ShootingComponent, MeleeComponent и т.д. Наследование идеально подходит для создания "is-a" отношений (Warrior is a Character), а композиция — для "has-a" отношений (Character has a Inventory).