后处理+FEEL完全改进

This commit is contained in:
SoulliesOfficial
2025-12-22 18:36:29 -05:00
parent c3914da4ac
commit a2052bfe16
1427 changed files with 193092 additions and 374110 deletions

View File

@@ -1,5 +1,6 @@
using System;
using Sirenix.OdinInspector;
using SLSFramework.General;
using SLSFramework.WwiseAssistance;
using SLSUtilities.FunctionalAnimation;
using UniRx;
@@ -220,4 +221,24 @@ namespace Cielonos.MainGame.Characters
return false;
}
}
public partial class CharacterBase
{
public Vector2 GetNormalizedScreenPosition(Camera cam = null)
{
if (this is Player player)
{
cam ??= player.viewSc.playerCamera;
}
else
{
if (cam == null)
{
throw new ArgumentNullException(nameof(cam), "Camera must be provided for non-player characters.");
}
}
return SpaceConverter.WorldPointToNormalizedScreenPoint(flexibleCenterPoint.position, cam);
}
}
}

View File

@@ -105,7 +105,7 @@ namespace Cielonos.MainGame.Characters
private void BoneShakeLateUpdate()
{
float dt = Time.deltaTime;
float dt = owner.selfTimeSm.DeltaTime;
for (int i = activeShakes.Count - 1; i >= 0; i--)
{
@@ -267,7 +267,7 @@ namespace Cielonos.MainGame.Characters
}
else
{
animator.CrossFade("Empty", 0f, fullBodyActionIndex, 0);
animator.CrossFade("Empty", 0.1f, fullBodyActionIndex, 0);
}
}
}

View File

@@ -13,6 +13,8 @@ namespace Cielonos.MainGame.Characters
{
foreach (var feedbackUnit in feedbacks.Values)
{
float timeScaleMultiplier = owner.selfTimeSm.TimeScale;
feedbackUnit.feedback.ExternalTimeScale = timeScaleMultiplier;
feedbackUnit.Update();
}
}

View File

@@ -141,7 +141,9 @@ namespace Cielonos.MainGame.Characters
{
sourceItem.audioContainer.PlaySoundFX("PerfectBlock");
sourceItem.feedbackSc["PerfectBlock"]?.Play();
sourceItem.blockData.InstantiateBlockEffect(perfectEffectName, blockEffectPosition, Quaternion.identity);
GameObject pObj = sourceItem.blockData.InstantiateBlockEffect(perfectEffectName, sourceCharacter, blockEffectPosition, Quaternion.identity);
pObj.GetComponent<ParticleSystem>().Simulate(0.1f, true, true);
pObj.GetComponent<ParticleSystem>().Play();//TODO: 增加起点时间参数
}
onPerfectBlock?.Invoke(attackArea);
@@ -153,7 +155,7 @@ namespace Cielonos.MainGame.Characters
{
sourceItem.audioContainer.PlaySoundFX("NormalBlock");
sourceItem.feedbackSc["NormalBlock"]?.Play();
sourceItem.blockData.InstantiateBlockEffect(normalEffectName, blockEffectPosition, Quaternion.identity);
sourceItem.blockData.InstantiateBlockEffect(normalEffectName, sourceCharacter, blockEffectPosition, Quaternion.identity);
}
onNormalBlock?.Invoke(attackArea);

View File

@@ -142,23 +142,23 @@ namespace Cielonos.MainGame.Characters
public void PerfectDodge()
{
onPerfectDodge?.Invoke();
if (sourceItem == null)
{
sourceCharacter.feedbackSc["PerfectDodge"].Play();
Debug.Log("Perfect Dodge!");
}
onPerfectDodge?.Invoke();
}
public void NormalDodge()
{
onNormalDodge?.Invoke();
if (sourceItem == null)
{
sourceCharacter.feedbackSc["NormalDodge"].Play();
}
onNormalDodge?.Invoke();
}
}
}

View File

@@ -136,6 +136,11 @@ namespace Cielonos.MainGame.Characters
foreach (Material mat in baseRenderMaterials)
{
if (!mat.HasProperty("_RimParams"))
{
continue;
}
Tweener rimTween = mat.DOVector(new Vector4(1, 1, 4, 1), "_RimParams", 0.5f)
.From(new Vector4(0, 1, 4, 1))
.OnPlay(() =>
@@ -152,33 +157,6 @@ namespace Cielonos.MainGame.Characters
}
getHitBlinkTween.Play();
/*getHitBlinkTween.OnPlay(() =>
{
effectContainers["GetHitBlink"].SetActive(true);
});
foreach (Material mat in effectRenderMaterials["GetHitBlink"])
{
TweenerCore<Color, Color, ColorOptions> matTween = mat.DOColor(Color.white * 0.5f, "_EmissionColor", 0.05f)
.OnStart(() => mat.EnableKeyword("_EMISSION"))
.From(Color.black)
.SetEase(Ease.OutQuad)
.OnComplete(() =>
{
mat.SetColor("_EmissionColor", Color.black);
mat.DisableKeyword("_EMISSION");
});
getHitBlinkTween.Join(matTween);
}
getHitBlinkTween.SetLoops(2, LoopType.Yoyo);
getHitBlinkTween.OnComplete(() =>
{
effectContainers["GetHitBlink"].SetActive(false);
});*/
getHitBlinkTween.Play();
}
}

View File

@@ -1,35 +1,107 @@
using System;
using System.Collections;
using System.Collections.Generic;
using Cielonos.MainGame.Characters;
using UniRx;
using UnityEngine;
using SLSFramework.General;
namespace Cielonos.MainGame.Characters
{
public partial class SelfTimeSubmodule : SubmoduleBase<CharacterBase>
{
public FloatReactiveProperty timeScaleCoefficient;
public float TimeScale => timeScaleCoefficient.Value * Time.timeScale;
public float DeltaTime => timeScaleCoefficient.Value * Time.deltaTime;
private TimeManager timeManager => TimeManager.Instance;
private IObservable<float> timeScaleObservable;
public FloatReactiveProperty localTimeScale;
private float globalTimeScale => timeManager.globalTimeScale.Value;
private float playerTimeScale => timeManager.playerTimeScale.Value;
private float alliedMinionTimeScale => timeManager.alliedMinionTimeScale.Value;
private float enemyTimeScale => timeManager.enemyTimeScale.Value;
private float nonPlayerTimeScale => timeManager.nonPlayerTimeScale.Value;
public float TimeScale => owner.fraction switch
{
Fraction.Player => localTimeScale.Value * globalTimeScale * playerTimeScale,
Fraction.AlliedMinion => localTimeScale.Value * globalTimeScale * alliedMinionTimeScale * nonPlayerTimeScale,
Fraction.Enemy => localTimeScale.Value * globalTimeScale * enemyTimeScale * nonPlayerTimeScale,
Fraction.Neutral => localTimeScale.Value * globalTimeScale * nonPlayerTimeScale,
_ => localTimeScale.Value * globalTimeScale
};
public float DeltaTime => owner.fraction switch
{
Fraction.Player => Time.deltaTime * localTimeScale.Value * globalTimeScale * playerTimeScale,
Fraction.AlliedMinion => Time.deltaTime * localTimeScale.Value * globalTimeScale * alliedMinionTimeScale * nonPlayerTimeScale,
Fraction.Enemy => Time.deltaTime * localTimeScale.Value * globalTimeScale * enemyTimeScale * nonPlayerTimeScale,
Fraction.Neutral => Time.deltaTime * localTimeScale.Value * globalTimeScale * nonPlayerTimeScale,
_ => Time.deltaTime * localTimeScale.Value * globalTimeScale
};
public SelfTimeSubmodule(CharacterBase entity) : base(entity)
{
timeScaleCoefficient = new FloatReactiveProperty(1);
localTimeScale = new FloatReactiveProperty(1);
switch (owner.fraction)
{
case Fraction.Player:
// 依赖: local, global, player
timeScaleObservable =
localTimeScale.CombineLatest(
timeManager.globalTimeScale,
timeManager.playerTimeScale,
(local, global, player) => local * global * player
);
break;
case Fraction.AlliedMinion:
// 依赖: local, global, alliedMinion, nonPlayer
timeScaleObservable =
localTimeScale.CombineLatest(
timeManager.globalTimeScale,
timeManager.alliedMinionTimeScale,
timeManager.nonPlayerTimeScale,
(local, global, minion, nonPlayer) => local * global * minion * nonPlayer
);
break;
case Fraction.Enemy:
// 依赖: local, global, enemy, nonPlayer
timeScaleObservable =
localTimeScale.CombineLatest(
timeManager.globalTimeScale,
timeManager.enemyTimeScale,
timeManager.nonPlayerTimeScale,
(local, global, enemy, nonPlayer) => local * global * enemy * nonPlayer
);
break;
case Fraction.Neutral:
// 依赖: local, global, nonPlayer
timeScaleObservable =
localTimeScale.CombineLatest(
timeManager.globalTimeScale,
timeManager.nonPlayerTimeScale,
(local, global, nonPlayer) => local * global * nonPlayer
);
break;
default:
throw new ArgumentOutOfRangeException();
}
if (entity.animationSc != null)
{
timeScaleCoefficient.Subscribe(x =>
timeScaleObservable.Subscribe(timeScale =>
{
entity.animationSc.fullBodyFuncAnimSm.currentPlaySpeedMultiplier = x;
entity.animationSc.fullBodyFuncAnimSm.currentPlaySpeedMultiplier = timeScale;
});
}
if (entity.animationSc.animator != null)
{
timeScaleCoefficient.Subscribe(x =>
timeScaleObservable.Subscribe(timeScale =>
{
entity.animationSc.animator.speed = x;
entity.animationSc.animator.speed = timeScale;
});
}
}
@@ -59,6 +131,26 @@ namespace Cielonos.MainGame.Characters
() => onComplete?.Invoke() // 4. 流结束时TakeWhile 返回 false执行 Action
).AddTo(owner); // 5. 绑定生命周期到角色,防止内存泄漏
}
public IDisposable AddGlobalTimer(float duration, Action onComplete, Action onUpdate = null)
{
// 用于记录累积时间
float accumulatedTime = 0f;
return Observable.EveryUpdate()
.Select(_ => Time.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
@@ -84,7 +176,7 @@ namespace Cielonos.MainGame.Characters
hitStopDisposable?.Dispose();
// 2. 设置当前的缩放倍率
timeScaleCoefficient.Value = targetScale;
localTimeScale.Value = targetScale;
// 3. 开启计时器
// 注意:这里使用 Scheduler.MainThread它是基于 Time.time (全局时间) 的。
@@ -95,7 +187,7 @@ namespace Cielonos.MainGame.Characters
.Subscribe(_ =>
{
// 计时结束,恢复为 1
timeScaleCoefficient.Value = 1f;
localTimeScale.Value = 1f;
hitStopDisposable = null;
})
.AddTo(owner); // 安全性:如果角色在顿帧期间死亡/销毁,自动取消计时器
@@ -105,17 +197,16 @@ namespace Cielonos.MainGame.Characters
/// 使用曲线动态修改本地时间流速
/// </summary>
/// <param name="duration">持续时间(秒)</param>
/// <param name="start">曲线值为0时对应的时间倍率通常是初始值</param>
/// <param name="peak">曲线值为1时对应的时间倍率通常是极值</param>
/// <param name="curve">时间变化曲线归一化X轴0~1Y轴通常0~1。如果为null则使用默认的“先升后降”抛物线。</param>
public void ModifyTimeScale(float duration, float start, float peak, AnimationCurve curve = null)
/// <param name="zeroValue">曲线起点值</param>
/// <param name="oneValue">曲线终点值</param>
/// <param name="easeType">插值曲线(null 则使用默认的 EaseInOut (从0到1) 曲线)</param>
public void ModifyTimeScale(float duration, EaseType easeType, float zeroValue = 0, float oneValue = 1)
{
// 1. 清理旧的计时器
hitStopDisposable?.Dispose();
// 2. 处理默认曲线逻辑
curve ??= DefaultParabola;
AnimationCurve curve = Ease.GetCurve(easeType);
// 3. 记录开始时的累计时间
float timer = 0f;
@@ -125,30 +216,27 @@ namespace Cielonos.MainGame.Characters
.Subscribe(
_ =>
{
// 累加时间 (使用 Time.deltaTime 以响应全局暂停)
timer += Time.deltaTime;
// 累加时间
timer += DeltaTime;
// 计算归一化进度 (0.0 ~ 1.0)
float progress = Mathf.Clamp01(timer / duration);
// 计算归一化时间(0 1
float normalizedTime = Mathf.Clamp01(timer / duration);
// 核心逻辑:
// A. 从曲线获取当前的“强度” (Y轴值)
float curveValue = curve.Evaluate(progress);
// 根据曲线获取当前的插值系数
float curveValue = curve.Evaluate(normalizedTime);
// B. 在 start 和 peak 之间根据强度进行插
// 当 curveValue = 0 时,结果为 start
// 当 curveValue = 1 时,结果为 peak
float currentScale = Mathf.Lerp(start, peak, curveValue);
// 计算当前的时间缩放
float currentScale = curveValue * (oneValue - zeroValue) + zeroValue;
// C. 应用到响应式属性
timeScaleCoefficient.Value = currentScale;
// 应用到 timeScaleCoefficient
localTimeScale.Value = currentScale;
},
() =>
{
// 5. 计时结束后的收尾工作
// 通常为了安全,结束后我们会强制恢复到 1.0 (正常速度)
// 或者你可以恢复到 start视具体需求而定
timeScaleCoefficient.Value = 1f;
localTimeScale.Value = 1f;
hitStopDisposable = null;
}
)
@@ -159,7 +247,7 @@ namespace Cielonos.MainGame.Characters
public void ResetTimeScale()
{
hitStopDisposable?.Dispose();
timeScaleCoefficient.Value = 1f;
localTimeScale.Value = 1f;
}
}
}