220 lines
8.1 KiB
C#
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);
|
|
}
|
|
}
|
|
}
|