新Feedback系统
This commit is contained in:
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 484bc3a7a27bb1945ba1d6c8e2fcea5c
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -195,6 +195,11 @@ namespace Cielonos.MainGame.Characters
|
||||
_ => 0
|
||||
};
|
||||
|
||||
if (string.IsNullOrEmpty(funcAnimName))
|
||||
{
|
||||
funcAnimName = GetHitFuncAnimName(breakthroughType, direction);
|
||||
}
|
||||
|
||||
if (CheckBreakthrough(breakthroughType))
|
||||
{
|
||||
if (!animationSc.fullBodyFuncAnimSm.Stop(disruptionType))
|
||||
@@ -225,6 +230,7 @@ namespace Cielonos.MainGame.Characters
|
||||
protected virtual string GetHitFuncAnimName(BreakthroughType breakthroughType, Vector3 direction)
|
||||
{
|
||||
string prefix = "GetHitMedium";
|
||||
|
||||
if (breakthroughType >= BreakthroughType.Medium)
|
||||
{
|
||||
prefix = breakthroughType switch
|
||||
@@ -236,7 +242,7 @@ namespace Cielonos.MainGame.Characters
|
||||
_ => "GetHit"
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
string directionStr = "Front";
|
||||
if (direction != default)
|
||||
{
|
||||
@@ -256,6 +262,16 @@ namespace Cielonos.MainGame.Characters
|
||||
}
|
||||
|
||||
string fullName = prefix + directionStr;
|
||||
|
||||
if (!animationSc.fullBodyFuncAnimSm.collection.ContainsKey(fullName))
|
||||
{
|
||||
if (prefix == "GetHitForced")
|
||||
{
|
||||
prefix = "GetHitHeavy"; //如果没有专门的“强制等级”受击动画,就使用“重度等级”的动画
|
||||
fullName = prefix + directionStr;
|
||||
}
|
||||
}
|
||||
|
||||
return fullName;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -232,67 +232,12 @@ namespace Cielonos.MainGame.Characters
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void PlayGetHitAnimation(string getHitAnimPrefix, out float animDuration,
|
||||
Vector3 direction = default, string funcAnimName = "")
|
||||
public virtual void PlayGetHitAnimation(string funcAnimName, out float animDuration,
|
||||
Vector3 direction = default)
|
||||
{
|
||||
bool useCustomFuncAnim = !string.IsNullOrEmpty(funcAnimName);
|
||||
int fullBodyActionIndex = animator.GetLayerIndex("FullBodyAction");
|
||||
|
||||
if (useCustomFuncAnim)
|
||||
{
|
||||
fullBodyFuncAnimSm.Play(funcAnimName);
|
||||
animDuration = fullBodyFuncAnimSm.currentClip.length;
|
||||
return;
|
||||
}
|
||||
|
||||
string getHitAnim = getHitAnimPrefix + "Front";
|
||||
string getHitFrontAnim = getHitAnimPrefix + "Front";
|
||||
float normalizedTransitionDuration = 0.1f / animator.GetCurrentAnimatorStateInfo(fullBodyActionIndex).length;
|
||||
animDuration = 0f;
|
||||
if (animator.HasState(fullBodyActionIndex, Animator.StringToHash(getHitAnim)))
|
||||
{
|
||||
direction.y = 0;
|
||||
direction = direction.normalized;
|
||||
float angle = Vector3.SignedAngle(transform.forward, direction, Vector3.up);
|
||||
|
||||
string directionStr = angle switch
|
||||
{
|
||||
> -45f and <= 45f => "Back",
|
||||
> 45f and <= 135f => "Left",
|
||||
> -135f and <= -45f => "Right",
|
||||
_ => "Front"
|
||||
};
|
||||
|
||||
getHitAnim = getHitAnimPrefix + directionStr;
|
||||
if (direction == default || !animator.HasState(fullBodyActionIndex, Animator.StringToHash(getHitAnim)))
|
||||
{
|
||||
animator.CrossFade(getHitFrontAnim, normalizedTransitionDuration, fullBodyActionIndex, 0);
|
||||
animDuration = mapper.GetClip(getHitFrontAnim).length;
|
||||
}
|
||||
else
|
||||
{
|
||||
animator.CrossFade(getHitAnim, normalizedTransitionDuration, fullBodyActionIndex, 0);
|
||||
animDuration = mapper.GetClip(getHitAnim).length;
|
||||
}
|
||||
}
|
||||
else if (animator.HasState(fullBodyActionIndex, Animator.StringToHash(getHitAnimPrefix)))
|
||||
{
|
||||
getHitAnim = getHitAnimPrefix;
|
||||
animator.CrossFade(getHitAnim, normalizedTransitionDuration, fullBodyActionIndex, 0);
|
||||
animDuration = mapper.GetClip(getHitAnim).length;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (animator.HasState(fullBodyActionIndex, Animator.StringToHash("GetHit")))
|
||||
{
|
||||
animator.CrossFade("GetHit", normalizedTransitionDuration, fullBodyActionIndex, 0);
|
||||
animDuration = mapper.GetClip("GetHit").length;
|
||||
}
|
||||
else
|
||||
{
|
||||
animator.CrossFade("Empty", 0.1f, fullBodyActionIndex, 0);
|
||||
}
|
||||
}
|
||||
fullBodyFuncAnimSm.Play(funcAnimName);
|
||||
animDuration = fullBodyFuncAnimSm.currentData.Interval(IntervalType.ActionDisruption).StartTime;
|
||||
//animDuration = fullBodyFuncAnimSm.currentData.Interval(IntervalType.Active).EndTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
using System.Collections.Generic;
|
||||
using Cielonos.MainGame.Effects.Feedback;
|
||||
using Lean.Pool;
|
||||
using MoreMountains.Feedbacks;
|
||||
using MoreMountains.FeedbacksForThirdParty;
|
||||
using SLSUtilities.Feedback;
|
||||
using SLSUtilities.FeelAssistance;
|
||||
using UnityEngine;
|
||||
|
||||
@@ -9,18 +11,114 @@ namespace Cielonos.MainGame.Characters
|
||||
{
|
||||
public partial class FeedbackSubcontroller : SubcontrollerBase<CharacterBase>
|
||||
{
|
||||
// === 旧系统(Feel)—— 保留向后兼容 ===
|
||||
public Dictionary<string, FeedbackUnit> feedbacks;
|
||||
public FeedbackUnit this[string feedbackName] => feedbacks?.GetValueOrDefault(feedbackName, null);
|
||||
|
||||
// === 新系统(Feedback System)===
|
||||
public FeedbackDataCollection feedbackDataCollection;
|
||||
|
||||
private CharacterFeedbackTimeProvider _timeProvider;
|
||||
private readonly List<FeedbackPlayer> _activePlayers = new List<FeedbackPlayer>(8);
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
if (owner != null)
|
||||
{
|
||||
_timeProvider = new CharacterFeedbackTimeProvider(owner);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 通过新系统播放一个 FeedbackData。
|
||||
/// </summary>
|
||||
public FeedbackPlayer PlayFeedback(FeedbackData data, bool stopPrevious = false)
|
||||
{
|
||||
if (data == null) return null;
|
||||
|
||||
if (stopPrevious)
|
||||
{
|
||||
StopFeedback(data);
|
||||
}
|
||||
|
||||
var player = new FeedbackPlayer(data, _timeProvider, owner?.transform);
|
||||
player.Play();
|
||||
_activePlayers.Add(player);
|
||||
return player;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 通过名称从 FeedbackDataCollection 中查找并播放。
|
||||
/// </summary>
|
||||
public FeedbackPlayer PlayFeedback(string name, bool stopPrevious = false)
|
||||
{
|
||||
if (feedbackDataCollection == null)
|
||||
{
|
||||
Debug.LogWarning($"[FeedbackSubcontroller] feedbackDataCollection is null on {owner?.name}.");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!feedbackDataCollection.TryGet(name, out FeedbackData data))
|
||||
{
|
||||
Debug.LogWarning($"[FeedbackSubcontroller] FeedbackData '{name}' not found on {owner?.name}.");
|
||||
return null;
|
||||
}
|
||||
|
||||
return PlayFeedback(data, stopPrevious);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 停止指定 FeedbackData 的所有活跃播放器。
|
||||
/// </summary>
|
||||
public void StopFeedback(FeedbackData data)
|
||||
{
|
||||
for (int i = _activePlayers.Count - 1; i >= 0; i--)
|
||||
{
|
||||
if (_activePlayers[i].Data == data)
|
||||
{
|
||||
_activePlayers[i].Stop();
|
||||
_activePlayers.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 停止所有活跃的新系统播放器。
|
||||
/// </summary>
|
||||
public void StopAllFeedbacks()
|
||||
{
|
||||
for (int i = _activePlayers.Count - 1; i >= 0; i--)
|
||||
{
|
||||
_activePlayers[i].Stop();
|
||||
}
|
||||
_activePlayers.Clear();
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (feedbacks == null) return;
|
||||
|
||||
foreach (var feedbackUnit in feedbacks.Values)
|
||||
// 旧系统驱动
|
||||
if (feedbacks != null)
|
||||
{
|
||||
float timeScaleMultiplier = owner.selfTimeSm.TimeScale;
|
||||
feedbackUnit.feedback.ExternalTimeScale = timeScaleMultiplier;
|
||||
feedbackUnit.Update();
|
||||
foreach (var feedbackUnit in feedbacks.Values)
|
||||
{
|
||||
float timeScaleMultiplier = owner.selfTimeSm.TimeScale;
|
||||
feedbackUnit.feedback.ExternalTimeScale = timeScaleMultiplier;
|
||||
feedbackUnit.Update();
|
||||
}
|
||||
}
|
||||
|
||||
// 新系统驱动
|
||||
float dt = Time.unscaledDeltaTime;
|
||||
for (int i = _activePlayers.Count - 1; i >= 0; i--)
|
||||
{
|
||||
FeedbackPlayer player = _activePlayers[i];
|
||||
player.Tick(dt);
|
||||
|
||||
if (player.IsCompleted || !player.IsActive)
|
||||
{
|
||||
_activePlayers.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,11 +14,10 @@ namespace Cielonos.MainGame.Characters
|
||||
public AnimationClip landWalkForward, landWalkBack, landWalkLeft, landWalkRight;
|
||||
public AnimationClip landRunForward, landRunBack, landRunLeft, landRunRight;
|
||||
public AnimationClip jumpStart;
|
||||
[FormerlySerializedAs("jumpInAir")] public AnimationClip inAir;
|
||||
public AnimationClip inAir;
|
||||
public AnimationClip jumpLand;
|
||||
public AnimationClip getHitMediumFront, getHitMediumBack, getHitMediumLeft, getHitMediumRight;
|
||||
public AnimationClip getHitHeavyFront, getHitHeavyBack, getHitHeavyLeft, getHitHeavyRight;
|
||||
public AnimationClip getHitDisruptionFront, getHitDisruptionBack, getHitDisruptionLeft, getHitDisruptionRight;
|
||||
public FuncAnimData getHitMediumFront, getHitMediumBack, getHitMediumLeft, getHitMediumRight;
|
||||
public FuncAnimData getHitHeavyFront, getHitHeavyBack, getHitHeavyLeft, getHitHeavyRight;
|
||||
public AnimationClip riseUpFront, riseUpBack, riseUpLeft, riseUpRight;
|
||||
public AnimationClip incapacitation, death;
|
||||
|
||||
@@ -44,20 +43,28 @@ namespace Cielonos.MainGame.Characters
|
||||
animatorOverride["InAir"] = inAir;
|
||||
animatorOverride["JumpLand"] = jumpLand;
|
||||
|
||||
animatorOverride["GetHitMediumFront"] = getHitMediumFront;
|
||||
animatorOverride["GetHitMediumBack"] = getHitMediumBack;
|
||||
animatorOverride["GetHitMediumLeft"] = getHitMediumLeft;
|
||||
animatorOverride["GetHitMediumRight"] = getHitMediumRight;
|
||||
animatorOverride["GetHitHeavyFront"] = getHitHeavyFront;
|
||||
animatorOverride["GetHitHeavyBack"] = getHitHeavyBack;
|
||||
animatorOverride["GetHitHeavyLeft"] = getHitHeavyLeft;
|
||||
animatorOverride["GetHitHeavyRight"] = getHitHeavyRight;
|
||||
animatorOverride["GetHitDisruptionFront"] = getHitDisruptionFront;
|
||||
animatorOverride["GetHitDisruptionBack"] = getHitDisruptionBack;
|
||||
animatorOverride["GetHitDisruptionLeft"] = getHitDisruptionLeft;
|
||||
animatorOverride["GetHitDisruptionRight"] = getHitDisruptionRight;
|
||||
animatorOverride["GetHitMediumFront"] = getHitMediumFront.animationClip;
|
||||
animatorOverride["GetHitMediumBack"] = getHitMediumBack.animationClip;
|
||||
animatorOverride["GetHitMediumLeft"] = getHitMediumLeft.animationClip;
|
||||
animatorOverride["GetHitMediumRight"] = getHitMediumRight.animationClip;
|
||||
animatorOverride["GetHitHeavyFront"] = getHitHeavyFront.animationClip;
|
||||
animatorOverride["GetHitHeavyBack"] = getHitHeavyBack.animationClip;
|
||||
animatorOverride["GetHitHeavyLeft"] = getHitHeavyLeft.animationClip;
|
||||
animatorOverride["GetHitHeavyRight"] = getHitHeavyRight.animationClip;
|
||||
|
||||
animatorOverride["Dash"] = dash.animationClip;
|
||||
animatorOverride["Dodge"] = dodge.animationClip;
|
||||
}
|
||||
|
||||
animSc.fullBodyFuncAnimSm.Reset(getHitMediumFront);
|
||||
animSc.fullBodyFuncAnimSm.Reset(getHitMediumBack);
|
||||
animSc.fullBodyFuncAnimSm.Reset(getHitMediumLeft);
|
||||
animSc.fullBodyFuncAnimSm.Reset(getHitMediumRight);
|
||||
animSc.fullBodyFuncAnimSm.Reset(getHitHeavyFront);
|
||||
animSc.fullBodyFuncAnimSm.Reset(getHitHeavyBack);
|
||||
animSc.fullBodyFuncAnimSm.Reset(getHitHeavyLeft);
|
||||
animSc.fullBodyFuncAnimSm.Reset(getHitHeavyRight);
|
||||
|
||||
animSc.fullBodyFuncAnimSm.Reset(dash);
|
||||
animSc.fullBodyFuncAnimSm.Reset(dodge);
|
||||
}
|
||||
|
||||
@@ -24,8 +24,24 @@ namespace Cielonos.MainGame.Characters
|
||||
{
|
||||
base.Initialize();
|
||||
upperBodyFuncAnimSm = new FunctionalAnimationSubmodule(this, "UpperBodyAction");
|
||||
player.operationSc.OnDash += (inputDirection, length) => SetupDash(inputDirection, true, length);
|
||||
player.operationSc.OnDodge += SetupDodge;
|
||||
player.operationSc.OnDash += (inputDirection, length) =>
|
||||
{
|
||||
if (player.statusSm.HasStatus(StatusType.Stun))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SetupDash(inputDirection, true, length);
|
||||
};
|
||||
player.operationSc.OnDodge += (length)=>
|
||||
{
|
||||
if (player.statusSm.HasStatus(StatusType.Stun))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SetupDodge(length);
|
||||
};
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
|
||||
@@ -236,12 +236,18 @@ namespace Cielonos.MainGame.Characters.Inventory.Collections
|
||||
|
||||
public override void OnSpecialCPress()
|
||||
{
|
||||
CharacterBase target = BattleManager.EnemySm.GetNearestEnemy(5);
|
||||
if (functionSm["Block"].IsAvailable() && fullBodyFuncAnimSm.Stop(DisruptionType.ForcedAction) &&
|
||||
PlayTargetedAnimation("Block", target, 2f, false, true, upperBodyFuncAnimSm, 1f, 0.1f, true))
|
||||
comboSm.main.Reset();
|
||||
SetBlock();
|
||||
|
||||
if (!player.statusSm.HasStatus(StatusType.Stun))
|
||||
{
|
||||
comboSm.main.Reset();
|
||||
SetBlock();
|
||||
CharacterBase target = BattleManager.EnemySm.GetNearestEnemy(5);
|
||||
if (functionSm["Block"].IsAvailable() &&
|
||||
fullBodyFuncAnimSm.Stop(DisruptionType.ForcedAction) &&
|
||||
PlayTargetedAnimation("Block", target, 2f, false, true, upperBodyFuncAnimSm, 1f, 0.1f, true))
|
||||
{
|
||||
feedbackSc.PlayFeedback("TestFeedBack");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Cielonos.MainGame.Effects.Feedback;
|
||||
using MoreMountains.Feedbacks;
|
||||
using SLSUtilities.Feedback;
|
||||
using SLSUtilities.FeelAssistance;
|
||||
using UnityEngine;
|
||||
|
||||
@@ -8,16 +10,115 @@ namespace Cielonos.MainGame.Characters.Inventory
|
||||
{
|
||||
public class FeedbackSubcontroller : SubcontrollerBase<ItemBase>
|
||||
{
|
||||
// === 旧系统(Feel)—— 保留向后兼容 ===
|
||||
public Dictionary<string, FeedbackUnit> feedbacks;
|
||||
public FeedbackUnit this[string feedbackName] => feedbacks.GetValueOrDefault(feedbackName, null);
|
||||
|
||||
// === 新系统(Feedback System)===
|
||||
public FeedbackDataCollection feedbackDataCollection;
|
||||
|
||||
private CharacterFeedbackTimeProvider _timeProvider;
|
||||
private readonly List<FeedbackPlayer> _activePlayers = new List<FeedbackPlayer>(8);
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
if (owner?.player != null)
|
||||
{
|
||||
_timeProvider = new CharacterFeedbackTimeProvider(owner.player);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 通过新系统播放一个 FeedbackData。
|
||||
/// </summary>
|
||||
public FeedbackPlayer PlayFeedback(FeedbackData data, bool stopPrevious = false)
|
||||
{
|
||||
if (data == null) return null;
|
||||
|
||||
if (stopPrevious)
|
||||
{
|
||||
StopFeedback(data);
|
||||
}
|
||||
|
||||
Transform ownerTransform = owner?.player?.transform;
|
||||
var player = new FeedbackPlayer(data, _timeProvider, ownerTransform);
|
||||
player.Play();
|
||||
_activePlayers.Add(player);
|
||||
return player;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 通过名称从 FeedbackDataCollection 中查找并播放。
|
||||
/// </summary>
|
||||
public FeedbackPlayer PlayFeedback(string name, bool stopPrevious = false)
|
||||
{
|
||||
if (feedbackDataCollection == null)
|
||||
{
|
||||
Debug.LogWarning($"[Item.FeedbackSubcontroller] feedbackDataCollection is null on {owner?.name}.");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!feedbackDataCollection.TryGet(name, out FeedbackData data))
|
||||
{
|
||||
Debug.LogWarning($"[Item.FeedbackSubcontroller] FeedbackData '{name}' not found on {owner?.name}.");
|
||||
return null;
|
||||
}
|
||||
|
||||
return PlayFeedback(data, stopPrevious);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 停止指定 FeedbackData 的所有活跃播放器。
|
||||
/// </summary>
|
||||
public void StopFeedback(FeedbackData data)
|
||||
{
|
||||
for (int i = _activePlayers.Count - 1; i >= 0; i--)
|
||||
{
|
||||
if (_activePlayers[i].Data == data)
|
||||
{
|
||||
_activePlayers[i].Stop();
|
||||
_activePlayers.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 停止所有活跃的新系统播放器。
|
||||
/// </summary>
|
||||
public void StopAllFeedbacks()
|
||||
{
|
||||
for (int i = _activePlayers.Count - 1; i >= 0; i--)
|
||||
{
|
||||
_activePlayers[i].Stop();
|
||||
}
|
||||
_activePlayers.Clear();
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
foreach (var feedbackUnit in feedbacks.Values)
|
||||
// 旧系统驱动
|
||||
if (feedbacks != null)
|
||||
{
|
||||
float timeScaleMultiplier = owner.player.selfTimeSm.TimeScale;
|
||||
feedbackUnit.feedback.ExternalTimeScale = timeScaleMultiplier;
|
||||
feedbackUnit.Update();
|
||||
foreach (var feedbackUnit in feedbacks.Values)
|
||||
{
|
||||
float timeScaleMultiplier = owner.player.selfTimeSm.TimeScale;
|
||||
feedbackUnit.feedback.ExternalTimeScale = timeScaleMultiplier;
|
||||
feedbackUnit.Update();
|
||||
}
|
||||
}
|
||||
|
||||
// 新系统驱动
|
||||
float dt = Time.unscaledDeltaTime;
|
||||
for (int i = _activePlayers.Count - 1; i >= 0; i--)
|
||||
{
|
||||
FeedbackPlayer player = _activePlayers[i];
|
||||
player.Tick(dt);
|
||||
|
||||
if (player.IsCompleted || !player.IsActive)
|
||||
{
|
||||
_activePlayers.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,8 +10,6 @@ namespace Cielonos.MainGame.Characters
|
||||
{
|
||||
public partial class Player : CharacterBase
|
||||
{
|
||||
public ICharacterBuffFactory buffFactory;
|
||||
|
||||
[TitleGroup("Data")]
|
||||
public AttributeData globalAttributeData;
|
||||
[TitleGroup("Data")]
|
||||
|
||||
Reference in New Issue
Block a user