Net.Like.Xue.Tokyo/Packages-Local/Com.Project.B.Unity/CharacterController/PlayerCameraController.cs

220 lines
8.1 KiB
C#

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<bool> OnToggleTpsCamera;
private readonly IWrapper<PlayerSettings> _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<InputAction> playerKeyMap, InputActionGroup inputActionGroup, IBlackboard blackboard, IHealthService healthService, IEntity entity, IMainTicker mainTicker, IPlayerCharacterController playerCharacterController, IWrapper<PlayerSettings> 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<CinemachineVirtualCamera>("camera_fps").value;
_tpsCamera = blackboard.GetVariable<CinemachineVirtualCamera>("camera_tps").value;
_deathCamera = blackboard.GetVariable<CinemachineVirtualCamera>("camera_death").value;
healthService.OnHealthChanged += OnHealthChanged;
playerCharacterController.AllowTpsCamera.AddListener(x=>
{
OnToggleTpsCamera?.Invoke(x);
_tpsCamera.enabled = x;
});
_mainTicker.Add(OnTick);
_follow = _tpsCamera.GetCinemachineComponent<Cinemachine3rdPersonFollow>();
_noise = _fpsCamera.GetCinemachineComponent<CinemachineBasicMultiChannelPerlin>();
_initialCameraDistance = _currentCameraDistance = _follow.CameraDistance;
_brain = Camera.main!.GetComponent<CinemachineBrain>();
_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<GameObject>("player_ragdoll")?.value is { } ragdoll)
{
var target = ragdoll.transform;
if (ragdoll.TryGetComponent<Animator>(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);
}
}
}