Unity. Вопрос по организации кода
У меня была обычная реализация кода "как в туториалах". Рассмотрю на примере скрипта игрока.
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);
}
}
}
К дальнейшему рефакторингу кода сподвигло два момента:
- Мне нужно было несколько разных поведений, но было и много одинакового (имеется в виду между объектами-игроками).
- Прочитал статью в которой оговариволось о централизованых мэнеджерах управления чего-либо.
Дальше сделал несколько изменений. Возможных игроков у меня 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 (т.е. менеджара по управлению текущим целевым объектом).
Нужна помощь в:
- Укажите на явные ошибки, которые я допустил при органзации.
- Если есть статьи или видео, которые помогут прямо или косвенно понять мне в каком направлении двигаться дальше, пожалуйста в коменты накидайте.