Как присвоить переменной тип "Скрипт" с последующим его выбором? Юнити С#

У меня есть 2 скрипта на объекте: 1ый "Enemy" и 2ой например "Goblin", класс Гоблин является наследуемый от Энеми и так как есть разные типы врагов то могут быть и другие скрипты "Golem" "Solder" и тд...

Есть отдельный скрипт, который вызывает void функцию о начале хода врага из наших скриптов Гоблинов и Солдат, которая в них называется одинаково но содержит разный код (например гоблин умеет ходить а голем нет).

Чтобы унифицировать код, я хочу в скрипт "Enemy" добавить переменную которая будет содержать ссылку на скрипт прикрепленный к врагу, определяющий его тип (гоблин, голем, солдат), но переменная требует "ТИП" при объявлении. Например:

public Goblin ScriptEnemyType;

Тем самым я могу прикрепить только определённый скрипт, а я хочу иметь возможность в скрипте "Гоблин" или "Солдат" назначать значение этой переменной скрипта "Enemy", которая будет вызывать одну и ту же функцию, но разную по наполнению.

Как правильно это сделать?


Ответы (1 шт):

Автор решения: ssa112112

То, что вы хотите сделать называется наследование.

Применительно к этой ситуации оно может выглядеть таким образом. Создаем абстрактный класс врага, это своего рода модель описывающая поведение (и иногда состояние) сущности. Суть абстрактного класса состоит в том, что его цель - чтобы от него наследовали. Напрямую от него создать экземпляр невозможно.

using UnityEngine;

public abstract class Enemy : MonoBehaviour
{
    protected int Health = 100; //к protected полям мы можем обращаться от наследников класса, но не из вне

    abstract public void Hurt(int value); //метод может быть виртуальным и содержать реализацию по умолчанию
}

Соответственно, дальше вы можете создавать не ограниченное кол-во типов врагов, например:

public class Goblin : Enemy
{
    public int armor = 1; //у гоблина есть броня, но её нет у абстрактного Enemy

    public override void Hurt(int value)
    {
        int damage = value - armor;
        if (damage > 0)
        {
            Health -= value;
        }
    }
}
public class Mag : Enemy
{
    public int magicAura = 10; // а у мага есть магическая аура, которая ведет себя не как броня

    public override void Hurt(int value)
    {
        if (value > magicAura)
        {
            Health -= value;
        }
    }
}

Теперь вы можете в любом месте использовать тип данных Enemy , например:

using UnityEngine;
using Random = UnityEngine.Random;

public class Player : MonoBehaviour
{
    private Enemy _enemy;

    private void Start()
    {
        _enemy = FindObjectOfType<Enemy>();
    }

    private void Update()
    {
        _enemy.Hurt(Random.Range(0,15));
    }
}

Естественно вы можете создавать цепочки наследования, типа Enemy -> Mag -> MagOfWater или интерфейсы выражающие отношение может быть, например:

public interface IVulnerable
{
    void Hurt(int value);
}
using UnityEngine;

public class Goblin : MonoBehaviour, IVulnerable
{
    public int armor = 1;
    private int _health = 100;

    public void Hurt(int value)
    {
        int damage = value - armor;
        if (damage > 0)
        {
            _health -= value;
        }
    }
}
using System.Linq;
using UnityEngine;
using Random = UnityEngine.Random;

public class Player : MonoBehaviour
{
    private IVulnerable _enemy;

    private void Start()
    {
        _enemy = FindObjectsOfType<MonoBehaviour>().OfType<IVulnerable>().First();
    }

    private void Update()
    {
        _enemy.Hurt(Random.Range(0,15));
    }
}

Вы также можете хранить определенный тип врага при помощи такого типа данных как enum перечисление. Оно создается с помощью ключевого слова enum И может содержать в себе некоторые строковые значения, каждое из которых равняется какому-либо числу. Подробнее можете почитать по ссылке.

public enum EnemyType {
    GOBLIN, // = 0 (по умолчанию нумерация с нуля)
    ORK, // = 1 
    JUST_BAD_GUY // = 2
}

После чего вы можете хранить тип врага в одной переменной типа данных названия перечисления:

public EnemyType enemyType = EnemyType.GOBLIN;
→ Ссылка