Files
riversong-code-showcase/Source/Riversong/Game/Camera/CameraSystem.cs
2026-05-21 16:04:49 +02:00

145 lines
4.9 KiB
C#

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<CinemachineBrain>();
_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;
}
}
}