314 lines
12 KiB
C#
314 lines
12 KiB
C#
using System;
|
||
using Cielonos.MainGame.Effects.Feedback;
|
||
using DG.Tweening;
|
||
using SLSUtilities.General;
|
||
using SLSUtilities.FunctionalAnimation;
|
||
using UniRx;
|
||
using Unity.Cinemachine;
|
||
using UnityEngine;
|
||
|
||
namespace Cielonos.MainGame.Characters
|
||
{
|
||
[RequireComponent(typeof(Player))]
|
||
public partial class PlayerLandMovementSubcontroller : LandMovementSubcontroller
|
||
{
|
||
public Player player => owner as Player;
|
||
private PlayerAnimationSubcontroller animationSc => base.animationSc as PlayerAnimationSubcontroller;
|
||
|
||
private static readonly int RushStopTriggerHash = Animator.StringToHash("RushStop");
|
||
private const float RushStopTriggerSpeed = 5f;
|
||
private bool wasMovingLastFrame;
|
||
|
||
protected override void OnAnimatorMove()
|
||
{
|
||
if (DeltaTime == 0)
|
||
{
|
||
return;
|
||
}
|
||
|
||
Rotate();
|
||
Move();
|
||
Jump();
|
||
|
||
base.OnAnimatorMove();
|
||
|
||
InitiativeMove();
|
||
}
|
||
|
||
protected override void Update()
|
||
{
|
||
base.Update();
|
||
|
||
bool cannotDodge = owner.movementSc.isDashing || owner.movementSc.isDodging || owner.movementSc.dodgeIntervalTimer > 0f;
|
||
(owner as Player)!.bodyPartsSc.AuxiliaryDrone.SetDroneEmissionColor(cannotDodge ? Color.red : Color.cyan);
|
||
}
|
||
|
||
public void TurnToInputDirection(Vector3 direction, float duration = 0f)
|
||
{
|
||
Vector3 dashRotation = Vector3.zero;
|
||
float angle = Vector3.SignedAngle(Vector3.forward, direction, Vector3.up);
|
||
|
||
if (owner is Player player)
|
||
{
|
||
dashRotation.y = player.viewSc.playerCamera.transform.eulerAngles.y + angle;
|
||
}
|
||
else
|
||
{
|
||
dashRotation = new Vector3(0, angle, 0);
|
||
}
|
||
|
||
if (duration > 0)
|
||
{
|
||
characterTransform.DORotateQuaternion(Quaternion.Euler(dashRotation), duration).Play();
|
||
}
|
||
else
|
||
{
|
||
characterTransform.rotation = Quaternion.Euler(dashRotation);
|
||
}
|
||
}
|
||
}
|
||
|
||
public partial class PlayerLandMovementSubcontroller
|
||
{
|
||
void Rotate()
|
||
{
|
||
if (animationSc.isDuringRootMotion) return;
|
||
|
||
Vector3 inputDirection = new Vector3(player.inputSc.Move.x, 0.0f, player.inputSc.Move.y).normalized;
|
||
|
||
if (player.inputSc.IsMoving)
|
||
{
|
||
targetRotation = Mathf.Atan2(inputDirection.x, inputDirection.z) * Mathf.Rad2Deg +
|
||
player.viewSc.playerCamera.transform.eulerAngles.y;
|
||
if (rotationType == CharacterRotationType.ByMovement && canRotate.Value)
|
||
{
|
||
float rotation = Mathf.SmoothDampAngle(transform.eulerAngles.y, targetRotation, ref rotationVelocity, rotationSmoothTime);
|
||
player.transform.rotation = Quaternion.Euler(0.0f, rotation, 0.0f);
|
||
}
|
||
}
|
||
|
||
targetDirection = Quaternion.Euler(0.0f, targetRotation, 0.0f) * Vector3.forward;
|
||
}
|
||
|
||
void Move()
|
||
{
|
||
if (!canMove.Value)
|
||
{
|
||
wasMovingLastFrame = player.inputSc.IsMoving;
|
||
moveSpeed = 0;
|
||
return;
|
||
}
|
||
|
||
float targetSpeed = player.attributeSm.Get(CharacterAttribute.MovementSpeed, 10f) * (isSprinting ? sprintSpeedMultiplier : 1f);
|
||
float movementAnimationSpeedMultiplier = !player.inputSc.IsWalking && targetSpeed > 10f ? targetSpeed / 10f : 1;
|
||
float inputMagnitude = player.inputSc.Move.magnitude;
|
||
moveAcceleration = player.attributeSm.Get(CharacterAttribute.MovementAcceleration, 6);
|
||
moveDeceleration = player.attributeSm.Get(CharacterAttribute.MovementDeceleration, 8);
|
||
|
||
bool isMoving = player.inputSc.IsMoving;
|
||
//TryPlayRushStop(isMoving);
|
||
|
||
if (isMoving)
|
||
{
|
||
bool success = animationSc.fullBodyFuncAnimSm.Stop(DisruptionType.Movement, 0.25f);
|
||
if (!success && animationSc.isDuringRootMotion)
|
||
{
|
||
if (!isDashing)
|
||
{
|
||
moveSpeed = Mathf.Lerp(moveSpeed, 0, DeltaTime * moveDeceleration);
|
||
}
|
||
|
||
wasMovingLastFrame = isMoving;
|
||
return;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
isSprinting = false;
|
||
}
|
||
|
||
animator.SetFloat("MoveSpeedMultiplier", movementAnimationSpeedMultiplier);
|
||
|
||
if (!isJumpLanding && isMoving)
|
||
{
|
||
float finalTargetSpeed = !player.inputSc.IsWalking ? targetSpeed * inputMagnitude : 2f;
|
||
finalTargetSpeed = Mathf.Max(finalTargetSpeed, 1);
|
||
moveSpeed = Mathf.Lerp(moveSpeed, finalTargetSpeed, DeltaTime * moveAcceleration);
|
||
}
|
||
else if (!isDashing)
|
||
{
|
||
moveSpeed = Mathf.Lerp(moveSpeed, 0, DeltaTime * moveDeceleration);
|
||
}
|
||
|
||
/*if (MotionController.characterRotationType == PlayerMotionController.CharacterRotationType.ByAiming)
|
||
{
|
||
float mx = Mathf.Lerp(mx, Player.inputController.InputMoveValue.x, DeltaTime * movementSpeedAccelerationRate);
|
||
float my = Mathf.Lerp(my, Player.inputController.InputMoveValue.y, DeltaTime * movementSpeedAccelerationRate);
|
||
|
||
animator.SetFloat("MovementSpeedX", mx * movementSpeed);
|
||
animator.SetFloat("MovementSpeedZ", my * movementSpeed);
|
||
}
|
||
else*/
|
||
{
|
||
animator.SetFloat("MoveSpeedZ", moveSpeed);
|
||
}
|
||
|
||
|
||
|
||
if (moveSpeed > 5f * DeltaTime && isMoving)
|
||
{
|
||
runningTime += DeltaTime;
|
||
}
|
||
else
|
||
{
|
||
runningTime = 0;
|
||
}
|
||
|
||
wasMovingLastFrame = isMoving;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 仅在奔跑状态下检测“释放方向输入”的瞬间,并触发一次性急停动作。
|
||
/// 没有配置急停动画时,不设置 Trigger,继续使用普通减速流程。
|
||
/// </summary>
|
||
private void TryPlayRushStop(bool isMoving)
|
||
{
|
||
if (!wasMovingLastFrame ||
|
||
isMoving ||
|
||
!isSprinting ||
|
||
moveSpeed < RushStopTriggerSpeed ||
|
||
isDashing ||
|
||
isDodging ||
|
||
isStartJumping ||
|
||
isJumping ||
|
||
isJumpLanding ||
|
||
animationSc.isDuringRootMotion ||
|
||
animationSc.currentAnimationGroup?.rushStop == null)
|
||
{
|
||
return;
|
||
}
|
||
|
||
animator.SetTrigger(RushStopTriggerHash);
|
||
}
|
||
|
||
private void Jump()
|
||
{
|
||
if (player.inputSc.JumpPressed && animationSc.fullBodyFuncAnimSm.Stop(DisruptionType.NormalAction))
|
||
{
|
||
// 从属性系统实时读取最大跳跃次数,确保装备/buff变化后立即生效
|
||
maxJumpCount = Mathf.Max(1, Mathf.RoundToInt(player.attributeSm.Get(CharacterAttribute.MaxJumpCount, 1f)));
|
||
|
||
if (!isJumping)
|
||
{
|
||
FirstJump();
|
||
}
|
||
else if (currentJumpCount < maxJumpCount)
|
||
{
|
||
ExtraJump(-1, true);
|
||
}
|
||
}
|
||
|
||
if (isJumping)
|
||
{
|
||
jumpTime += DeltaTime;
|
||
|
||
if (jumpVelocity > 0)
|
||
{
|
||
jumpVelocity -= jumpGravity * DeltaTime;
|
||
}
|
||
else
|
||
{
|
||
jumpVelocity = 0f;
|
||
gravitationalMovement += new Vector3(0, -jumpGravity * gravityMultiplier * DeltaTime, 0) * DeltaTime;
|
||
}
|
||
|
||
if (jumpTime > 0.1f)
|
||
{
|
||
if (jumpTime > 1f && groundDetector.DetectGround(4f, 1f))
|
||
{
|
||
jumpVelocity = 0f;
|
||
animationSc.grounderFBBIK.weight = 1;
|
||
isJumping = false;
|
||
currentJumpCount = 0;
|
||
movementModifier = Vector3.zero;
|
||
animator.SetBool("IsLandingOnGround", true);
|
||
animator.SetTrigger("LandFromHighAltitude");
|
||
isJumpLanding = true;
|
||
Observable.Timer(TimeSpan.FromSeconds(0.4f)).Subscribe(_ => { isJumpLanding = false; });
|
||
return;
|
||
}
|
||
|
||
if (groundDetector.isOnGround || groundDetector.DetectGround())
|
||
{
|
||
jumpVelocity = 0f;
|
||
animationSc.grounderFBBIK.weight = 1;
|
||
animator.SetBool("IsLandingOnGround", true);
|
||
isJumping = false;
|
||
currentJumpCount = 0;
|
||
movementModifier = Vector3.zero;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
public void FirstJump()
|
||
{
|
||
if (isStartJumping || isJumping || !groundDetector.isOnGround)
|
||
{
|
||
return;
|
||
}
|
||
|
||
animator.SetTrigger("Jump");
|
||
(animationSc as PlayerAnimationSubcontroller).grounderFBBIK.weight = 0;
|
||
isStartJumping = true;
|
||
player.eventSm.onFirstJump.Invoke();
|
||
player.selfTimeSm.AddLocalTimer(0.16f, () =>
|
||
{
|
||
jumpVelocity = jumpForce;
|
||
isStartJumping = false;
|
||
isJumping = true;
|
||
currentJumpCount = 1;
|
||
jumpTime = 0;
|
||
jumpHeldTime = 0;
|
||
gravitationalMovement = Vector3.zero;
|
||
animator.SetBool("IsLandingOnGround", false);
|
||
});
|
||
}
|
||
|
||
public void ExtraJump(float overrideForce = -1, bool hasAnimation = false, bool isAdditive = true)
|
||
{
|
||
if (!isAdditive)
|
||
{
|
||
jumpVelocity = overrideForce > 0 ? overrideForce : jumpForce;
|
||
}
|
||
else
|
||
{
|
||
jumpVelocity += overrideForce > 0 ? overrideForce : jumpForce;
|
||
}
|
||
|
||
currentJumpCount++;
|
||
isJumping = true;
|
||
gravitationalMovement = Vector3.zero;
|
||
}
|
||
|
||
protected override void UpdateFinalMovement()
|
||
{
|
||
base.UpdateFinalMovement();
|
||
|
||
if (!isDashing && !isDodging)
|
||
{
|
||
PostProcessingManager.Instance.speedLinesSm.remap.IsPausing = false;
|
||
float horizontalSpeed = horizontalMovement.magnitude / DeltaTime;
|
||
float remapFactor = Mathf.InverseLerp(10f, 15f, horizontalSpeed);
|
||
PostProcessingManager.Instance.speedLinesSm.SetRemap(1 - remapFactor);
|
||
|
||
//player.viewSc.cameraFovSm.UpdateFovBySpeed(horizontalSpeed, DeltaTime);
|
||
}
|
||
else
|
||
{
|
||
PostProcessingManager.Instance.speedLinesSm.remap.IsPausing = true;
|
||
}
|
||
}
|
||
}
|
||
}
|