Files
Cielonos/Assets/Scripts/MainGame/Characters/Automata/Subcontrollers/AutomataLandMovementSubcontroller.cs
SoulliesOfficial 649b7a5ddc 更新
2026-05-23 08:27:50 -04:00

262 lines
11 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using Cielonos.MainGame.Buffs.Character;
using Opsive.BehaviorDesigner.Runtime;
using SLSUtilities.General;
using UnityEngine;
using UnityEngine.AI;
namespace Cielonos.MainGame.Characters
{
public partial class AutomataLandMovementSubcontroller : LandMovementSubcontroller
{
public NavMeshAgent navMeshAgent => (owner as Automata)!.behaviorSc.navMeshAgent;
public LerpFloat animatorMoveSpeedX;
public LerpFloat animatorMoveSpeedZ;
public override void Initialize()
{
base.Initialize();
navMeshAgent.isStopped = true;
animatorMoveSpeedX = new LerpFloat(0f, 5f);
animatorMoveSpeedZ = new LerpFloat(0f, 5f);
}
private Vector3 lastDirection;
protected override void Update()
{
base.Update();
HandleNavMeshState();
}
protected override void OnAnimatorMove()
{
if (owner.selfTimeSm.DeltaTime == 0 || owner.statusSm.isDead)
{
return;
}
if (navMeshAgent != null && navMeshAgent.enabled)
{
UpdateNavigationAnimParams();
}
base.OnAnimatorMove();
InitiativeMove();
lastDirection = transform.forward;
}
protected override void InitiativeMove()
{
if (navMeshAgent.enabled)
{
navMeshAgent.Move(finalMovementVelocity);
navMeshAgent.baseOffset += finalMovementVelocity.y;
navMeshAgent.baseOffset = Mathf.Max(navMeshAgent.baseOffset, 0f);
}
else
{
if (owner.collisionSc.useCharacterController)
{
owner.collisionSc.characterController.Move(finalMovementVelocity);
}
else
{
Vector3 startPosition = owner.collisionSc.mainRigidbody.position;
owner.collisionSc.mainRigidbody.MovePosition(startPosition + finalMovementVelocity);
}
}
}
// ────────────────────────────────────────────────────────────────────
// Navigation Animation
// ────────────────────────────────────────────────────────────────────
private void UpdateNavigationAnimParams()
{
if (!is8WayMovement)
{
animatorMoveSpeedZ.targetValue = navMeshAgent.isStopped
? 0f
: navMeshAgent.velocity.magnitude
+ Vector3.Angle(lastDirection, transform.forward) / owner.selfTimeSm.DeltaTime * 0.02f;
animatorMoveSpeedZ.Update(0.2f, 1);
animator.SetFloat("MoveSpeedZ", animatorMoveSpeedZ.currentValue);
}
else
{
if (!navMeshAgent.isStopped)
{
Vector3 velocity = navMeshAgent.velocity;
animatorMoveSpeedX.targetValue = Vector3.Dot(velocity, transform.right);
animatorMoveSpeedZ.targetValue = Vector3.Dot(velocity, transform.forward);
}
else
{
animatorMoveSpeedX.targetValue = 0f;
animatorMoveSpeedZ.targetValue = 0f;
}
animatorMoveSpeedX.Update(0.2f, 1);
animatorMoveSpeedZ.Update(0.2f, 1);
animator.SetFloat("MoveSpeedX", animatorMoveSpeedX.currentValue);
animator.SetFloat("MoveSpeedZ", animatorMoveSpeedZ.currentValue);
}
}
}
// ════════════════════════════════════════════════════════════════════════
// Hit Impact & Stun
// ════════════════════════════════════════════════════════════════════════
public partial class AutomataLandMovementSubcontroller
{
private const float DEFAULT_STUN_DURATION = 0.25f;
public bool stagnantFirstHalf;
public bool stagnantLastHalf;
/// <summary>当前是否处于击飞引发的眩晕状态(由落地检测解除)。</summary>
private bool isLaunchStunned;
private Automata Automata => owner as Automata;
/// <summary>
/// 处理受击位移(击退 / 击飞)。
/// </summary>
/// <param name="force">力的矢量XZ = 击退方向和强度Y = 击飞强度)。</param>
/// <param name="isLaunch">是否为"击飞"(无视抗性,自动眩晕)。</param>
/// <param name="isParabolic">Y 轴是否使用抛物线物理。仅 isLaunch 时有意义。</param>
/// <param name="duration">脉冲持续时间0 使用默认值。</param>
/// <param name="curve">速度衰减曲线null 使用默认 EaseOut。</param>
/// <param name="applyStun">(仅击退)是否附加眩晕状态。击飞始终附加眩晕。</param>
/// <param name="stunDuration">仅击退眩晕持续时间0 跟随脉冲持续时间。</param>
/// <param name="gravityMultiplier">重力缩放倍率。0 = 浮空1 = 正常(默认),大于 1 = 加速坠落。</param>
/// <param name="gravityDuration">重力修正持续时间。0 = 不施加重力修正。</param>
public void ApplyHitImpact(Vector3 force, bool isLaunch, bool isParabolic,
float duration = 0f, AnimationCurve curve = null,
bool applyStun = false, float stunDuration = 0f,
float gravityMultiplier = 1f, float gravityDuration = 0f)
{
if (isLaunch && navMeshAgent.enabled) navMeshAgent.enabled = false;
if (!stagnantFirstHalf && !stagnantLastHalf)
{
stagnantFirstHalf = true;
}
impulseSm.ApplyImpulse(force, isLaunch, duration, curve, isParabolic);
if (isLaunch)
{
ApplyStun();
isLaunchStunned = true;
}
else if (applyStun)
{
float effectiveDuration = stunDuration > 0f
? stunDuration
: (duration > 0f ? duration : DEFAULT_STUN_DURATION);
ApplyStun(effectiveDuration);
}
ApplyGravityModifier(gravityMultiplier, gravityDuration);
}
// ────────────────────────────────────────────────────────────────────
// NavMesh State Machine
// ────────────────────────────────────────────────────────────────────
private void HandleNavMeshState()
{
if (navMeshAgent == null) return;
if (stagnantFirstHalf && !isOnGround && finalMovementVelocity.y < 0)
{
stagnantFirstHalf = false;
stagnantLastHalf = true;
}
if (stagnantLastHalf && isOnGround)
{
impulseSm.ClearLaunch();
TryRemoveLaunchStun();
if (!navMeshAgent.enabled)
{
navMeshAgent.enabled = true;
navMeshAgent.Warp(transform.position);
}
stagnantFirstHalf = false;
stagnantLastHalf = false;
}
}
// ────────────────────────────────────────────────────────────────────
// Gravity Modifier
// ────────────────────────────────────────────────────────────────────
/// <summary>
/// 通过 VerticalMoveModification Buff 施加重力修正。
/// multiplier 大于 1 时先清除已有浮空 Buff确保 Finisher 坠落生效。
/// </summary>
private void ApplyGravityModifier(float multiplier, float duration)
{
if (duration <= 0f) return;
if (multiplier > 1f)
{
if (owner.buffSm.TryGetBuff<VerticalMoveModification>(out var existingBuff))
{
existingBuff.Remove();
}
}
new VerticalMoveModification(duration, multiplier).Apply(owner);
}
// ────────────────────────────────────────────────────────────────────
// Stun Helpers
// ────────────────────────────────────────────────────────────────────
/// <summary>
/// 施加眩晕。无参数版为永久眩晕(击飞用),由落地检测解除;
/// 带 duration 版为定时眩晕(击退用),到期自动解除。
/// </summary>
private void ApplyStun(float duration = -1f)
{
Automata.statusSm.AddStatus(StatusType.Stun);
Automata.behaviorSc.mainBehaviorTree.StopBehavior(true);
if (duration > 0f)
{
owner.selfTimeSm.AddLocalTimer(duration, () => TryResumeFromStun());
}
}
/// <summary>
/// 尝试解除击飞眩晕(落地时调用)。
/// </summary>
private void TryRemoveLaunchStun()
{
if (!isLaunchStunned) return;
isLaunchStunned = false;
TryResumeFromStun();
}
/// <summary>
/// 移除一层 Stun若所有 Stun 层归零则恢复行为树。
/// </summary>
private void TryResumeFromStun()
{
Automata.statusSm.RemoveStatus(StatusType.Stun);
if (!Automata.statusSm.HasStatus(StatusType.Stun))
{
Automata.behaviorSc.mainBehaviorTree.StartBehavior();
}
}
}
}