更新
This commit is contained in:
@@ -10,8 +10,8 @@ namespace Cielonos.MainGame.Characters
|
||||
|
||||
public AdditionalForceSubmodule(CharacterBase character) : base(character)
|
||||
{
|
||||
additionalForceXZ = new LerpVector3(Vector3.zero, 5f);
|
||||
additionalForceY = new LerpFloat(0f, 5f);
|
||||
additionalForceXZ = new LerpVector3(Vector3.zero, 1f);
|
||||
additionalForceY = new LerpFloat(0f, 1f);
|
||||
}
|
||||
|
||||
public void AddForce(Vector3 force)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Cielonos.MainGame.Characters;
|
||||
using Sirenix.OdinInspector;
|
||||
using SLSUtilities.FunctionalAnimation;
|
||||
using UnityEngine;
|
||||
|
||||
@@ -23,8 +24,11 @@ namespace Cielonos.MainGame
|
||||
|
||||
|
||||
public float currentPlaySpeedMultiplier = 1f;
|
||||
public float currentPlayTime => currentRuntimeFuncAnim.currentPlayTime;
|
||||
[ShowInInspector]
|
||||
public float currentPlayTime => currentRuntimeFuncAnim?.currentPlayTime ?? 0f;
|
||||
public float currentNormalizedPlayTime => Mathf.Min(1, currentPlayTime / currentClip.length);
|
||||
[ShowInInspector]
|
||||
public float currentFrame => currentRuntimeFuncAnim?.currentPlayTime * currentRuntimeFuncAnim?.funcAnimData.animationClip.frameRate ?? 0f;
|
||||
public float currentScaledClipLength => currentClip.length / currentPlaySpeedMultiplier;
|
||||
|
||||
|
||||
@@ -216,7 +220,7 @@ namespace Cielonos.MainGame
|
||||
return;
|
||||
}
|
||||
|
||||
currentRuntimeFuncAnim.currentPlayTime += Time.deltaTime * currentData.animInfo.overridePlaySpeed * currentPlaySpeedMultiplier;
|
||||
currentRuntimeFuncAnim.currentPlayTime += owner.owner.selfTimeSm.DeltaTime * currentData.animInfo.overridePlaySpeed * currentPlaySpeedMultiplier;
|
||||
|
||||
if (currentPlayTime >= currentClip.length)
|
||||
{
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Cielonos.MainGame.Characters;
|
||||
using UniRx;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Cielonos.MainGame.Characters
|
||||
{
|
||||
public class SelfTimeSubmodule : SubmoduleBase<CharacterBase>
|
||||
public partial class SelfTimeSubmodule : SubmoduleBase<CharacterBase>
|
||||
{
|
||||
public FloatReactiveProperty timeScaleCoefficient;
|
||||
public float TimeScale => timeScaleCoefficient.Value * Time.timeScale;
|
||||
@@ -13,10 +16,150 @@ namespace Cielonos.MainGame.Characters
|
||||
public SelfTimeSubmodule(CharacterBase entity) : base(entity)
|
||||
{
|
||||
timeScaleCoefficient = new FloatReactiveProperty(1);
|
||||
|
||||
if (entity.animationSc != null)
|
||||
{
|
||||
timeScaleCoefficient.Subscribe(x =>
|
||||
{
|
||||
entity.animationSc.fullBodyFuncAnimSm.currentPlaySpeedMultiplier = x;
|
||||
});
|
||||
}
|
||||
|
||||
if (entity.animationSc.animator != null)
|
||||
{
|
||||
timeScaleCoefficient.Subscribe(x => { entity.animationSc.animator.speed = x; });
|
||||
timeScaleCoefficient.Subscribe(x =>
|
||||
{
|
||||
entity.animationSc.animator.speed = x;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public partial class SelfTimeSubmodule
|
||||
{
|
||||
/// <summary>
|
||||
/// 添加一个基于本地时间(Local DeltaTime)的计时器
|
||||
/// </summary>
|
||||
public IDisposable AddLocalTimer(float duration, Action onComplete, Action onUpdate = null)
|
||||
{
|
||||
// 用于记录累积时间
|
||||
float accumulatedTime = 0f;
|
||||
|
||||
return Observable.EveryUpdate()
|
||||
.Select(_ => DeltaTime) // 1. 获取每帧的真实 DeltaTime
|
||||
.TakeWhile(dt =>
|
||||
{
|
||||
// 2. 累加时间
|
||||
accumulatedTime += dt;
|
||||
// 3. 如果累积时间小于总时长,继续流;否则停止流并触发 OnCompleted
|
||||
return accumulatedTime < duration;
|
||||
})
|
||||
.Subscribe(
|
||||
_ => onUpdate?.Invoke(), // 每帧更新时执行 Action
|
||||
() => onComplete?.Invoke() // 4. 流结束时(TakeWhile 返回 false)执行 Action
|
||||
).AddTo(owner); // 5. 绑定生命周期到角色,防止内存泄漏
|
||||
}
|
||||
}
|
||||
|
||||
public partial class SelfTimeSubmodule
|
||||
{
|
||||
// 缓存一个默认的抛物线曲线,避免每次 null 时都 new 一个
|
||||
// 形状:(0,0) -> (0.5, 1) -> (1, 0)
|
||||
private static readonly AnimationCurve DefaultParabola = new AnimationCurve(
|
||||
new Keyframe(0f, 0f),
|
||||
new Keyframe(0.5f, 1f),
|
||||
new Keyframe(1f, 0f)
|
||||
);
|
||||
|
||||
private IDisposable hitStopDisposable;
|
||||
|
||||
/// <summary>
|
||||
/// 应用顿帧(Hit Stop)
|
||||
/// </summary>
|
||||
/// <param name="duration">持续时间(秒,基于全局游戏时间)</param>
|
||||
/// <param name="targetScale">目标缩放倍率(通常为 0 或 0.1)</param>
|
||||
public void ModifyTimeScale(float duration, float targetScale = 0f)
|
||||
{
|
||||
// 1. 如果之前有正在进行的顿帧,先取消它(防止旧的恢复逻辑覆盖新的设置)
|
||||
hitStopDisposable?.Dispose();
|
||||
|
||||
// 2. 设置当前的缩放倍率
|
||||
timeScaleCoefficient.Value = targetScale;
|
||||
|
||||
// 3. 开启计时器
|
||||
// 注意:这里使用 Scheduler.MainThread,它是基于 Time.time (全局时间) 的。
|
||||
// 这意味着:
|
||||
// - 它会受到 Time.timeScale (全局暂停) 的影响(符合预期,游戏暂停时顿帧也该暂停)。
|
||||
// - 它 *不会* 受到 timeScaleCoefficient (我们自己改的本地时间) 的影响(关键!)。
|
||||
hitStopDisposable = Observable.Timer(TimeSpan.FromSeconds(duration), Scheduler.MainThread)
|
||||
.Subscribe(_ =>
|
||||
{
|
||||
// 计时结束,恢复为 1
|
||||
timeScaleCoefficient.Value = 1f;
|
||||
hitStopDisposable = null;
|
||||
})
|
||||
.AddTo(owner); // 安全性:如果角色在顿帧期间死亡/销毁,自动取消计时器
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 使用曲线动态修改本地时间流速
|
||||
/// </summary>
|
||||
/// <param name="duration">持续时间(秒)</param>
|
||||
/// <param name="start">曲线值为0时对应的时间倍率(通常是初始值)</param>
|
||||
/// <param name="peak">曲线值为1时对应的时间倍率(通常是极值)</param>
|
||||
/// <param name="curve">时间变化曲线(归一化:X轴0~1,Y轴通常0~1)。如果为null,则使用默认的“先升后降”抛物线。</param>
|
||||
public void ModifyTimeScale(float duration, float start, float peak, AnimationCurve curve = null)
|
||||
{
|
||||
// 1. 清理旧的计时器
|
||||
hitStopDisposable?.Dispose();
|
||||
|
||||
// 2. 处理默认曲线逻辑
|
||||
curve ??= DefaultParabola;
|
||||
|
||||
// 3. 记录开始时的累计时间
|
||||
float timer = 0f;
|
||||
|
||||
// 4. 开启每帧更新的流
|
||||
hitStopDisposable = Observable.EveryUpdate()
|
||||
.TakeWhile(_ => timer < duration) // 当时间超过 duration 时结束流
|
||||
.Subscribe(
|
||||
_ =>
|
||||
{
|
||||
// 累加时间 (使用 Time.deltaTime 以响应全局暂停)
|
||||
timer += Time.deltaTime;
|
||||
|
||||
// 计算归一化进度 (0.0 ~ 1.0)
|
||||
float progress = Mathf.Clamp01(timer / duration);
|
||||
|
||||
// 核心逻辑:
|
||||
// A. 从曲线获取当前的“强度” (Y轴值)
|
||||
float curveValue = curve.Evaluate(progress);
|
||||
|
||||
// B. 在 start 和 peak 之间根据强度进行插值
|
||||
// 当 curveValue = 0 时,结果为 start
|
||||
// 当 curveValue = 1 时,结果为 peak
|
||||
float currentScale = Mathf.Lerp(start, peak, curveValue);
|
||||
|
||||
// C. 应用到响应式属性
|
||||
timeScaleCoefficient.Value = currentScale;
|
||||
},
|
||||
() =>
|
||||
{
|
||||
// 5. 计时结束后的收尾工作
|
||||
// 通常为了安全,结束后我们会强制恢复到 1.0 (正常速度)
|
||||
// 或者你可以恢复到 start,视具体需求而定
|
||||
timeScaleCoefficient.Value = 1f;
|
||||
hitStopDisposable = null;
|
||||
}
|
||||
)
|
||||
.AddTo(owner); // 绑定生命周期
|
||||
}
|
||||
|
||||
// 可选:提供一个强制恢复的方法,用于因为某些逻辑需要立刻打断顿帧时调用
|
||||
public void ResetTimeScale()
|
||||
{
|
||||
hitStopDisposable?.Dispose();
|
||||
timeScaleCoefficient.Value = 1f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,8 @@ namespace Cielonos.MainGame.Characters
|
||||
Disarm = 2, //缴械,无法攻击(使用主武器)
|
||||
Restraint = 3, //束缚,无法移动
|
||||
|
||||
Stun = 100, //眩晕
|
||||
|
||||
//正面状态
|
||||
Invincible = 1000, //无敌
|
||||
Invisible = 1001, //隐身
|
||||
|
||||
Reference in New Issue
Block a user