433 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
		
		
			
		
	
	
			433 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
|  | namespace TPSBR | ||
|  | { | ||
|  | 	using System; | ||
|  | 	using UnityEngine; | ||
|  | 	using UnityEngine.Profiling; | ||
|  | 	using Fusion.Addons.AnimationController; | ||
|  | 	using Fusion.Addons.KCC; | ||
|  | 
 | ||
|  | 	[Serializable] | ||
|  | 	public sealed class CharacterView | ||
|  | 	{ | ||
|  | 		public Transform RootBone; | ||
|  | 		public Transform HeadTransform; | ||
|  | 
 | ||
|  | 		public Transform CameraHandle; | ||
|  | 		public Transform CameraTransformHead; | ||
|  | 		public Transform DefaultCameraTransform; | ||
|  | 		public Transform AimCameraTransform; | ||
|  | 		public Transform JetpackCameraTransform; | ||
|  | 
 | ||
|  | 		public Transform FireTransformRoot; | ||
|  | 		public Transform FireTransform; | ||
|  | 
 | ||
|  | 		public Transform LeftFoot; | ||
|  | 		public Transform RightFoot; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	[Serializable] | ||
|  | 	public struct TransformData | ||
|  | 	{ | ||
|  | 		public Vector3    Position; | ||
|  | 		public Vector3    LocalPosition; | ||
|  | 		public Quaternion Rotation; | ||
|  | 
 | ||
|  | 		public TransformData(Vector3 position, Vector3 localPosition, Quaternion rotation) | ||
|  | 		{ | ||
|  | 			Position      = position; | ||
|  | 			LocalPosition = localPosition; | ||
|  | 			Rotation      = rotation; | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 	public class Character : MonoBehaviour | ||
|  | 	{ | ||
|  | 		// PUBLIC MEMBERS | ||
|  | 
 | ||
|  | 		public Agent                        Agent               => _agent; | ||
|  | 		public bool                         HasInputAuthority   => _agent.HasInputAuthority; | ||
|  | 		public KCC                          CharacterController => _characterController; | ||
|  | 		public CharacterAnimationController AnimationController => _animationController; | ||
|  | 		public CharacterView                ThirdPersonView     => _thirdPersonView; | ||
|  | 
 | ||
|  | 		public float DispersionMultiplier { get; private set; } | ||
|  | 
 | ||
|  | 		// PRIVATE MEMBERS | ||
|  | 
 | ||
|  | 		[SerializeField] | ||
|  | 		private CharacterView _thirdPersonView; | ||
|  | 		[SerializeField] | ||
|  | 		private float _cameraChangeDuration = 0.3f; | ||
|  | 
 | ||
|  | 		[Header("Aim")] | ||
|  | 		[SerializeField] | ||
|  | 		private float _defaultFOV = 60f; | ||
|  | 		[SerializeField] | ||
|  | 		private float _aimFOV = 40f; | ||
|  | 		[SerializeField] | ||
|  | 		private float _fovChangeSpeed = 20f; | ||
|  | 
 | ||
|  | 		[Header("Dispersion")] | ||
|  | 		[SerializeField] | ||
|  | 		private float _aimDispersionMultiplier = 0.5f; | ||
|  | 		[SerializeField] | ||
|  | 		private float _airDispersionMultiplier = 4f; | ||
|  | 		[SerializeField] | ||
|  | 		private float _runDispersionMultiplier = 5f; | ||
|  | 
 | ||
|  | 		private KCC                          _characterController; | ||
|  | 		private CharacterAnimationController _animationController; | ||
|  | 		private Agent                        _agent; | ||
|  | 		private SceneCamera                  _camera; | ||
|  | 
 | ||
|  | 		private Vector3                      _defaultHeadOffset; | ||
|  | 		private Vector3                      _defaultCameraOffset; | ||
|  | 
 | ||
|  | 		private float                        _targetFOV; | ||
|  | 
 | ||
|  | 		private float                        _cameraChangeTime; | ||
|  | 		private float                        _cameraDistance; | ||
|  | 
 | ||
|  | 		private Vector3                      _defaultFireTransformPosition; | ||
|  | 
 | ||
|  | 		private ECameraState                 _previousCameraState; | ||
|  | 		private ECameraState                 _currentCameraState; | ||
|  | 		private bool                         _previousLeftSide; | ||
|  | 		private bool                         _currentLeftSide; | ||
|  | 		private TransformSampler             _fireTransformSampler   = new TransformSampler(); | ||
|  | 		private TransformSampler             _cameraTransformSampler = new TransformSampler(); | ||
|  | 
 | ||
|  | 		// PUBLIC METHODS | ||
|  | 
 | ||
|  | 		public Transform GetCameraHandle() | ||
|  | 		{ | ||
|  | 			return _thirdPersonView.CameraHandle; | ||
|  | 		} | ||
|  | 
 | ||
|  | 		public TransformData GetCameraTransform(bool resolveRenderHistory) | ||
|  | 		{ | ||
|  | 			TransformData transformData = GetCameraTransform(_currentCameraState, _agent.LeftSide); | ||
|  | 
 | ||
|  | 			if (resolveRenderHistory == true && CharacterController.IsProxy == false && _cameraTransformSampler.ResolveRenderPositionAndRotation(_characterController, _agent.AgentInput.FixedInput.LocalAlpha, out Vector3 cameraPosition, out Quaternion cameraRotation) == true) | ||
|  | 			{ | ||
|  | 				transformData.Position = cameraPosition; | ||
|  | 				transformData.Rotation = cameraRotation; | ||
|  | 			} | ||
|  | 
 | ||
|  | 			return transformData; | ||
|  | 		} | ||
|  | 
 | ||
|  | 		public TransformData GetFireTransform(bool resolveRenderHistory) | ||
|  | 		{ | ||
|  | 			Vector3    firePosition; | ||
|  | 			Vector3    localFirePosition; | ||
|  | 			Quaternion fireRotation; | ||
|  | 
 | ||
|  | 			if (resolveRenderHistory == true && CharacterController.IsProxy == false && _fireTransformSampler.ResolveRenderPositionAndRotation(_characterController, _agent.AgentInput.FixedInput.LocalAlpha, out firePosition, out fireRotation) == true) | ||
|  | 			{ | ||
|  | 				localFirePosition = transform.InverseTransformPoint(firePosition); | ||
|  | 			} | ||
|  | 			else | ||
|  | 			{ | ||
|  | 				_thirdPersonView.FireTransform.GetPositionAndRotation(out firePosition, out fireRotation); | ||
|  | 				localFirePosition = transform.InverseTransformPoint(firePosition); | ||
|  | 
 | ||
|  | 				if (_agent.LeftSide == true) | ||
|  | 				{ | ||
|  | 					localFirePosition.x = -localFirePosition.x; | ||
|  | 					firePosition = transform.TransformPoint(localFirePosition); | ||
|  | 				} | ||
|  | 			} | ||
|  | 
 | ||
|  | 			return new TransformData(firePosition, localFirePosition, fireRotation); | ||
|  | 		} | ||
|  | 
 | ||
|  | 		public void OnSpawned(Agent agent) | ||
|  | 		{ | ||
|  | 			_agent  = agent; | ||
|  | 			_camera = agent.Context.Camera; | ||
|  | 
 | ||
|  | 			_characterController.SetManualUpdate(true); | ||
|  | 			_animationController.SetManualUpdate(true); | ||
|  | 
 | ||
|  | 			_previousCameraState = ECameraState.Default; | ||
|  | 			_currentCameraState = ECameraState.Default; | ||
|  | 
 | ||
|  | 			_cameraDistance = GetCameraTransform(ECameraState.Default, false).LocalPosition.magnitude; | ||
|  | 
 | ||
|  | 			_fireTransformSampler.Clear(); | ||
|  | 			_cameraTransformSampler.Clear(); | ||
|  | 		} | ||
|  | 
 | ||
|  | 		public void OnFixedUpdate() | ||
|  | 		{ | ||
|  | 			Profiler.BeginSample(nameof(Character)); | ||
|  | 
 | ||
|  | 			if (_agent.Runner.IsClient == false) | ||
|  | 			{ | ||
|  | 				int playerCount = _agent.Context.NetworkGame.ActivePlayerCount; | ||
|  | 				if (playerCount <  50) | ||
|  | 				{ | ||
|  | 					_animationController.SetInterlacedEvaluation(EEvaluationTarget.FixedUpdate, 1, _agent.Object.InputAuthority.AsIndex); | ||
|  | 				} | ||
|  | 				else if (playerCount < 100) | ||
|  | 				{ | ||
|  | 					_animationController.SetInterlacedEvaluation(EEvaluationTarget.FixedUpdate, 2, _agent.Object.InputAuthority.AsIndex); | ||
|  | 				} | ||
|  | 				else if (playerCount < 150) | ||
|  | 				{ | ||
|  | 					_animationController.SetInterlacedEvaluation(EEvaluationTarget.FixedUpdate, 4, _agent.Object.InputAuthority.AsIndex); | ||
|  | 				} | ||
|  | 				else | ||
|  | 				{ | ||
|  | 					_animationController.SetInterlacedEvaluation(EEvaluationTarget.FixedUpdate, 6, _agent.Object.InputAuthority.AsIndex); | ||
|  | 				} | ||
|  | 			} | ||
|  | 
 | ||
|  | 			if (_agent.Health.IsAlive == false) | ||
|  | 			{ | ||
|  | 				_animationController.SetDead(true); | ||
|  | 			} | ||
|  | 
 | ||
|  | 			_characterController.ManualFixedUpdate(); | ||
|  | 			_animationController.ManualFixedUpdate(); | ||
|  | 
 | ||
|  | 			RefreshCameraHeadPosition(); | ||
|  | 			RefreshFiringPosition(); | ||
|  | 
 | ||
|  | 			TransformData fireTransformData = GetFireTransform(false); | ||
|  | 			_fireTransformSampler.Sample(_characterController, fireTransformData.Position, fireTransformData.Rotation); | ||
|  | 
 | ||
|  | 			TransformData cameraTransformData = GetCameraTransform(false); | ||
|  | 			_cameraTransformSampler.Sample(_characterController, cameraTransformData.Position, cameraTransformData.Rotation); | ||
|  | 
 | ||
|  | 			DispersionMultiplier = GetDispersionMultiplier(); | ||
|  | 
 | ||
|  | 			Profiler.EndSample(); | ||
|  | 		} | ||
|  | 
 | ||
|  | 		public void OnRender() | ||
|  | 		{ | ||
|  | 			_characterController.ManualRenderUpdate(); | ||
|  | 			_animationController.ManualRenderUpdate(); | ||
|  | 
 | ||
|  | 			SetCameraState(_agent.Jetpack.IsActive == true ? ECameraState.Jetpack: (_characterController.Data.Aim == true ? ECameraState.Aim : ECameraState.Default), _agent.LeftSide); | ||
|  | 
 | ||
|  | 			RefreshCameraHeadPosition(); | ||
|  | 			RefreshFiringPosition(); | ||
|  | 
 | ||
|  | 			TransformData fireTransformData = GetFireTransform(false); | ||
|  | 			_fireTransformSampler.Sample(_characterController, fireTransformData.Position, fireTransformData.Rotation); | ||
|  | 
 | ||
|  | 			TransformData cameraTransformData = GetCameraTransform(false); | ||
|  | 			_cameraTransformSampler.Sample(_characterController, cameraTransformData.Position, cameraTransformData.Rotation); | ||
|  | 		} | ||
|  | 
 | ||
|  | 		public void OnAgentRender() | ||
|  | 		{ | ||
|  | 			if (_agent.IsObserved == false) | ||
|  | 				return; | ||
|  | 
 | ||
|  | 			float aimFOV = _aimFOV; | ||
|  | 			if (_agent.Weapons.CurrentWeapon != null && _agent.Weapons.CurrentWeapon.AimFOV > 1.0f) | ||
|  | 			{ | ||
|  | 				aimFOV = _agent.Weapons.CurrentWeapon.AimFOV; | ||
|  | 			} | ||
|  | 
 | ||
|  | 			_targetFOV = _characterController.Data.Aim == true ? aimFOV : _defaultFOV; | ||
|  | 			_camera.Camera.fieldOfView = Mathf.Lerp(_camera.Camera.fieldOfView, _targetFOV, _fovChangeSpeed * Time.deltaTime); | ||
|  | 
 | ||
|  | 			if (_previousCameraState != _currentCameraState || _previousLeftSide != _currentLeftSide) | ||
|  | 			{ | ||
|  | 				_cameraChangeTime += Time.deltaTime; | ||
|  | 
 | ||
|  | 				if (_cameraChangeTime >= _cameraChangeDuration) | ||
|  | 				{ | ||
|  | 					_previousCameraState = _currentCameraState; | ||
|  | 					_previousLeftSide = _currentLeftSide; | ||
|  | 				} | ||
|  | 			} | ||
|  | 
 | ||
|  | 			if (_previousCameraState != _currentCameraState || _previousLeftSide != _currentLeftSide) | ||
|  | 			{ | ||
|  | 				var previousCameraTransform = GetCameraTransform(_previousCameraState, _previousLeftSide); | ||
|  | 				var currentCameraTransform = GetCameraTransform(_currentCameraState, _currentLeftSide); | ||
|  | 
 | ||
|  | 				float progress = _cameraChangeTime / _cameraChangeDuration; | ||
|  | 
 | ||
|  | 				float maxCameraDistance = currentCameraTransform.LocalPosition.magnitude; | ||
|  | 				_cameraDistance = Mathf.Clamp(_cameraDistance + maxCameraDistance * 8.0f * Time.deltaTime, 0.0f, maxCameraDistance); | ||
|  | 
 | ||
|  | 				Vector3 raycastDirection = Vector3.Normalize(currentCameraTransform.LocalPosition); | ||
|  | 				Vector3 raycastStart     = Vector3.Lerp(previousCameraTransform.Position, currentCameraTransform.Position, progress) - raycastDirection * maxCameraDistance; | ||
|  | 				if (_agent.Runner.GetPhysicsScene().Raycast(raycastStart, raycastDirection, out RaycastHit hitInfo, maxCameraDistance + 0.25f, -5, QueryTriggerInteraction.Ignore) == true) | ||
|  | 				{ | ||
|  | 					Agent agent = hitInfo.transform.GetComponentInParent<Agent>(); | ||
|  | 					if (agent == null || agent != _agent) | ||
|  | 					{ | ||
|  | 						hitInfo.distance = Mathf.Clamp(hitInfo.distance - 0.25f, 0.0f, maxCameraDistance); | ||
|  | 
 | ||
|  | 						if (hitInfo.distance < _cameraDistance) | ||
|  | 						{ | ||
|  | 							_cameraDistance = hitInfo.distance; | ||
|  | 						} | ||
|  | 					} | ||
|  | 				} | ||
|  | 
 | ||
|  | 				_camera.transform.position = raycastStart + raycastDirection * _cameraDistance; | ||
|  | 				_camera.transform.rotation = Quaternion.Slerp(previousCameraTransform.Rotation, currentCameraTransform.Rotation, progress); | ||
|  | 			} | ||
|  | 			else | ||
|  | 			{ | ||
|  | 				var cameraTransform = GetCameraTransform(_currentCameraState, _currentLeftSide); | ||
|  | 
 | ||
|  | 				float maxCameraDistance = cameraTransform.LocalPosition.magnitude; | ||
|  | 				_cameraDistance = Mathf.Clamp(_cameraDistance + maxCameraDistance * 8.0f * Time.deltaTime, 0.0f, maxCameraDistance); | ||
|  | 
 | ||
|  | 				Vector3 raycastDirection = Vector3.Normalize(cameraTransform.LocalPosition); | ||
|  | 				Vector3 raycastStart     = cameraTransform.Position - raycastDirection * maxCameraDistance; | ||
|  | 				if (_agent.Runner.GetPhysicsScene().Raycast(raycastStart, raycastDirection, out RaycastHit hitInfo, maxCameraDistance + 0.25f, -5, QueryTriggerInteraction.Ignore) == true) | ||
|  | 				{ | ||
|  | 					Agent agent = hitInfo.transform.GetComponentInParent<Agent>(); | ||
|  | 					if (agent == null || agent != _agent) | ||
|  | 					{ | ||
|  | 						hitInfo.distance = Mathf.Clamp(hitInfo.distance - 0.25f, 0.0f, maxCameraDistance); | ||
|  | 
 | ||
|  | 						if (hitInfo.distance < _cameraDistance) | ||
|  | 						{ | ||
|  | 							_cameraDistance = hitInfo.distance; | ||
|  | 						} | ||
|  | 					} | ||
|  | 				} | ||
|  | 
 | ||
|  | 				_camera.transform.position = raycastStart + raycastDirection * _cameraDistance; | ||
|  | 				_camera.transform.rotation = cameraTransform.Rotation; | ||
|  | 			} | ||
|  | 
 | ||
|  | 			if (_agent.HasInputAuthority == true) | ||
|  | 			{ | ||
|  | 				_animationController.RefreshSnapping(); | ||
|  | 			} | ||
|  | 		} | ||
|  | 
 | ||
|  | 		// MonoBehaviour INTERFACE | ||
|  | 
 | ||
|  | 		private void Awake() | ||
|  | 		{ | ||
|  | 			_characterController = GetComponent<KCC>(); | ||
|  | 			_animationController = GetComponent<CharacterAnimationController>(); | ||
|  | 
 | ||
|  | 			_defaultHeadOffset   = _thirdPersonView.HeadTransform.position - transform.position; | ||
|  | 			_defaultCameraOffset = _thirdPersonView.CameraTransformHead.position - transform.position; | ||
|  | 
 | ||
|  | 			_defaultFireTransformPosition = _thirdPersonView.FireTransformRoot.localPosition; | ||
|  | 		} | ||
|  | 
 | ||
|  | 		// PRIVATE METHODS | ||
|  | 
 | ||
|  | 		private TransformData GetCameraTransform(ECameraState cameraState, bool leftSide) | ||
|  | 		{ | ||
|  | 			Transform cameraTransform = null; | ||
|  | 
 | ||
|  | 			switch (cameraState) | ||
|  | 			{ | ||
|  | 				case ECameraState.Default: | ||
|  | 					cameraTransform = _thirdPersonView.DefaultCameraTransform; | ||
|  | 					break; | ||
|  | 				case ECameraState.Aim: | ||
|  | 					cameraTransform = _thirdPersonView.AimCameraTransform; | ||
|  | 					break; | ||
|  | 				case ECameraState.Jetpack: | ||
|  | 					cameraTransform = _thirdPersonView.JetpackCameraTransform; | ||
|  | 					break; | ||
|  | 			} | ||
|  | 
 | ||
|  | 			if (cameraTransform == null) | ||
|  | 				return default; | ||
|  | 
 | ||
|  | 			var transformData = new TransformData(cameraTransform.position, cameraTransform.position - cameraTransform.parent.position, cameraTransform.rotation); | ||
|  | 
 | ||
|  | 			if (leftSide == false) | ||
|  | 				return transformData; | ||
|  | 
 | ||
|  | 			transformData.Position = transform.TransformPoint(MultiplyVector(transform.InverseTransformPoint(transformData.Position), -1.0f, 1.0f, 1.0f)); | ||
|  | 			transformData.LocalPosition = transformData.Position - transform.TransformPoint(MultiplyVector(transform.InverseTransformPoint(cameraTransform.parent.position), -1.0f, 1.0f, 1.0f)); | ||
|  | 
 | ||
|  | 			return transformData; | ||
|  | 		} | ||
|  | 
 | ||
|  | 		private float GetDispersionMultiplier() | ||
|  | 		{ | ||
|  | 			float multiplier = 1f; | ||
|  | 
 | ||
|  | 			bool isGrounded = _characterController.FixedData.IsGrounded == true; | ||
|  | 
 | ||
|  | 			if (isGrounded == true && _characterController.FixedData.RealSpeed > 0.5f) | ||
|  | 			{ | ||
|  | 				multiplier *= _runDispersionMultiplier; | ||
|  | 			} | ||
|  | 
 | ||
|  | 			if (isGrounded == false) | ||
|  | 			{ | ||
|  | 				multiplier *= _airDispersionMultiplier; | ||
|  | 			} | ||
|  | 
 | ||
|  | 			if (_characterController.FixedData.Aim == true) | ||
|  | 			{ | ||
|  | 				multiplier *= _aimDispersionMultiplier; | ||
|  | 			} | ||
|  | 
 | ||
|  | 			return multiplier; | ||
|  | 		} | ||
|  | 
 | ||
|  | 		private void RefreshCameraHeadPosition() | ||
|  | 		{ | ||
|  | 			_thirdPersonView.RootBone.localScale = new Vector3(_agent.LeftSide == true ? -1.0f : 1.0f, 1.0f, 1.0f); | ||
|  | 
 | ||
|  | 			Vector3 currentHeadOffset    = _thirdPersonView.HeadTransform.position - transform.position; | ||
|  | 			Vector3 headOffsetDifference = currentHeadOffset - _defaultHeadOffset; | ||
|  | 			Vector3 cameraPosition       = transform.position + _defaultHeadOffset + headOffsetDifference * 0.5f; | ||
|  | 
 | ||
|  | 			Vector3 cameraHeadPosition = _thirdPersonView.CameraTransformHead.position; | ||
|  | 			cameraHeadPosition.y = cameraPosition.y; | ||
|  | 			_thirdPersonView.CameraTransformHead.position = cameraHeadPosition; | ||
|  | 		} | ||
|  | 
 | ||
|  | 		private void RefreshFiringPosition() | ||
|  | 		{ | ||
|  | 			_thirdPersonView.FireTransformRoot.localPosition = _defaultFireTransformPosition; | ||
|  | 		} | ||
|  | 
 | ||
|  | 		private void SetCameraState(ECameraState state, bool leftSide) | ||
|  | 		{ | ||
|  | 			if (state == _currentCameraState && leftSide == _currentLeftSide) | ||
|  | 				return; | ||
|  | 
 | ||
|  | 			_previousCameraState = _currentCameraState; | ||
|  | 			_previousLeftSide = _currentLeftSide; | ||
|  | 			_currentCameraState = state; | ||
|  | 			_currentLeftSide = leftSide; | ||
|  | 			_cameraChangeTime = 0f; | ||
|  | 			_cameraDistance = Mathf.Max(_cameraDistance, GetCameraTransform(_currentCameraState, _currentLeftSide).LocalPosition.magnitude); | ||
|  | 		} | ||
|  | 
 | ||
|  | 		private static Vector3 MultiplyVector(Vector3 vector, float x, float y, float z) | ||
|  | 		{ | ||
|  | 			vector.x *= x; | ||
|  | 			vector.y *= y; | ||
|  | 			vector.z *= z; | ||
|  | 			return vector; | ||
|  | 		} | ||
|  | 
 | ||
|  | 		// HELPERS | ||
|  | 
 | ||
|  | 		private enum ECameraState | ||
|  | 		{ | ||
|  | 			None, | ||
|  | 			Default, | ||
|  | 			Aim, | ||
|  | 			Jetpack, | ||
|  | 		} | ||
|  | 	} | ||
|  | } |