Files
Cielonos/Assets/Scripts/MainGame/AttackArea/Submodules/ImpulseSubmodule.cs
SoulliesOfficial 649b7a5ddc 更新
2026-05-23 08:27:50 -04:00

224 lines
9.4 KiB
C#
Raw 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 Cielonos.MainGame.Characters;
using SLSUtilities.General;
using UnityEngine;
namespace Cielonos.MainGame
{
/// <summary>
/// 脉冲力的方向计算模式。
/// </summary>
public enum ImpulseForceMode
{
/// <summary>使用预设的固定力矢量。</summary>
Custom,
/// <summary>沿攻击区域 forward 方向施加力(投射物常用)。</summary>
Dynamic,
/// <summary> 沿攻击区域 forward 方向施加力,但在计算时将竖直分量忽略(仅在水平面内施加力,适合地面攻击)。 </summary>
FlattenedDynamic,
/// <summary>从攻击区域中心向外推开目标。</summary>
Repulsion,
/// <summary>将目标拉向攻击区域中心。</summary>
Suction,
}
/// <summary>
/// 攻击区域的脉冲子模块:配置并向命中目标施加击退/击飞力。
/// 采用单构造函数 + 链式 With* 方法进行配置。
/// </summary>
public class ImpulseSubmodule : AttackAreaSubmoduleBase
{
private ImpulseForceMode forceMode;
private Vector3 customForce;
private float dynamicStrength;
private float strengthXZ;
private float strengthY;
private bool isLaunch;
private bool isParabolic;
private float impulseDuration;
private AnimationCurve impulseCurve;
private bool applyStun;
private float stunDuration;
private float gravityMultiplier = 1f;
private float gravityDuration;
// ────────────────────────────────────────────────────────────────────
// Constructor
// ────────────────────────────────────────────────────────────────────
/// <summary>
/// 创建一个新的 ImpulseSubmodule 实例。
/// </summary>
/// <param name="attackArea">所属的攻击区域。</param>
/// <param name="duration">脉冲持续时间(秒)。</param>
/// <param name="curve">速度衰减曲线。null 使用默认 EaseOut。</param>
public ImpulseSubmodule(AttackAreaBase attackArea, float duration, AnimationCurve curve) : base(attackArea)
{
this.impulseDuration = duration;
this.impulseCurve = curve;
}
// ────────────────────────────────────────────────────────────────────
// Chain Configuration
// ────────────────────────────────────────────────────────────────────
/// <summary>
/// 设置为斥力模式:从攻击区域中心向外推开目标。
/// </summary>
public ImpulseSubmodule WithRepulsion(float strengthXZ, float strengthY = 0)
{
this.forceMode = ImpulseForceMode.Repulsion;
this.strengthXZ = strengthXZ;
this.strengthY = strengthY;
return this;
}
/// <summary>
/// 设置为吸力模式:将目标拉向攻击区域中心。
/// </summary>
public ImpulseSubmodule WithSuction(float strengthXZ, float strengthY = 0)
{
this.forceMode = ImpulseForceMode.Suction;
this.strengthXZ = strengthXZ;
this.strengthY = strengthY;
return this;
}
/// <summary>
/// 设置为自定义力矢量模式。
/// </summary>
public ImpulseSubmodule WithCustomForce(Vector3 force)
{
this.forceMode = ImpulseForceMode.Custom;
this.customForce = force;
return this;
}
/// <summary>
/// 设置为动态力模式:沿攻击区域 forward 方向施加指定强度的力。
/// </summary>
public ImpulseSubmodule WithDynamicForce(float strength, bool isFlattened = true)
{
this.forceMode = isFlattened ? ImpulseForceMode.FlattenedDynamic : ImpulseForceMode.Dynamic;
this.dynamicStrength = strength;
return this;
}
/// <summary>
/// 标记为击飞:无视抗性,自动附加眩晕。
/// parabolic 为 true 时使用物理弧线(距离击飞),为 false 时使用曲线衰减(空战浮空)。
/// </summary>
/// <param name="strengthY">竖直方向的初始击飞强度。</param>
/// <param name="parabolic">是否使用抛物线物理。默认 true。</param>
public ImpulseSubmodule WithLaunch(float strengthY, bool parabolic = true)
{
this.isLaunch = true;
this.strengthY = strengthY;
this.isParabolic = parabolic;
return this;
}
/// <summary>
/// 标记击退时附加眩晕(击飞始终自动眩晕,无需此方法)。
/// </summary>
/// <param name="duration">眩晕持续时间。0 表示跟随脉冲持续时间。</param>
public ImpulseSubmodule WithStun(float duration = 0f)
{
this.applyStun = true;
this.stunDuration = duration;
return this;
}
/// <summary>
/// 设置命中后的重力修正。通过 VerticalMoveModification Buff 实现。
/// multiplier 小于 1 使目标浮空(空战连击),大于 1 使目标加速坠落Finisher
/// 当 multiplier 大于 1 时,会先清除目标身上已有的浮空 Buff确保坠落生效。
/// </summary>
/// <param name="multiplier">重力缩放倍率。0 = 完全浮空1 = 正常,大于 1 = 加速坠落。</param>
/// <param name="duration">重力修正持续时间(秒)。</param>
public ImpulseSubmodule WithGravityModifier(float multiplier, float duration)
{
this.gravityMultiplier = multiplier;
this.gravityDuration = duration;
return this;
}
// ────────────────────────────────────────────────────────────────────
// Force Calculation & Application
// ────────────────────────────────────────────────────────────────────
/// <summary>
/// 根据当前力模式计算施加给目标的最终力矢量。
/// </summary>
public Vector3 GetFinalForce(CharacterBase target)
{
return forceMode switch
{
ImpulseForceMode.Repulsion =>
(target.centerPoint.position - attackArea.topParent.position).Flatten().normalized * strengthXZ
+ Vector3.up * strengthY,
ImpulseForceMode.Suction =>
(attackArea.topParent.position - target.centerPoint.position).Flatten().normalized * strengthXZ
+ Vector3.up * strengthY,
ImpulseForceMode.Dynamic =>
attackArea.topParent.forward * dynamicStrength,
ImpulseForceMode.FlattenedDynamic =>
attackArea.topParent.forward.Flatten().normalized * dynamicStrength,
_ => customForce,
};
}
/// <summary>
/// 对目标角色施加脉冲力。自动区分 Automata 和玩家的处理路径。
/// </summary>
public void ApplyImpulse(CharacterBase target)
{
Vector3 force = GetFinalForce(target);
if (target.movementSc is AutomataLandMovementSubcontroller automataSc)
{
automataSc.ApplyHitImpact(force, isLaunch, isParabolic,
impulseDuration, impulseCurve, applyStun, stunDuration,
gravityMultiplier, gravityDuration);
}
else
{
target.movementSc.impulseSm.ApplyImpulse(force, isLaunch, impulseDuration, impulseCurve, isParabolic);
ApplyGravityModifier(target);
}
}
/// <summary>
/// 对非 Automata 目标(玩家等)施加重力修正 Buff。
/// </summary>
private void ApplyGravityModifier(CharacterBase target)
{
if (gravityDuration <= 0f) return;
if (gravityMultiplier > 1f)
{
ClearExistingGravityModifier(target);
}
new VerticalMoveModification(gravityDuration, gravityMultiplier).Apply(target);
}
/// <summary>
/// 清除目标身上已有的 VerticalMoveModification BuffFinisher 坠落前调用)。
/// </summary>
private static void ClearExistingGravityModifier(CharacterBase target)
{
if (target.buffSm.TryGetBuff<VerticalMoveModification>(out var existingBuff))
{
existingBuff.Remove();
}
}
}
}