using System; using System.Collections; using Cinemachine; using UnityEngine; public partial class CameraController : MonoBehaviour { public event Action OnCameraChanged; public event Action OnCameraBlendEnded; public Camera Main { get; private set; } public CameraItem ActiveCamera { get; private set; } [SerializeField] private CameraItem[] _cameras; [SerializeField] private Camera _uiCamera; private CinemachineBrain _brain; private Camera[] _mainChildCameras; private CinemachineTargetGroup _targetGroup; public CameraTargetFollower TargetFollower { get; private set; } private void Start() { CheckCameras(); Main = Camera.main; if (Main != null) { _mainChildCameras = Main.gameObject.GetComponentsInChildren(); _brain = Main.GetComponent(); } else throw new NullReferenceException("MainCamera is null"); InitSwitchCameras(); UI.Canvas.worldCamera = _uiCamera; _brain.m_CameraActivatedEvent.AddListener( (cam1, cam2) => { OnCameraChanged?.Invoke(); StartCoroutine(InvokeCameraBlendEnded(_brain.ActiveBlend?.Duration ?? 0f)); }); TargetFollower = CameraTargetFollower.CreateTarget(); StartCoroutine(SetFollowTargetCycle(TargetFollower.transform)); StartCoroutine(SetLookAtTargetCycle(TargetFollower.transform)); if(_cameras.Length > 0) SetActive(_cameras[0]); } private void Update() { if(_mainChildCameras.Length > 0) foreach (Camera addCamera in _mainChildCameras) { addCamera.orthographic = Main.orthographic; addCamera.orthographicSize = Main.orthographicSize; addCamera.fieldOfView = Main.fieldOfView; } SwitchCamerasTick(); } private IEnumerator InvokeCameraBlendEnded(float delay) { yield return new WaitForSecondsRealtime(delay); OnCameraBlendEnded?.Invoke(); } private void CheckCameras() { foreach (CameraItem cameraItem in _cameras) { if (cameraItem.camera == null) throw new NullReferenceException(); } } private void DisableAllCameras() { foreach (CameraItem item in _cameras) item.camera.gameObject.SetActive(false); } public void SetActive(CameraTypeEnum type) { DisableAllCameras(); CameraItem cameraItem = Array.Find(_cameras, item => item.type == type); if (cameraItem == null) throw new Exception($"Cant find camera with type {type}"); cameraItem.camera.gameObject.SetActive(true); ActiveCamera = cameraItem; } public void SetActive(CameraItem cameraItem) { DisableAllCameras(); if (cameraItem == null) throw new NullReferenceException(); cameraItem.camera.gameObject.SetActive(true); ActiveCamera = cameraItem; } public void SetTarget(Transform target, bool instant = true) { TargetFollower.SetTarget(target, instant); } public void SetTarget(Transform[] targetsTransforms, CameraGroupTargetParameters[] parameters = null, bool instant = true) { _targetGroup = GetTargetGroup(targetsTransforms, parameters); TargetFollower.SetTarget(_targetGroup.transform, instant); } private CinemachineTargetGroup GetTargetGroup(Transform[] targetsTransforms, CameraGroupTargetParameters[] parameters = null) { if (_targetGroup == null) { var go = new GameObject("TargetGroup"); _targetGroup = go.AddComponent(); go.transform.SetParent(transform); } _targetGroup.m_Targets = new CinemachineTargetGroup.Target[targetsTransforms.Length]; if (parameters == null) { parameters = new CameraGroupTargetParameters[targetsTransforms.Length]; parameters.ForEach(() => new CameraGroupTargetParameters()); } else if (parameters.Length < targetsTransforms.Length) throw new Exception("Parameters array length less then elements count"); for (var i = 0; i < _targetGroup.m_Targets.Length; i++) { _targetGroup.m_Targets[i].target = targetsTransforms[i]; _targetGroup.m_Targets[i].weight = parameters[i].Weight; _targetGroup.m_Targets[i].radius = parameters[i].Radius; } return _targetGroup; } private IEnumerator SetFollowTargetCycle(Transform target, bool reenable = true) { void SetFollow(CinemachineVirtualCamera cam) { cam.m_Follow = target; if(reenable) cam.enabled = true; } if(reenable) { foreach (CameraItem cam in _cameras) cam.camera.enabled = false; } yield return null; foreach (CameraItem cam in _cameras) SetFollow(cam.camera); } private IEnumerator SetLookAtTargetCycle(Transform target, bool reenable = true) { void SetLookAt(CinemachineVirtualCamera cam) { cam.m_LookAt = target; if(reenable) cam.enabled = true; } if(reenable) { foreach (CameraItem cam in _cameras) cam.camera.enabled = false; } yield return null; foreach (CameraItem cam in _cameras) SetLookAt(cam.camera); } }