using Cysharp.Threading.Tasks; using Unity.Cinemachine; using UnityEngine; using UnityEngine.InputSystem; namespace DanieleMarotta.RiversongCodeShowcase { [Service(typeof(ICameraProperties))] public class CameraSystem : GameSystem, IInitializable, IUpdatable, ICameraProperties { [InjectService] private GameConfig _gameConfig; [InjectService] private IScene _scene; private GameConfig.CameraConfig _cameraConfig; private CinemachineBrain _cinemachineBrain; private CinemachineFollow _cinemachineFollow; private Transform _target; private float _targetZoom; private Vector3? _terrainDragAnchor; public CameraSystem(IServiceLocator serviceLocator) : base(serviceLocator) { } public float Zoom { get; private set; } public UniTask InitializeAsync() { _cameraConfig = _gameConfig.Camera; _cinemachineBrain = _scene.MainCamera.GetComponent(); _cinemachineFollow = (CinemachineFollow)_scene.CinemachineCamera.GetCinemachineComponent(CinemachineCore.Stage.Body); _target = _cinemachineFollow.FollowTarget; Zoom = _targetZoom = _cinemachineFollow.FollowOffset.magnitude; return UniTask.CompletedTask; } public void Update() { var dt = Time.unscaledDeltaTime; UpdateZoom(dt); UpdatePosition(dt); UpdateRotation(dt); _cinemachineBrain.ManualUpdate(); } private void UpdateZoom(float dt) { var zoomInput = -1 * Mouse.current.scroll.y.ReadValue(); _targetZoom += zoomInput * _cameraConfig.ZoomSensitivity; _targetZoom = Mathf.Clamp(_targetZoom, _cameraConfig.ZoomRange.x, _cameraConfig.ZoomRange.y); Zoom = Mathf.Lerp(Zoom, _targetZoom, _cameraConfig.ZoomSpeed * dt); _cinemachineFollow.FollowOffset = _cinemachineFollow.FollowOffset.normalized * Zoom; } private void UpdatePosition(float dt) { var moveInput = Vector3.zero; moveInput.x += Keyboard.current.dKey.isPressed ? 1 : 0; moveInput.x += Keyboard.current.aKey.isPressed ? -1 : 0; moveInput.z += Keyboard.current.wKey.isPressed ? 1 : 0; moveInput.z += Keyboard.current.sKey.isPressed ? -1 : 0; var right = _target.right; var forward = _target.forward; forward.y = 0; forward.Normalize(); var normalizedZoom = Mathf.InverseLerp(_cameraConfig.ZoomRange.x, _cameraConfig.ZoomRange.y, Zoom); var moveSpeed = Mathf.Lerp(_cameraConfig.MoveSpeed.x, _cameraConfig.MoveSpeed.y, normalizedZoom); var delta = moveSpeed * dt; _target.position += right * (moveInput.x * delta); _target.position += forward * (moveInput.z * delta); UpdateDraggingTerrain(); } private void UpdateDraggingTerrain() { if (!Mouse.current.middleButton.isPressed) { _terrainDragAnchor = null; return; } var plane = new Plane(Vector3.up, new Vector3(0, _gameConfig.GeneralSettings.BaseElevation, 0)); var ray = _scene.MainCamera.ScreenPointToRay(Mouse.current.position.ReadValue()); if (!plane.Raycast(ray, out var enter)) return; var hit = ray.GetPoint(enter); if (_terrainDragAnchor == null) { _terrainDragAnchor = hit; return; } _target.position += _terrainDragAnchor.Value - hit; } private void UpdateRotation(float dt) { var keyboard = Keyboard.current; var rotation = _target.rotation.eulerAngles; var keyboardRotationDelta = Vector2.zero; keyboardRotationDelta.x += keyboard.eKey.isPressed ? 1 : 0; keyboardRotationDelta.x += keyboard.qKey.isPressed ? -1 : 0; keyboardRotationDelta.y += keyboard.rKey.isPressed ? 1 : 0; keyboardRotationDelta.y += keyboard.fKey.isPressed ? -1 : 0; rotation.y += keyboardRotationDelta.x * _cameraConfig.KeyboardRotationSpeed.x * dt; rotation.x -= keyboardRotationDelta.y * _cameraConfig.KeyboardRotationSpeed.y * dt; if (keyboard.leftAltKey.isPressed) { var mouseDelta = Mouse.current.delta; rotation.x += mouseDelta.y.ReadValue() * _cameraConfig.MouseRotationSpeed.y * dt; rotation.y += mouseDelta.x.ReadValue() * _cameraConfig.MouseRotationSpeed.x * dt; } rotation.x = Mathf.Clamp(rotation.x, _cameraConfig.PitchRange.x, _cameraConfig.PitchRange.y); _target.forward = Quaternion.Euler(rotation) * Vector3.forward; } } }