Unity. Вопрос по организации кода

Рейтинг: -2Ответов: 1Опубликовано: 24.07.2023

У меня была обычная реализация кода "как в туториалах". Рассмотрю на примере скрипта игрока.

public class PlayerController : MonoBehaviour
{
    public Camera cam;
    public NavMeshAgent agent;

    private void Update() {
        if (Input.GetMouseButtonDown(0))
        {
            Move();
        }
    }

    /// <summary>
    /// Базовое движение
    /// </summary>
    private void Move()
    {
        Ray ray = cam.ScreenPointToRay(Input.mousePosition);
        RaycastHit hit;

        if (Physics.Raycast(ray, out hit))
        {
            agent.SetDestination(hit.point);
        }
    }
}

К дальнейшему рефакторингу кода сподвигло два момента:

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

Дальше сделал несколько изменений. Возможных игроков у меня 2. Это корабль и человек.

Я вынес общую логику в абстрактный класс (сразу сделав объеденение общего + вынос логики в менеджер):

APlayerBase:

public abstract class APlayerBase: IDisposable
{
    private PlayerOptions _options;
    private GameObject _playerGameObject;
    private EPlayerType _type;

    private Camera _camera;
    private NavMeshAgent _agent;
    private InputManager _inputManager;

    public APlayerBase(GameObject playerGameObject, EPlayerType type, PlayerOptions options)
    {
        _options = options;
        _playerGameObject = playerGameObject;
        _type = type;


        _agent = _playerGameObject.GetComponent<NavMeshAgent>();
        _agent.speed = _options.Speed;
        _camera = Helper.GetMainCamera<Camera>();
        _inputManager = Helper.GetGameManager<InputManager>();

        _inputManager.LeftMouseClick += Move;
    }

    public void SetDestination(Vector3 targetPoint)
    {
        _agent.SetDestination(targetPoint);
    }

    public void Warp(Vector3 targetPoint)
    {
        _agent.Warp(targetPoint);
    }

    public void SetActive(bool state)
    {
        this._options.IsActive = state;
    }

    public GameObject GetGameObject() { return _playerGameObject; }

    public void Dispose()
    {
        _inputManager.LeftMouseClick -= Move;
    }

    private void Move()
    {
        if (_options.IsActive)
        {
            Ray ray = _camera.ScreenPointToRay(Input.mousePosition);
            RaycastHit hit;

            if (Physics.Raycast(ray, out hit))
            {
                _agent.SetDestination(hit.point);
            }
        }
    }
}

Далее сделал 2 дочерних класса для Человека и Корабля соответсвенно:

public class CharacterManager : APlayerBase
{
    public CharacterManager(GameObject shipGameObject, PlayerOptions options) : base(shipGameObject, EPlayerType.CHARACTER, options)
    {

    }
}

public class ShipManager : APlayerBase
{
    public ShipManager(GameObject shipGameObject, PlayerOptions options) : base(shipGameObject, EPlayerType.SHIP, options)
    {

    }
}

В данный момент с логикой вроде все ок, она отделена от общего View. Но далее мне нужно прикрепить эту логику к рендеру. Я не нашел ничего лучше, чем создать на каждый GameObject свой "View script" и так появились в превабах Человека и Корабля скрипты:

public class CharacterScript : MonoBehaviour
{
    public APlayerBase playerManager;
    // Start is called before the first frame update
    void Start()
    {
        playerManager = new CharacterManager(gameObject, new PlayerOptions()
        {
            Speed = 5f,
            IsActive = true
        });
    }

    // Update is called once per frame
    void Update()
    {

    }

    private void OnDestroy()
    {
        playerManager.Dispose();
    }
}


public class ShipScript : MonoBehaviour
{
    public APlayerBase playerManager;
    // Start is called before the first frame update
    void Start()
    {
        playerManager = new ShipPlayerManager(gameObject, new PlayerOptions()
        {
            Speed = 5f,
            IsActive = false
        });
    }

    // Update is called once per frame
    void Update()
    {

    }

    private void OnDestroy()
    {
        playerManager.Dispose();
    }
}

И вот я теперь подошел к проблеме. Теперь я хочу переключится с Человека на Корабль....все на простом. Просто сказать камере следовать за другим объектом. И вот у меня появляется GameManager, который следит за (пока еще не большой) логикой самой игры.

public static class GameManager
{
    public static CameraManager cameraManager = new CameraManager();
    public static GameObject _player;
    public static InputManager inputManager;
    private static EWordViewMode _worldViewMode = EWordViewMode.ISLAND;
    private static Dictionary<EWordViewMode, float> cameraDistantions = new Dictionary<EWordViewMode, float>
    {
        { EWordViewMode.CITY, 20f },
        { EWordViewMode.ISLAND, 40f },
        { EWordViewMode.WORLD, 70f }
    };

    public static void Init(InputManager inputManager)
    {
        inputManager = inputManager;
    }

    public static void ChangePlayer(APlayerBase player)
    {
        cameraManager.SetFollowObject(player.GetGameObject());
    }
}

И вот на этом этапе я понял, что не могу получить внутри функции ChangePlayer(...) объект APlayerBase (т.е. менеджара по управлению текущим целевым объектом).

Нужна помощь в:

  1. Укажите на явные ошибки, которые я допустил при органзации.
  2. Если есть статьи или видео, которые помогут прямо или косвенно понять мне в каком направлении двигаться дальше, пожалуйста в коменты накидайте.

Ответы

▲ -1

У тебя человек унаследовался от управления... даже звучит глупо, потому что оно так и есть.

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

В играх не редко управление забирают/дают, переходит от отдного персонажа к другому, юниты в стратегиях меняют фракциюю и соответственно управление, потому что управляемые объекты, НИКАК не привязаны к управлению, от слова СОВСЕМ! Что бы это понят даже сомнительных статей в интернетах читать не нужно... достаточно просто потыкать пару игр в своей жизни.