using System; using System.Collections; using System.Collections.Generic; using System.Linq; using BITKit; using BITKit.Entities; using BITKit.FPS; using Cinemachine; using Cysharp.Threading.Tasks; using Net.Project.B.Damage; using Net.Project.B.Health; using NodeCanvas.Framework; using Project.B.Player; using Unity.Mathematics; using UnityEngine; using UnityEngine.InputSystem; using Random = UnityEngine.Random; namespace Project.B.CharacterController { public class PlayerCameraController:IDisposable { public event Action OnToggleTpsCamera; private readonly IWrapper _playerSettings; private readonly IPlayerCharacterController _playerCharacterController; private readonly IBlackboard _blackboard; private readonly IHealthService _healthService; private readonly CinemachineVirtualCamera _fpsCamera; private readonly CinemachineVirtualCamera _tpsCamera; private readonly IMainTicker _mainTicker; private readonly CinemachineVirtualCamera _deathCamera; private readonly IEntity _entity; private readonly Cinemachine3rdPersonFollow _follow; private readonly CinemachineBasicMultiChannelPerlin _noise; private readonly IDamageService _damageService; private bool _manualEnabled; private float _currentCameraDistance; private readonly float _initialCameraDistance; private readonly CinemachineBrain _brain; private readonly LayerMask _cameraCollisionFilter; private readonly Spring3 _spring3=new(8,8) { }; public PlayerCameraController(IPlayerKeyMap playerKeyMap, InputActionGroup inputActionGroup, IBlackboard blackboard, IHealthService healthService, IEntity entity, IMainTicker mainTicker, IPlayerCharacterController playerCharacterController, IWrapper playerSettings, IDamageService damageService) { _blackboard = blackboard; _healthService = healthService; _entity = entity; _mainTicker = mainTicker; _playerCharacterController = playerCharacterController; _playerSettings = playerSettings; _damageService = damageService; var inputAction = playerKeyMap.ToggleCameraKey; inputActionGroup.RegisterCallback(inputAction, OnToggleCamera); _fpsCamera = blackboard.GetVariable("camera_fps").value; _tpsCamera = blackboard.GetVariable("camera_tps").value; _deathCamera = blackboard.GetVariable("camera_death").value; healthService.OnHealthChanged += OnHealthChanged; playerCharacterController.AllowTpsCamera.AddListener(x=> { OnToggleTpsCamera?.Invoke(x); _tpsCamera.enabled = x; }); _mainTicker.Add(OnTick); _follow = _tpsCamera.GetCinemachineComponent(); _noise = _fpsCamera.GetCinemachineComponent(); _initialCameraDistance = _currentCameraDistance = _follow.CameraDistance; _brain = Camera.main!.GetComponent(); _brain.m_CameraActivatedEvent.AddListener(OnCameraActivated); _damageService.OnDamaged += OnDamaged; _playerCharacterController.OnInputViewFactor += OnInputViewFactor; _cameraCollisionFilter = _follow.CameraCollisionFilter; } private float _sensitivityFactor=1; private float2 OnInputViewFactor(float2 arg) { return arg * _sensitivityFactor; } private void OnDamaged(IDamageReport obj) { if(obj.Target!=_entity.Id)return; _spring3.Value += Random.insideUnitSphere * obj.FinalDamage; } private void OnCameraActivated(ICinemachineCamera arg0, ICinemachineCamera arg1) { if (ReferenceEquals(arg0, _tpsCamera)) { _playerCharacterController.AllowTpsCamera.RemoveElement(64); return; } _playerCharacterController.AllowTpsCamera.SetElements(64,!ReferenceEquals(arg0, _fpsCamera)); } private void OnTick(float obj) { var zoomFactor = _playerCharacterController.ZoomFactor.Values.Max(); _fpsCamera.m_Lens.FieldOfView = Mathf.Lerp(_fpsCamera.m_Lens.FieldOfView, GetZoomedFOV(_playerSettings.Value.Fov, zoomFactor), 32 * obj); _tpsCamera.m_Lens.FieldOfView = Mathf.Lerp(_tpsCamera.m_Lens.FieldOfView, GetZoomedFOV(_playerSettings.Value.TpsFov, zoomFactor), 32 * obj); if (_playerCharacterController.AllowTpsCamera) { _sensitivityFactor = _fpsCamera.m_Lens.FieldOfView / _playerSettings.Value.Fov; } else { _sensitivityFactor = _tpsCamera.m_Lens.FieldOfView / _playerSettings.Value.TpsFov ; } if (_noise) _noise.m_AmplitudeGain = Mathf.Lerp(0, 1, MathV.GetLength(_playerCharacterController.CharacterController.Velocity)*obj); if (_playerCharacterController.CharacterController.CurrentState is ICharacterSeating seating) { _currentCameraDistance =math.max(seating.SeatNode.CameraDistance,_playerSettings.Value.TpsMinDistance) ; } else { _currentCameraDistance =math.max(_initialCameraDistance,_playerSettings.Value.TpsMinDistance) ; } _follow.CameraDistance = Mathf.MoveTowards(_follow.CameraDistance, _currentCameraDistance, 8 * obj); _follow.CameraCollisionFilter = math.abs(_currentCameraDistance - _playerSettings.Value.TpsMinDistance) < 0.01f ? default : _cameraCollisionFilter; _spring3.Tick(obj,default); _playerCharacterController.AdditiveCameraQuaternion.Set(GetHashCode(),Quaternion.Euler(_spring3.Value)); } // 计算缩放后的FOV private float GetZoomedFOV(int sourceFOV, float zoomFactor) { // 使用反比例公式计算缩放后的FOV return 2 * Mathf.Atan(Mathf.Tan(sourceFOV * Mathf.Deg2Rad / 2) / zoomFactor) * Mathf.Rad2Deg; } private async void OnHealthChanged(int arg1, int arg2, int arg3, object arg4) { if (arg1 != _entity.Id) return; if (_deathCamera) _deathCamera.enabled = arg3 < 0; if (arg3 < 0) { for (var i = 0; i < 16; i++) { if (_blackboard.GetVariable("player_ragdoll")?.value is { } ragdoll) { var target = ragdoll.transform; if (ragdoll.TryGetComponent(out var animator)) { target = animator.GetBoneTransform(HumanBodyBones.UpperChest); } //_deathCamera.Follow = ragdoll.transform; //_deathCamera.LookAt = target; break; } await UniTask.NextFrame(); } } } private void OnToggleCamera(InputAction.CallbackContext obj) { _manualEnabled = !_manualEnabled; _playerCharacterController.AllowTpsCamera.SetElements(this,_manualEnabled); } public void Dispose() { _damageService.OnDamaged -= OnDamaged; _healthService.OnHealthChanged -= OnHealthChanged; _mainTicker.Remove(OnTick); _brain.m_CameraActivatedEvent.RemoveListener(OnCameraActivated); } } }