This commit is contained in:
SoulliesOfficial
2025-12-17 04:19:38 -05:00
parent 7c1cb7e8e1
commit d15957c719
4315 changed files with 8260710 additions and 2940 deletions

View File

@@ -13,11 +13,10 @@ using UnityEngine;
namespace Cielonos.MainGame
{
public abstract partial class AttackAreaBase : PooledObject
public abstract partial class AttackAreaBase : MonoBehaviour
{
[Title("References")]
public CharacterBase creator;
public bool isGeneratedByPlayer => creator == MainGameManager.Instance.player;
public ItemBase itemSource;
public List<Fraction> targetFractions;
public Transform topParent;
@@ -39,13 +38,6 @@ namespace Cielonos.MainGame
[HideInEditorMode] public ForceSubmodule forceSm;
[HideInEditorMode] public ReactionSubmodule reactionSm;
#if UNITY_EDITOR
private void Reset()
{
isAutoDespawn = false;
}
#endif
public T Initialize<T>(CharacterBase creator, params Fraction[] targetFractions) where T : AttackAreaBase
{
return Initialize<T>(creator, null, targetFractions);
@@ -278,7 +270,10 @@ namespace Cielonos.MainGame
//float disruptionBreakLevel = attackModule.modifiedAttackValue.baseDisruptionBreakLevel;
//SetReaction(player, disruptionBreakLevel);
target.GetHit(attackSm.modifiedAttackValue.breakthroughType, attackSm.modifiedAttackValue.disruptionType);
BreakthroughType breakthroughType = attackSm.modifiedAttackValue.breakthroughType;
DisruptionType disruptionType = attackSm.modifiedAttackValue.disruptionType;
Vector3 direction = (target.flexibleCenterPoint.position - creator.flexibleCenterPoint.position).normalized;
target.GetHit(breakthroughType, out float recoveryTime, disruptionType, direction);
}
//应用额外力
@@ -316,14 +311,14 @@ namespace Cielonos.MainGame
protected virtual GameObject GenerateHitEffect(CharacterBase target, Collider hitCollider, Vector3 hitPosition)
{
GameObject hitEffect = attackSm.SpawnHitVFX(hitPosition);
GameObject hitEffect = attackSm.SpawnHitVFX(creator, hitPosition);
attackSm.modifyHitEffectAction?.Invoke(hitEffect, target);
return hitEffect;
}
protected virtual GameObject GenerateHitEffect(Vector3 hitPosition)
{
GameObject hitEffect = attackSm.SpawnHitVFX(hitPosition);
GameObject hitEffect = attackSm.SpawnHitVFX(creator, hitPosition);
attackSm.modifyHitEffectAction?.Invoke(hitEffect, null);
return hitEffect;
}

View File

@@ -29,14 +29,14 @@ namespace Cielonos.MainGame
this.damageNumberCriticalPrefab = MainGameManager.BasePrefabs.hudTextCollection["DefaultDamageNumber_Critical"];
}
public GameObject SpawnHitVFX(Vector3 position, Vector3 direction = default)
public GameObject SpawnHitVFX(CharacterBase creator, Vector3 position, Vector3 direction = default)
{
if (isOverridingHitEffect) return null;
if (hitVFXPrefab != null)
{
direction = direction == default ? direction : Vector3.up;
GameObject hitEffect = LeanPool.Spawn(hitVFXPrefab, position, Quaternion.LookRotation(direction));
GameObject hitEffect = VFXObject.Spawn(hitVFXPrefab, creator, position, Quaternion.LookRotation(direction));
return hitEffect;
}

View File

@@ -92,7 +92,7 @@ namespace Cielonos.MainGame
return;
}
currentIntervalTime += Time.deltaTime; //attackArea.creator.selfTimeModule.EntityDeltaTime;
currentIntervalTime += attackArea.creator.selfTimeSm.DeltaTime;
if (currentIntervalTime >= hitInterval)
{
checkedObjects.Clear();

View File

@@ -83,7 +83,7 @@ namespace Cielonos.MainGame
if (delayTime > 0)
{
delayTime -= Time.deltaTime; //attackArea.creator.selfTimeModule.EntityDeltaTime;
delayTime -= attackArea.creator.selfTimeSm.DeltaTime;
return;
}
@@ -93,9 +93,9 @@ namespace Cielonos.MainGame
enableAction?.Invoke();
}
enablingTime += Time.deltaTime; //attackArea.creator.selfTimeModule.EntityDeltaTime;
remainingLifeTime -= Time.deltaTime; //attackArea.creator.selfTimeModule.EntityDeltaTime;
remainingEnableTime -= Time.deltaTime; //attackArea.creator.selfTimeModule.EntityDeltaTime;
enablingTime += attackArea.creator.selfTimeSm.DeltaTime;
remainingLifeTime -= attackArea.creator.selfTimeSm.DeltaTime;
remainingEnableTime -= attackArea.creator.selfTimeSm.DeltaTime;
if (remainingLifeTime <= 0)
{

View File

@@ -43,18 +43,17 @@ namespace Cielonos.MainGame
}
public abstract bool OnBuffApply(out CharacterBuffBase existingBuff);
public override void OnAfterFirstApply()
{
statusSubmodule?.AddStatus();
attachedCharacter.buffSm.buffList.Exclude(this).For(buff => buff.eventSubmodule?.onOtherBuffFirstApplied.Invoke(this));
}
protected float DeltaTime => attachedCharacter.selfTimeSm.DeltaTime;
public override void OnBuffUpdate()
{
timeSubmodule?.Update(DeltaTime);
independentStackSubmodule?.Update(DeltaTime);
timeSubmodule?.Update(attachedCharacter.selfTimeSm.DeltaTime);
independentStackSubmodule?.Update(attachedCharacter.selfTimeSm.DeltaTime);
}
public override void OnBuffRemove()

View File

@@ -0,0 +1,19 @@
using SLSUtilities.FunctionalAnimation;
using UnityEngine;
namespace Cielonos.MainGame.FunctionalAnimation
{
public class SpawnVFX : FuncAnimPayloadBase
{
public string vfxKey = "VFXKey";
public override void Invoke()
{
VFXObject vfxObject = runtimeFuncAnim.executor.vfxData.SpawnVFX(vfxKey).GetComponent<VFXObject>();
if (vfxObject != null)
{
vfxObject.SetCreator(runtimeFuncAnim.executor);
}
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 0127f520fea3f7947afd7ecb0e9b1940

View File

@@ -17,7 +17,6 @@ namespace Cielonos.MainGame.Characters
[TitleGroup("Data & Presets")]
public List<FuncAnimData> fullBodyFuncAnims = new List<FuncAnimData>();
public VFXData vfxData;
public AttackData attackData;
[HideInInspector]
private List<string> registeredFunctionNames = new List<string>();
@@ -35,7 +34,7 @@ namespace Cielonos.MainGame.Characters
protected override void Start()
{
base.Start();
vfxData.Initialize(this);
RegisterFullBodyFuncAnims();
//RegisterFunctionsToAnimSc(LightAttack0);
}
@@ -49,16 +48,18 @@ namespace Cielonos.MainGame.Characters
public partial class Automata
{
public override bool GetHit(BreakthroughType breakthroughType,
public override bool GetHit(BreakthroughType breakthroughType, out float recoveryTime,
DisruptionType disruptionType = DisruptionType.NormalExternal, Vector3 direction = default)
{
if (base.GetHit(breakthroughType, disruptionType, direction))
if (base.GetHit(breakthroughType, out recoveryTime, disruptionType, direction))
{
navMeshAgent.isStopped = true;
statusSm.AddStatus(StatusType.Stun);
getHitRecovery?.Dispose();
getHitRecovery = Observable.Timer(TimeSpan.FromSeconds(1f)).Subscribe(_ =>
getHitRecovery = Observable.Timer(TimeSpan.FromSeconds(recoveryTime)).Subscribe(_ =>
{
navMeshAgent.isStopped = false;
statusSm.RemoveStatus(StatusType.Stun);
}).AddTo(this);
}

View File

@@ -52,7 +52,7 @@ namespace Cielonos.MainGame.Characters
private void ThreeSwings_2() => GenerateSlash("ThreeSwings_2");
private void ClawStabBlast()
{
GenerateBlast("ClawStabBlast", "ClawStabBlast", 6);
GenerateClawStabBlast("ClawStabBlast", "ClawStabBlast", 25);
feedbackSc["ImpaleWave_Stab"].Play();
}
@@ -64,7 +64,7 @@ namespace Cielonos.MainGame.Characters
private void JumpAttackBlast()
{
GenerateBlast("JumpAttackBlast", "JumpAttackBlast", 30);
GenerateJumpAttackBlast("JumpAttackBlast", "JumpAttackBlast", 40);
feedbackSc["JumpAttack_Blast"].Play();
}
@@ -125,7 +125,19 @@ namespace Cielonos.MainGame.Characters
slash.hitSm.AddHitSound("GeneralHit");
}
private void GenerateBlast(string vfxName, string attackDataName, float force)
private void GenerateClawStabBlast(string vfxName, string attackDataName, float force)
{
NormalArea slash = vfxData.SpawnVFX(vfxName).GetComponentInChildren<NormalArea>();
slash.Initialize<NormalArea>(this, null, Fraction.Player)
.SetAttackSubmodule<NormalArea>(attackData[attackDataName])
.SetTimeSubmodule<NormalArea>(1.2f)
.SetHitSubmodule<NormalArea>()
.SetForceSubmodule<NormalArea>(transform.forward * force)
.SetReactionSubmodule<NormalArea>(false, false, false, true, true, false);
}
private void GenerateJumpAttackBlast(string vfxName, string attackDataName, float force)
{
NormalArea slash = vfxData.SpawnVFX(vfxName).GetComponentInChildren<NormalArea>();
slash.Initialize<NormalArea>(this, null, Fraction.Player)
@@ -143,7 +155,7 @@ namespace Cielonos.MainGame.Characters
.SetAttackSubmodule<NormalArea>(attackData["ImpaleWave"])
.SetTimeSubmodule<NormalArea>(3f, 0.2f, 1.3f)
.SetHitSubmodule<NormalArea>()
.SetForceSubmodule<NormalArea>(16f, true)
.SetForceSubmodule<NormalArea>(transform.forward * 30f)
.SetReactionSubmodule<NormalArea>(false, false, false, true, true, false);
slash.transform.DOLocalMoveZ(8f, 0.8f).From(0f).Play();

View File

@@ -25,6 +25,7 @@ namespace Cielonos.MainGame.Characters
[TitleGroup("Data & Presets")]
public AttributeData attributeData;
public VFXData vfxData;
public BaseAnimationGroup baseAnimationGroup;
[TitleGroup("Submodules")]
@@ -50,8 +51,8 @@ namespace Cielonos.MainGame.Characters
protected virtual void Awake()
{
InitializeSubmodules();
InitializeSubcontrollers();
InitializeSubmodules();
}
#if UNITY_EDITOR
@@ -63,6 +64,8 @@ namespace Cielonos.MainGame.Characters
protected virtual void Start()
{
vfxData.Initialize(this);
if (fraction == Fraction.Enemy)
{
BattleManager.EnemySm.activeEnemiesList.Add(this);
@@ -175,16 +178,45 @@ namespace Cielonos.MainGame.Characters
public partial class CharacterBase
{
public virtual bool GetHit(BreakthroughType breakthroughType,
public virtual bool GetHit(BreakthroughType breakthroughType, out float recoveryTime,
DisruptionType disruptionType = DisruptionType.NormalExternal, Vector3 direction = default)
{
renderSc.GetHitBlink();
if (animationSc.SetGetHitDisruption(disruptionType, breakthroughType))
{
animationSc.PlayGetHitMediumAnimation(direction);
switch (breakthroughType)
{
case BreakthroughType.Medium:
animationSc.PlayGetHitMediumAnimation(out recoveryTime, direction);
break;
case BreakthroughType.Heavy:
animationSc.PlayGetHitHeavyAnimation(out recoveryTime,direction);
break;
case BreakthroughType.Disruption:
case BreakthroughType.Forced:
animationSc.PlayGetHitDisruptionAnimation(out recoveryTime,direction);
break;
default:
animationSc.PlayGetHitBoneShake(0.4f, direction);
recoveryTime = 0f;
break;
}
return true;
}
float intensity = breakthroughType switch
{
BreakthroughType.None => 0,
BreakthroughType.Weak => 0.2f,
BreakthroughType.Medium => 0.4f,
BreakthroughType.Heavy or BreakthroughType.Disruption or BreakthroughType.Forced => 0.8f,
_ => 0
};
recoveryTime = 0f;
animationSc.PlayGetHitBoneShake(intensity, direction);
return false;
}
}

View File

@@ -5,6 +5,7 @@ using SLSFramework.General;
using SLSUtilities.FunctionalAnimation;
using UnityEngine;
using UnityEngine.Serialization;
using Random = UnityEngine.Random;
namespace Cielonos.MainGame.Characters
{
@@ -57,6 +58,7 @@ namespace Cielonos.MainGame.Characters
protected virtual void LateUpdate()
{
fullBodyFuncAnimSm?.UpdateEvents();
BoneShakeLateUpdate();
}
}
@@ -79,6 +81,90 @@ namespace Cielonos.MainGame.Characters
}
}
public partial class AnimationSubcontrollerBase
{
private class BoneShakeState
{
public Transform bone;
public Vector3 shakeAxis; // 震动轴
// 物理变量
public float currentAngle = 0f; // 当前偏离角度 (位移 x)
public float velocity = 0f; // 当前震动速度 (速度 v)
}
[Header("Bone Shake Settings")]
// 刚度:越大越硬,回弹越快。对于重型机甲,建议 150-300轻型无人机建议 80-150。
public float stiffness = 200f;
// 阻尼:越大停得越快。建议 10-20。如果太小机器人会像果冻一样晃。
public float damping = 15f;
// 冲击力倍率:将受击力度转化为弹簧的初始速度
public float impactForceMultiplier = 500f;
public List<Transform> testShakeBones; // 用于测试的骨骼列表
private List<BoneShakeState> activeShakes = new List<BoneShakeState>();
private void BoneShakeLateUpdate()
{
float dt = Time.deltaTime;
for (int i = activeShakes.Count - 1; i >= 0; i--)
{
var state = activeShakes[i];
if (state.bone == null) {
activeShakes.RemoveAt(i);
continue;
}
// --- 核心:阻尼弹簧物理公式 (Hooke's Law + Damping) ---
// F = -k * x - d * v
// force = -stiffness * displacement - damping * velocity
float force = -stiffness * state.currentAngle - damping * state.velocity;
// a = F / m (假设质量为1简化计算)
// v += a * dt
state.velocity += force * dt;
// x += v * dt
state.currentAngle += state.velocity * dt;
// --- 应用旋转 ---
// 将计算出的角度应用到轴向上
Quaternion shakeRot = Quaternion.AngleAxis(state.currentAngle, state.shakeAxis);
state.bone.localRotation = state.bone.localRotation * shakeRot;
// --- 移除条件 ---
// 当能量非常小速度和位移都接近0时移除节省性能
if (Mathf.Abs(state.currentAngle) < 0.1f && Mathf.Abs(state.velocity) < 0.1f)
{
activeShakes.RemoveAt(i);
}
}
}
public void ApplyBoneShake(Transform hitBone, Vector3 hitDirection, float intensity)
{
var state = activeShakes.Find(x => x.bone == hitBone);
if (state == null)
{
state = new BoneShakeState();
state.bone = hitBone;
activeShakes.Add(state);
}
// 机械特质:受击瞬间不是直接设置位移,而是给予一个巨大的“初速度” (Impulse)
// 这会让骨骼瞬间弹出去,然后被弹簧拉回来,非常有力量感
state.velocity += intensity * impactForceMultiplier;
// 计算震动轴:依旧是垂直于攻击方向
Vector3 axis = Vector3.Cross(hitDirection, Vector3.up).normalized;
if (axis == Vector3.zero) axis = Vector3.right;
state.shakeAxis = axis;
}
}
public partial class AnimationSubcontrollerBase
{
public virtual void RegisterDefaultFunctions()
@@ -116,47 +202,68 @@ namespace Cielonos.MainGame.Characters
return false;
}
public virtual void PlayGetHitMediumAnimation(Vector3 direction = default)
public virtual void PlayGetHitBoneShake(float intensity, Vector3 direction = default)
{
if (direction == default)
{
direction = owner.transform.right;
}
else
{
direction = Quaternion.Euler(0, 90, 0) * direction;
}
foreach (Transform bone in testShakeBones)
{
ApplyBoneShake(bone, direction, intensity);
}
}
protected virtual void PlayGetHitAnimation(string getHitAnimPrefix, out float animDuration, Vector3 direction = default)
{
int fullBodyActionIndex = animator.GetLayerIndex("FullBodyAction");
if (animator.HasState(fullBodyActionIndex, Animator.StringToHash("GetHitMediumFront")))
string getHitFrontAnim = getHitAnimPrefix + "Front";
string getHitAnim = getHitAnimPrefix + "Front";
float normalizedTransitionDuration = 0.1f / animator.GetCurrentAnimatorStateInfo(fullBodyActionIndex).length;
animDuration = 0f;
if (animator.HasState(fullBodyActionIndex, Animator.StringToHash(getHitAnim)))
{
float normalizedTransitionDuration = 0.1f / animator.GetCurrentAnimatorStateInfo(fullBodyActionIndex).length;
if (direction == default)
{
animator.CrossFade("GetHitMediumFront", normalizedTransitionDuration, fullBodyActionIndex, 0);
return;
}
direction.y = 0;
direction = direction.normalized;
float angle = Vector3.SignedAngle(transform.forward, direction, Vector3.up);
if (angle > -45f && angle <= 45f)
string directionStr = angle switch
{
animator.CrossFade("GetHitMediumBack", normalizedTransitionDuration, fullBodyActionIndex, 0);
}
else if (angle > 45f && angle <= 135f)
> -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("GetHitMediumLeft", normalizedTransitionDuration, fullBodyActionIndex, 0);
}
else if (angle > -135f && angle <= -45f)
{
animator.CrossFade("GetHitMediumRight", normalizedTransitionDuration, fullBodyActionIndex, 0);
animator.CrossFade(getHitFrontAnim, normalizedTransitionDuration, fullBodyActionIndex, 0);
}
else
{
animator.CrossFade("GetHitMediumFront", normalizedTransitionDuration, fullBodyActionIndex, 0);
animator.CrossFade(getHitAnim, normalizedTransitionDuration, fullBodyActionIndex, 0);
}
animDuration = animator.GetCurrentAnimatorStateInfo(fullBodyActionIndex).length;
}
else if (animator.HasState(fullBodyActionIndex, Animator.StringToHash(getHitAnimPrefix)))
{
getHitAnim = getHitAnimPrefix;
animator.CrossFade(getHitAnim, normalizedTransitionDuration, fullBodyActionIndex, 0);
animDuration = animator.GetCurrentAnimatorStateInfo(fullBodyActionIndex).length;
}
else
{
if (animator.HasState(fullBodyActionIndex, Animator.StringToHash("GetHit")))
{
animator.CrossFade("GetHit", 0.1f, fullBodyActionIndex, 0);
animator.CrossFade("GetHit", normalizedTransitionDuration, fullBodyActionIndex, 0);
animDuration = animator.GetCurrentAnimatorStateInfo(fullBodyActionIndex).length;
}
else
{
@@ -164,5 +271,20 @@ namespace Cielonos.MainGame.Characters
}
}
}
public virtual void PlayGetHitMediumAnimation(out float animDuration, Vector3 direction = default)
{
PlayGetHitAnimation("GetHitMedium", out animDuration, direction);
}
public virtual void PlayGetHitHeavyAnimation(out float animDuration, Vector3 direction = default)
{
PlayGetHitAnimation("GetHitHeavy", out animDuration, direction);
}
public virtual void PlayGetHitDisruptionAnimation(out float animDuration, Vector3 direction = default)
{
PlayGetHitAnimation("GetHitDisruption", out animDuration, direction);
}
}
}

View File

@@ -1,13 +1,15 @@
using System.Collections.Generic;
using Sirenix.OdinInspector;
using UnityEngine;
using UnityEngine.Serialization;
namespace Cielonos.MainGame.Characters
{
public class BodyPartsSubcontroller : SubcontrollerBase<CharacterBase>
{
[Title("Main Parts")]
public Transform centerPoint;
[FormerlySerializedAs("centerPoint")] [Title("Main Parts")]
public Transform flexibleCenterPoint;
public Transform staticCenterPoint;
public Transform footPoint;
public Transform head;
public Transform leftHand;

View File

@@ -48,6 +48,32 @@ namespace Cielonos.MainGame.Characters
{
characterTransform.DOLookAt(target.transform.position, duration, AxisConstraint.Y);
}
public void TurnToDirection(Vector3 direction, float duration = 0f)
{
Vector3 dashRotation = Vector3.zero;
float angle = Vector3.SignedAngle(Vector3.forward, direction, Vector3.up);
if (owner is Player player)
{
dashRotation.y = player.viewSc.isLockedOn
? player.viewSc.cameraRotationSm.cinemachineEndLockYaw + angle
: player.viewSc.cameraRotationSm.cinemachineTargetYaw + angle;
}
else
{
dashRotation = new Vector3(0, angle, 0);
}
if (duration > 0)
{
characterTransform.DORotateQuaternion(Quaternion.Euler(dashRotation), duration);
}
else
{
characterTransform.rotation = Quaternion.Euler(dashRotation);
}
}
}
public partial class MovementSubcontrollerBase

View File

@@ -133,8 +133,27 @@ namespace Cielonos.MainGame.Characters
{
getHitBlinkTween?.Kill(true);
getHitBlinkTween = DOTween.Sequence();
getHitBlinkTween.OnPlay(() =>
foreach (Material mat in baseRenderMaterials)
{
Tweener rimTween = mat.DOVector(new Vector4(1, 1, 4, 1), "_RimParams", 0.5f)
.From(new Vector4(0, 1, 4, 1))
.OnPlay(() =>
{
mat.EnableKeyword("_RIM");
})
.SetEase(Ease.OutQuad)
.OnComplete(() =>
{
mat.DisableKeyword("_RIM");
});
getHitBlinkTween.Join(rimTween);
}
getHitBlinkTween.Play();
/*getHitBlinkTween.OnPlay(() =>
{
effectContainers["GetHitBlink"].SetActive(true);
});
@@ -157,7 +176,7 @@ namespace Cielonos.MainGame.Characters
getHitBlinkTween.OnComplete(() =>
{
effectContainers["GetHitBlink"].SetActive(false);
});
});*/
getHitBlinkTween.Play();
}

View File

@@ -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)

View File

@@ -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)
{

View File

@@ -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~1Y轴通常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;
}
}
}

View File

@@ -12,6 +12,8 @@ namespace Cielonos.MainGame.Characters
Disarm = 2, //缴械,无法攻击(使用主武器)
Restraint = 3, //束缚,无法移动
Stun = 100, //眩晕
//正面状态
Invincible = 1000, //无敌
Invisible = 1001, //隐身

View File

@@ -16,9 +16,9 @@ namespace Cielonos.MainGame.Characters
public AnimationClip jumpStart;
[FormerlySerializedAs("jumpInAir")] public AnimationClip inAir;
public AnimationClip jumpLand;
public AnimationClip getHitLightFront, getHitLightBack, getHitLightLeft, getHitLightRight;
public AnimationClip getHitMediumFront, getHitMediumBack, getHitMediumLeft, getHitMediumRight;
public AnimationClip getHitHeavyFront, getHitHeavyBack, getHitHeavyLeft, getHitHeavyRight;
public AnimationClip getHitKnockedFront, getHitKnockedBack, getHitKnockedLeft, getHitKnockedRight;
public AnimationClip getHitDisruptionFront, getHitDisruptionBack, getHitDisruptionLeft, getHitDisruptionRight;
public AnimationClip riseUpFront, riseUpBack, riseUpLeft, riseUpRight;
public AnimationClip incapacitation, death;
@@ -43,6 +43,19 @@ namespace Cielonos.MainGame.Characters
animatorOverride["JumpStart"] = jumpStart;
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;
}
animSc.fullBodyFuncAnimSm.ReSet(dash);

View File

@@ -100,6 +100,7 @@ namespace Cielonos.MainGame.Characters
if (IsMoving)
{
operation.Dash();
player.landMovementSc.TurnToDirection(new Vector3(Move.x, 0, Move.y));
preinputSubmodule.RegisterPreinputAction(() => operation.Dash(), 10);
}
else

View File

@@ -1,3 +1,4 @@
using Sirenix.OdinInspector;
using SLSFramework.General;
using UnityEngine;
@@ -11,15 +12,22 @@ namespace Cielonos.MainGame.Characters
private const float RotateThreshold = 0.01f;
[Title("Cinemachine Settings")]
public float cinemachineTargetYaw;
public float cinemachineEndLockYaw;
public float cinemachineTargetPitch;
public float topClamp = 70.0f;
public float bottomClamp = -30.0f;
public float cameraAngleOverride = 0.0f;
public bool lockCameraPosition = false;
[Title("Combat Recenter Settings")]
public float recenterSmoothTime = 0.1f; // 平滑时间,越小转得越快
private float recenterVelocity; // SmoothDamp使用的速度变量
private float targetRecenterYaw; // 目标角度
private bool isRecentering = false; // 是否正在校准
private float recenterTimer = 0.0f; // 校准剩余时间
public CameraRotationSubmodule(PlayerViewSubcontroller owner, float initialYaw) : base(owner)
{
this.cinemachineTargetYaw = initialYaw;
@@ -29,6 +37,7 @@ namespace Cielonos.MainGame.Characters
{
if (inputSc.Look.sqrMagnitude >= RotateThreshold && !lockCameraPosition)
{
isRecentering = false;
float deltaTimeMultiplier = inputSc.CurrentScheme == "KeyboardMouse" ? 1.0f : Time.deltaTime;
//if (!viewSc.lockTargetModule.isLockedMelee)
{
@@ -36,13 +45,26 @@ namespace Cielonos.MainGame.Characters
cinemachineTargetPitch += inputSc.Look.y * deltaTimeMultiplier;
}
}
else if (isRecentering)
{
recenterTimer -= Time.deltaTime;
if (recenterTimer <= 0)
{
isRecentering = false;
}
else
{
cinemachineTargetYaw = Mathf.SmoothDampAngle(cinemachineTargetYaw, targetRecenterYaw, ref recenterVelocity, recenterSmoothTime);
}
}
cinemachineTargetYaw = MathExtensions.ClampAngle(cinemachineTargetYaw, float.MinValue, float.MaxValue);
cinemachineTargetPitch = MathExtensions.ClampAngle(cinemachineTargetPitch, bottomClamp, topClamp);
viewSc.cameraTarget.rotation = Quaternion.Euler(
viewSc.cameraRoot.rotation = Quaternion.Euler(
cinemachineTargetPitch + cameraAngleOverride /*- viewSc.muzzleLiftModule.currentMuzzlePositionY*/, cinemachineTargetYaw, 0.0f);
/*
/*
if (player.motionController.characterRotationType == PlayerMotionController.CharacterRotationType.ByAiming)
{
player.transform.rotation = Quaternion.Euler(0.0f, cinemachineTargetYaw, 0.0f);
@@ -60,7 +82,43 @@ namespace Cielonos.MainGame.Characters
cinemachineEndLockYaw = viewSc.lockingCamera.transform.eulerAngles.y;
}
}
*/
*/
}
/// <summary>
/// [新增] 触发相机战斗校准
/// </summary>
/// <param name="attackDirection">攻击的方向向量 (通常是 player.transform.forward)</param>
/// <param name="duration">校准持续时间 (建议 0.2f ~ 0.5f)</param>
public void TriggerCameraRecenter(Vector3 attackDirection, float duration = 0.2f)
{
if (attackDirection.sqrMagnitude < 0.01f) return;
// 计算目标角度
Quaternion targetRot = Quaternion.LookRotation(attackDirection);
targetRecenterYaw = targetRot.eulerAngles.y;
// 激活状态
recenterTimer = duration;
isRecentering = true;
}
// 将此方法添加到你控制相机旋转的脚本中 (例如 PlayerCameraController)
public void SyncRotationWithCamera(Camera camera = null)
{
camera ??= viewSc.playerCamera;
Vector3 currentEuler = camera.transform.eulerAngles;
cinemachineTargetYaw = currentEuler.y;
float currentPitch = currentEuler.x;
if (currentPitch > 180)
{
currentPitch -= 360;
}
cinemachineTargetPitch = currentPitch - cameraAngleOverride;
isRecentering = false;
// 强制执行一次旋转更新,防止下一帧才生效导致的微小跳动 (可选)
// viewSc.cameraRoot.rotation = Quaternion.Euler(
// cinemachineTargetPitch + cameraAngleOverride, cinemachineTargetYaw, 0.0f);
}
}
}

View File

@@ -0,0 +1,16 @@
using UnityEngine;
public class LockTargetCamera : MonoBehaviour
{
// Start is called once before the first execution of Update after the MonoBehaviour is created
void Start()
{
}
// Update is called once per frame
void Update()
{
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: cbc378284638d9549945b8c5090f5a1b

View File

@@ -67,7 +67,7 @@ namespace Cielonos.MainGame.Characters
obstructionLayer = LayerMask.GetMask("FadableEnvironment");
targetCamera = owner.playerCamera;
playerHead = owner.player.bodyPartsSc.head;
playerCenter = owner.player.bodyPartsSc.centerPoint;
playerCenter = owner.player.bodyPartsSc.flexibleCenterPoint;
playerFeet = owner.player.bodyPartsSc.footPoint;
}

View File

@@ -1,7 +1,9 @@
using System;
using DG.Tweening;
using SLSFramework.General;
using Unity.Cinemachine;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.Serialization;
namespace Cielonos.MainGame.Characters
@@ -11,28 +13,102 @@ namespace Cielonos.MainGame.Characters
public Player player => owner;
public Camera playerCamera;
public Transform cameraTarget;
public Transform cameraRoot;
public CinemachineStateDrivenCamera stateDrivenCamera;
public CinemachineCamera freeLookCamera;
public CinemachineCamera lockOnCamera;
public bool isLockedOn = false;
public bool isLockedSetRoot;
public CharacterBase testEnemy;
public Transform testEnemyTarget;
public CameraRotationSubmodule cameraRotationSm;
public OcclusionFadeSubmodule occlusionFadeSm;
public LerpFloat cameraDistance;
public override void Initialize()
{
base.Initialize();
cameraRotationSm = new CameraRotationSubmodule(this, player.transform.eulerAngles.y);
occlusionFadeSm = new OcclusionFadeSubmodule(this);
cameraDistance = new LerpFloat(10f, 1f);
}
private void Start()
{
testEnemyTarget = testEnemy.bodyPartsSc.staticCenterPoint;
//SwitchToLockTarget( testEnemyTarget );
}
private void Update()
{
if (Keyboard.current.tabKey.wasPressedThisFrame)
{
if (!isLockedOn)
{
SwitchToLockTarget( testEnemyTarget );
}
else
{
SwitchToFreeLook();
}
}
cameraDistance.Update(player.selfTimeSm.DeltaTime);
freeLookCamera.GetComponent<CinemachineThirdPersonFollow>().CameraDistance = cameraDistance.currentValue;
lockOnCamera.GetComponent<CinemachinePositionComposer>().CameraDistance = cameraDistance.currentValue;
}
private void LateUpdate()
{
cameraRotationSm.Update();
if (!isLockedOn)
{
cameraRotationSm.Update();
}
else
{
if(isLockedSetRoot) cameraRoot.LookAt(testEnemyTarget);
float distance = (testEnemyTarget.position - cameraRoot.transform.position).Flatten().magnitude;
if(distance < 1f) SwitchToFreeLook();
}
occlusionFadeSm.Update();
}
void SwitchToLockTarget(Transform target)
{
testEnemyTarget = target;
isLockedOn = true;
isLockedSetRoot = false;
// --- CM3 核心操作 ---
// 1. 设置 LookAt 目标。在 CM3 中LookingAt 是 Target 结构体的一部分,或者直接赋值给 LookAt 属性
lockOnCamera.Target.LookAtTarget = testEnemyTarget;
// 2. 提高优先级,激活锁定相机
stateDrivenCamera.GetComponent<Animator>().SetBool("isLockTarget", true);
cameraRoot.DOLookAt(testEnemyTarget.position, 0.5f).SetEase(Ease.InOutSine).OnComplete(()=>isLockedSetRoot = true).Play();
// (可选) 你可以在这里播放锁定音效或显示 UI 准星
Debug.Log($"Locked on: {target.name}");
}
void SwitchToFreeLook()
{
cameraRotationSm.SyncRotationWithCamera(playerCamera);
isLockedOn = false;
// --- CM3 核心操作 ---
// 1. 重置 LookAt 目标
//lockOnCamera.Target.LookAtTarget = null;
// 2. 降低优先级,切换回自由视角相机
stateDrivenCamera.GetComponent<Animator>().SetBool("isLockTarget", false);
// (可选) 你可以在这里播放解锁音效或隐藏 UI 准星
Debug.Log("Switched to Free Look");
}
}
}

View File

@@ -22,12 +22,12 @@ namespace Cielonos.MainGame
public partial class VFXData
{
//Runtime
[NonSerialized]
private Transform executorTransform;
[NonSerialized] private CharacterBase executor;
[NonSerialized] private Transform executorTransform;
public void Initialize(CharacterBase character)
{
executor = character;
executorTransform = character.transform;
}
@@ -36,7 +36,7 @@ namespace Cielonos.MainGame
VFXUnit vfxUnit = Get(vfxName);
Transform startTransform = overrideStartTransform ?? executorTransform;
GameObject vfxInstance = LeanPool.Spawn(vfxUnit.mainVFX, startTransform);
GameObject vfxInstance = VFXObject.Spawn(vfxUnit.mainVFX, executor, startTransform);
Transform vfxTransform = vfxInstance.transform;
vfxTransform.localPosition = vfxUnit.localPosition;
@@ -53,12 +53,12 @@ namespace Cielonos.MainGame
public GameObject SpawnMuzzleVFX(string effectName, Transform muzzleTransform)
{
return LeanPool.Spawn(Get(effectName).muzzleVFX, muzzleTransform);
return VFXObject.Spawn(Get(effectName).muzzleVFX, executor, muzzleTransform);
}
public GameObject SpawnHitVFX(string effectName, Vector3 hitPosition)
{
return LeanPool.Spawn(Get(effectName).muzzleVFX, hitPosition, Quaternion.identity);
return VFXObject.Spawn(Get(effectName).muzzleVFX, executor, hitPosition, Quaternion.identity);
}
}

View File

@@ -0,0 +1,87 @@
using System;
using System.Collections.Generic;
using Cielonos.MainGame.Characters;
using Lean.Pool;
using Sirenix.OdinInspector;
using SLSFramework.LeanPoolAssistance;
using UnityEngine;
namespace Cielonos.MainGame
{
public class VFXObject : PooledObject
{
[NonSerialized]
public CharacterBase creator;
public List<ParticleSystem> particles = new List<ParticleSystem>();
[NonSerialized]
public List<ParticleSystem.MainModule> particleMainModules = new List<ParticleSystem.MainModule>();
public static GameObject Spawn(GameObject vfxPrefab, CharacterBase creator, Transform parent = null)
{
VFXObject vfxObject = LeanPool.Spawn(vfxPrefab, parent).GetComponent<VFXObject>();
vfxObject.SetCreator(creator);
return vfxObject.gameObject;
}
public static GameObject Spawn(GameObject vfxPrefab, CharacterBase creator, Vector3 position, Quaternion rotation, Transform parent = null)
{
VFXObject vfxObject = LeanPool.Spawn(vfxPrefab, position, rotation, parent).GetComponent<VFXObject>();
vfxObject.SetCreator(creator);
return vfxObject.gameObject;
}
private void Reset()
{
CollectParticles();
}
public override void OnSpawn()
{
if (spawnExecuted)
{
return;
}
base.OnSpawn();
particleMainModules = new List<ParticleSystem.MainModule>();
foreach (var ps in particles)
{
particleMainModules.Add(ps.main);
}
}
[Button("Collect Particles")]
private void CollectParticles()
{
particles.Clear();
ParticleSystem[] foundParticles = GetComponentsInChildren<ParticleSystem>();
foreach (var ps in foundParticles)
{
particles.Add(ps);
}
}
protected override void Update()
{
float deltaTime = Time.deltaTime;
float timeScale = 1f;
if (creator != null)
{
deltaTime = creator.selfTimeSm.DeltaTime;
timeScale = creator.selfTimeSm.timeScaleCoefficient.Value;
}
UpdateTimer(deltaTime);
particleMainModules.ForEach(main => main.simulationSpeed = timeScale);
}
public void SetCreator(CharacterBase character)
{
creator = character;
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 5dc3f30af6da99e4aa3cece3b17619f8

View File

@@ -1,6 +1,7 @@
using Cielonos.MainGame.Characters;
using Cielonos.MainGame.Characters.Buffs;
using SLSUtilities.FunctionalAnimation;
using Unity.Cinemachine;
using UnityEngine;
namespace Cielonos.MainGame.Inventory
@@ -31,6 +32,7 @@ namespace Cielonos.MainGame.Inventory
functionSm["TripleAttack"].Execute();
CharacterBase target = BattleManager.EnemySm.GetNearestEnemy(5);
PlayTargetedAnimation("TripleAttack", target, 1f);
//player.viewSc.cameraRotationSm.TriggerCameraRecenter(player.transform.forward);
return;
}
@@ -40,6 +42,7 @@ namespace Cielonos.MainGame.Inventory
functionSm["LightAttack"].Execute();
CharacterBase target = BattleManager.EnemySm.GetNearestEnemy(8);
PlayTargetedAnimation("RunAttack", target, 1f);
//player.viewSc.cameraRotationSm.TriggerCameraRecenter(player.transform.forward);
return;
}
@@ -49,6 +52,7 @@ namespace Cielonos.MainGame.Inventory
functionSm["LightAttack"].Execute();
CharacterBase target = BattleManager.EnemySm.GetNearestEnemy(5);
PlayTargetedAnimation("LightAttack" + comboSm.GetCurrentNodeName(), target, 1f);
//player.viewSc.cameraRotationSm.TriggerCameraRecenter(player.transform.forward);
}
}
@@ -61,6 +65,7 @@ namespace Cielonos.MainGame.Inventory
functionSm["HeavyAttack"].Execute();
CharacterBase target = BattleManager.EnemySm.GetNearestEnemy(5);
PlayTargetedAnimation("ParryAttack", target, 1f);
//player.viewSc.cameraRotationSm.TriggerCameraRecenter(player.transform.forward);
return;
}
@@ -71,6 +76,7 @@ namespace Cielonos.MainGame.Inventory
functionSm["DisruptAttack"].Execute();
CharacterBase target = BattleManager.EnemySm.GetNearestEnemy(5);
PlayTargetedAnimation("DisruptAttack", target, 1f);
//player.viewSc.cameraRotationSm.TriggerCameraRecenter(player.transform.forward);
return;
}
@@ -80,6 +86,7 @@ namespace Cielonos.MainGame.Inventory
functionSm["HeavyAttack"].Execute();
CharacterBase target = BattleManager.EnemySm.GetNearestEnemy(5);
PlayTargetedAnimation("HeavyAttack", target, 1f);
//player.viewSc.cameraRotationSm.TriggerCameraRecenter(player.transform.forward);
}
}
@@ -141,6 +148,8 @@ namespace Cielonos.MainGame.Inventory
.AddHitEvent((enemy, hitPosition) =>
{
feedbackSc["NormalHit"].Play();
player.selfTimeSm.ModifyTimeScale(0.06f);
enemy.selfTimeSm.ModifyTimeScale(0.06f);
new ElectronicDisturbance(2).Apply(enemy);
});
@@ -164,6 +173,8 @@ namespace Cielonos.MainGame.Inventory
.AddHitEvent((enemy, hitPosition) =>
{
feedbackSc["NormalHit"].Play();
player.selfTimeSm.ModifyTimeScale(0.06f);
enemy.selfTimeSm.ModifyTimeScale(0.06f);
new ElectronicDisturbance(2).Apply(enemy);
if (enemy.statusSm.HasStatus(StatusType.Incapacitation))
@@ -183,7 +194,7 @@ namespace Cielonos.MainGame.Inventory
slash.Initialize<NormalArea>(player, this, Fraction.Enemy)
.SetAttackSubmodule<NormalArea>(attackData["HeavyAttack"])
.SetTimeSubmodule<NormalArea>(1f, 0.04f)
.SetTimeSubmodule<NormalArea>(1f, 0.04f, 0.16f)
.SetHitSubmodule<NormalArea>()
.SetForceSubmodule<NormalArea>(3f, true);
@@ -192,6 +203,9 @@ namespace Cielonos.MainGame.Inventory
.AddHitEvent((enemy, hitPosition) =>
{
feedbackSc["HeavyHit"].Play();
player.selfTimeSm.ModifyTimeScale(0.12f);
enemy.selfTimeSm.ModifyTimeScale(0.12f);
player.viewSc.cameraDistance.currentValue -= 2f;
new ElectronicDisturbance(5).Apply(enemy);
});
@@ -215,6 +229,8 @@ namespace Cielonos.MainGame.Inventory
.AddHitEvent((enemy, hitPosition) =>
{
feedbackSc["HeavyHit"].Play();
player.selfTimeSm.ModifyTimeScale(0.12f);
enemy.selfTimeSm.ModifyTimeScale(0.12f);
new ElectronicDisturbance(5).Apply(enemy);
});
@@ -238,6 +254,8 @@ namespace Cielonos.MainGame.Inventory
.AddHitEvent((enemy, hitPosition) =>
{
feedbackSc["HeavyHit"].Play();
player.selfTimeSm.ModifyTimeScale(0.12f);
enemy.selfTimeSm.ModifyTimeScale(0.12f);
new ElectronicDisturbance(5).Apply(enemy);
});
@@ -262,6 +280,8 @@ namespace Cielonos.MainGame.Inventory
.AddHitEvent((enemy, hitPosition) =>
{
feedbackSc["NormalHit"].Play();
player.selfTimeSm.ModifyTimeScale(0.06f);
enemy.selfTimeSm.ModifyTimeScale(0.06f);
new ElectronicDisturbance(2).Apply(enemy);
});

View File

@@ -36,10 +36,7 @@ namespace Cielonos.MainGame.Inventory
public void Setup(float disappearTime = -1)
{
disappearTime = disappearTime <= 0 ? defaultDisappearTime : disappearTime;
comboTimer = Observable.Timer(TimeSpan.FromSeconds(disappearTime))
.First()
.Subscribe(_ => { comboTree.Reset(); });
comboTimer = owner.player.selfTimeSm.AddLocalTimer(disappearTime, () => comboTree.Reset());
}
/// <summary>
@@ -49,9 +46,7 @@ namespace Cielonos.MainGame.Inventory
{
comboTimer?.Dispose();
suspender?.Dispose();
suspender = Observable.Timer(TimeSpan.FromSeconds(duration))
.First()
.Subscribe(_ => Setup());
suspender = owner.player.selfTimeSm.AddLocalTimer(duration, () => Setup());
}
/// <summary>

View File

@@ -1,3 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Lean.Pool;
@@ -13,12 +14,14 @@ namespace SLSFramework.LeanPoolAssistance
[ShowIf("isAutoDespawn")][Tooltip("自动回收时间")]
public float autoDespawnTime = 1;
[ShowIf("isAutoDespawn")][Tooltip("自动回收计时器")][ReadOnly][HideInEditorMode]
public float despawnTimer;
private List<IPoolable> children;
private bool spawnExecuted = false;
protected bool spawnExecuted = false;
public void OnSpawn()
public virtual void OnSpawn()
{
if (spawnExecuted)
{
@@ -26,14 +29,30 @@ namespace SLSFramework.LeanPoolAssistance
}
spawnExecuted = true;
despawnTimer = 0;
children = GetComponentsInChildren<IPoolable>().ToList();
children.Remove(this);
children.ForEach(child => child.OnSpawn());
if (isAutoDespawn)
}
protected virtual void Update()
{
UpdateTimer(Time.deltaTime);
}
protected virtual void UpdateTimer(float deltaTime)
{
if (!isAutoDespawn)
{
LeanPool.Despawn(gameObject, autoDespawnTime);
return;
}
despawnTimer += deltaTime;
if (despawnTimer >= autoDespawnTime)
{
LeanPool.Despawn(gameObject);
}
}

View File

@@ -145,6 +145,8 @@ namespace SLSUtilities.FunctionalAnimation
ActionDisruption = 30,
[InspectorName("移动打断窗口 (Movement Disruption)")]
MovementDisruption = 31,
[InspectorName("强制动作打断窗口 (Forced Action Disruption) 如果没有此区间,默认整个动作都可被强制打断")]
ForcedActionDisruption = 32,
[InspectorName("Root Motion 作用区间,角色会使用动画中的位移")]
RootMotion = 40,
[InspectorName("自定义 (Custom)")]