cadence first attempt

This commit is contained in:
Nilton Constantino 2026-06-11 12:14:46 +01:00
parent 56baa88bad
commit 9aead314a7
No known key found for this signature in database
58 changed files with 2104 additions and 602 deletions

View File

@ -118,7 +118,7 @@ ModelImporter:
maskSource: {instanceID: 0}
additiveReferencePoseFrame: 0
- serializedVersion: 16
name: Dash
name: Dash Impulse
takeName: Armature|Main
internalID: 3938472908730161291
firstFrame: 158
@ -147,7 +147,7 @@ ModelImporter:
maskSource: {instanceID: 0}
additiveReferencePoseFrame: 0
- serializedVersion: 16
name: Dash Window
name: Dash Recovery
takeName: Armature|Main
internalID: 9206814923105168154
firstFrame: 159
@ -176,7 +176,7 @@ ModelImporter:
maskSource: {instanceID: 0}
additiveReferencePoseFrame: 0
- serializedVersion: 16
name: Dash Breaking
name: Dash Landing
takeName: Armature|Main
internalID: 44099723154497190
firstFrame: 171
@ -234,7 +234,7 @@ ModelImporter:
maskSource: {instanceID: 0}
additiveReferencePoseFrame: 0
- serializedVersion: 16
name: FallLanding
name: Fall Landing
takeName: Armature|Main
internalID: 7716892539185763080
firstFrame: 165

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 60e9c0806878144959bcc23c977fcdb2
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,24 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 8f4d583fd594418e8ecea0731be75f23, type: 3}
m_Name: Dash - Cadence Profile
m_EditorClassIdentifier: Assembly-CSharp::Intrepid.Gameplay.Entities.Player.Sentinel.Cadence.CadenceProfile
totalTimeMS: 400
strainedEndP: 0.25
alignedEndP: 0.65
resonantEndP: 1
strainedSettlement: 1
alignedSettlement: 0.5
resonantSettlement: 0
lateSettlement: 1
maxCoreCost: 8
resonantRecovery: 5

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: d78cbdd6d5253439f830d91ccedc2132
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,19 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 8cb4003a571646f28d99cec70813327d, type: 3}
m_Name: Core Stability Service
m_EditorClassIdentifier: Assembly-CSharp::Intrepid.Gameplay.Entities.Player.Sentinel.Cadence.CoreStabilityService
maxStability: 100
unstableThresholdP: 0.7
criticalThresholdP: 0.35
collapsedRecoveryStability: 25
naturalRecoveryPerSecond: 10

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 7023b8d166c954e5a8b93090dc373803
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,18 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: c03b6f34733d48c18d0373cc1ca3d338, type: 3}
m_Name: Sentinel Controls
m_EditorClassIdentifier: Assembly-CSharp::Intrepid.Gameplay.Entities.Player.Sentinel.SentinelControls
dashBreakingThreshold: 3
cadenceConnectedThreshold: 3
cadenceConnectedCounter: 0
dashCounter: 0

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 648f106f61b2f40f6b83c7e3d14a1a2f
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,15 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 4042f157f2bd47e3938657e1c2fca5b0, type: 3}
m_Name: Sentinel Input Resolver
m_EditorClassIdentifier: Assembly-CSharp::Intrepid.Gameplay.Entities.Player.Sentinel.Inputs.SentinelInputResolver
inputLifespan: 0.5

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 4115e0e987fe248c998f6106517eb4e1
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@ -14,4 +14,6 @@ MonoBehaviour:
m_EditorClassIdentifier: Intrepid General Utilities::Intrepid.Utilities.SingletonScriptableObjectManifest
singletons:
- {fileID: 11400000, guid: 9ea72f9e3cacb4fc495eff7431ec5551, type: 2}
- {fileID: 11400000, guid: 4115e0e987fe248c998f6106517eb4e1, type: 2}
- {fileID: 11400000, guid: 84f36528c13ab48d6ae2ae41878f2f9f, type: 2}
- {fileID: 11400000, guid: 7023b8d166c954e5a8b93090dc373803, type: 2}

File diff suppressed because it is too large Load Diff

View File

@ -78,7 +78,7 @@ namespace Intrepid.Core.Entities
return StateMachine.ChangeState(state, parameters);
}
private void Update()
public void UpdateStateMachine()
{
CurrentState = StateMachine.GetCurrentState().GetStateKey();
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: cfc7dee444ad4606b32b1b35c4b276a5
timeCreated: 1781160724

View File

@ -0,0 +1,143 @@
using Intrepid.Utilities;
using UnityEngine;
namespace Intrepid.Core.TimeControl
{
internal enum TargetTimeScale
{
NONE,
VERY_SLOW_MOTION,
SLOW_MOTION,
BREATH_MOTION,
NORMAL,
FAST_MOTION,
}
[DefaultExecutionOrder(GonConstants.ExecutionOrder.TimeSimulation)]
public class TimeSimulation : SingletonMonoBehaviour<TimeSimulation>
{
[GroupConfigurations, SerializeField, Range(0f, 1f)] private float verySlowMotionScale = 0.25f;
[GroupConfigurations, SerializeField, Range(0f, 1f)] private float slowMotionScale = 0.5f;
[GroupConfigurations, SerializeField, Range(0f, 1f)] private float breathMotionScale = 0.75f;
[GroupConfigurations, SerializeField, Range(1f, 2f)] private float fastMotionScale = 1.25f;
public float UnscaledElapsedTime { get; private set; }
public float ScaledElapsedTime { get; private set; }
private TargetTimeScale _currentTargetTimeScale = TargetTimeScale.NONE;
private TargetTimeScale _targetTimeScale = TargetTimeScale.NONE;
private float _blendStartedAt;
private float _blendDuration;
private float _blendStartScale;
private float _blendTargetScale;
private void Awake()
{
SingletonInstantiation(false, s =>
{
s.ResetSimulation();
});
}
public void ResetSimulation()
{
Time.timeScale = 1f;
UnscaledElapsedTime = 0f;
ScaledElapsedTime = 0f;
_currentTargetTimeScale = TargetTimeScale.NORMAL;
_targetTimeScale = TargetTimeScale.NONE;
_blendStartedAt = 0f;
_blendDuration = 0f;
_blendStartScale = 1f;
_blendTargetScale = 1f;
}
private void Update()
{
UpdateTimeScaleBlend();
UnscaledElapsedTime += Time.unscaledDeltaTime;
ScaledElapsedTime += Time.deltaTime;
}
private void UpdateTimeScaleBlend()
{
if (_targetTimeScale == TargetTimeScale.NONE)
{
return;
}
if (_blendDuration <= 0f)
{
Time.timeScale = _blendTargetScale;
_currentTargetTimeScale = _targetTimeScale;
_targetTimeScale = TargetTimeScale.NONE;
return;
}
var elapsed = UnscaledElapsedTime - _blendStartedAt;
var t = Mathf.Clamp01(elapsed / _blendDuration);
Time.timeScale = Mathf.Lerp(_blendStartScale, _blendTargetScale, t);
if (t >= 1f)
{
Time.timeScale = _blendTargetScale;
_currentTargetTimeScale = _targetTimeScale;
_targetTimeScale = TargetTimeScale.NONE;
}
}
public void AsBreathMotion(float blendTimeInSeconds = 0f)
{
BlendTo(TargetTimeScale.BREATH_MOTION, breathMotionScale, blendTimeInSeconds);
}
public void AsVerySlowMotion(float blendTimeInSeconds = 0f)
{
BlendTo(TargetTimeScale.VERY_SLOW_MOTION, verySlowMotionScale, blendTimeInSeconds);
}
public void AsSlowMotion(float blendTimeInSeconds = 0f)
{
BlendTo(TargetTimeScale.SLOW_MOTION, slowMotionScale, blendTimeInSeconds);
}
public void AsFastMotion(float blendTimeInSeconds = 0f)
{
BlendTo(TargetTimeScale.FAST_MOTION, fastMotionScale, blendTimeInSeconds);
}
public void AsNormalMotion(float blendTimeInSeconds = 0f)
{
BlendTo(TargetTimeScale.NORMAL, 1f, blendTimeInSeconds);
}
private void BlendTo(
TargetTimeScale target,
float targetScale,
float blendTimeInSeconds)
{
if (_currentTargetTimeScale == target)
{
return;
}
if (blendTimeInSeconds <= 0f)
{
Time.timeScale = targetScale;
return;
}
_targetTimeScale = target;
_blendStartedAt = UnscaledElapsedTime;
_blendDuration = Mathf.Max(0f, blendTimeInSeconds);
_blendStartScale = Time.timeScale;
_blendTargetScale = Mathf.Max(0f, targetScale);
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: c627436787e94833b34c62a63b81e075
timeCreated: 1781160746

View File

@ -0,0 +1,11 @@
namespace Intrepid.Gameplay.Entities.Player.Sentinel.Cadence
{
public enum CadenceBand
{
None,
Strained,
Aligned,
Resonant,
Late
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: ca7e3fbd71414fb58db003ab08548dd5
timeCreated: 1781091177

View File

@ -1,25 +0,0 @@
using System;
using Sirenix.OdinInspector;
using UnityEngine;
namespace Intrepid.Gameplay.Entities.Player.Sentinel.Cadence
{
[Serializable]
public class CadenceContext
{
[SerializeField, Range(0, 5000)] private float warmTimeMS;
[SerializeField, Range(0, 5000)] private float activationTimeMS;
[MinMaxSlider("warmTimeMS", "TotalTimeMS", true), SerializeField] private Vector2 optimalWindowMS;
public float OptimalMinTimeMS => optimalWindowMS.x;
public float OptimalMaxTimeMS => optimalWindowMS.y;
public float TotalTimeMS => warmTimeMS + activationTimeMS;
public float OptimalMinTime => OptimalMinTimeMS * 0.001f;
public float OptimalMaxTime => OptimalMaxTimeMS * 0.001f;
public float WarmTime => warmTimeMS * 0.001f;
public float ActivationTime => activationTimeMS * 0.001f;
public float TotalTime => (warmTimeMS + activationTimeMS) * 0.001f;
public float OptimalTime => (OptimalMinTime + OptimalMaxTime) * 0.5f;
}
}

View File

@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: df1d7ef780ec4251bc0c7101c986eeca
timeCreated: 1781075513

View File

@ -0,0 +1,27 @@
namespace Intrepid.Gameplay.Entities.Player.Sentinel.Cadence
{
public readonly struct CadenceEvaluation
{
public static CadenceEvaluation None = new(0f, 0f, CadenceBand.None, 0f, 0f);
public float T { get; }
public float ElapsedAt { get; }
public CadenceBand Band { get; }
public float SettlementFactor { get; }
public float Recovery { get; }
public CadenceEvaluation(
float t,
float elapsedAt,
CadenceBand band,
float settlementFactor,
float recovery)
{
T = t;
ElapsedAt = elapsedAt;
Band = band;
SettlementFactor = settlementFactor;
Recovery = recovery;
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: aa860d1818f7460698edb395014cb9ae
timeCreated: 1781091290

View File

@ -0,0 +1,132 @@
using Intrepid.Utilities;
using Sirenix.OdinInspector;
using UnityEngine;
namespace Intrepid.Gameplay.Entities.Player.Sentinel.Cadence
{
[CreateAssetMenu(fileName = "Cadence Profile", menuName = "GON/Services/Cadence/Cadence Profile")]
public sealed class CadenceProfile : ScriptableObject
{
[SerializeField, Min(1f)] private float totalTimeMS = 370f;
[GroupConfigurations("Normalized Band End Points"), SerializeField, Range(0f, 1f)] private float strainedEndP = 0.25f;
[GroupConfigurations("Normalized Band End Points"), SerializeField, Range(0f, 1f)] private float alignedEndP = 0.65f;
[GroupConfigurations("Normalized Band End Points"), SerializeField, Range(0f, 1f)] private float resonantEndP = 1f;
[GroupConfigurations("Settlement"), SerializeField, Range(0f, 1f)] private float strainedSettlement = 1f;
[GroupConfigurations("Settlement"), SerializeField, Range(0f, 1f)] private float alignedSettlement = 0.5f;
[GroupConfigurations("Settlement"), SerializeField, Range(0f, 1f)] private float resonantSettlement;
[GroupConfigurations("Settlement"), SerializeField, Range(0f, 1f)] private float lateSettlement = 1f;
[GroupConfigurations("Core Stability"), SerializeField, Range(0, 100)] private float maxCoreCost = 8f;
[GroupConfigurations("Core Stability"), SerializeField, Range(0, 100)] private float resonantRecovery = 8f;
public float TotalTimeMS => totalTimeMS;
public float TotalTime => TotalTimeMS * 0.001f;
public float StrainedEndP => strainedEndP;
public float AlignedEndP => alignedEndP;
public float ResonantEndP => resonantEndP;
// public float StrainedEndTime => TotalTime * strainedEndP;
// public float AlignedEndTime => TotalTime * alignedEndP;
// public float ResonantEndTime => TotalTime * resonantEndP;
//
// public float StrainedEndMS => totalTimeMS * strainedEndP;
// public float AlignedEndMS => totalTimeMS * alignedEndP;
// public float ResonantEndMS => totalTimeMS * resonantEndP;
public float StrainedSettlement => strainedSettlement;
public float AlignedSettlement => alignedSettlement;
public float ResonantSettlement => resonantSettlement;
public float LateSettlement => lateSettlement;
public float MaxCoreCost => maxCoreCost;
public float ResonantRecovery => resonantRecovery;
private void OnValidate()
{
Validate();
}
[GroupDebug, Button(ButtonSizes.Medium)]
private void Validate()
{
totalTimeMS = Mathf.Max(1f, totalTimeMS);
strainedEndP = Mathf.Clamp01(strainedEndP);
alignedEndP = Mathf.Clamp01(alignedEndP);
resonantEndP = Mathf.Clamp01(resonantEndP);
if (alignedEndP < strainedEndP)
{
alignedEndP = strainedEndP;
}
if (resonantEndP < alignedEndP)
{
resonantEndP = alignedEndP;
}
strainedSettlement = Mathf.Clamp01(strainedSettlement);
alignedSettlement = Mathf.Clamp01(alignedSettlement);
resonantSettlement = Mathf.Clamp01(resonantSettlement);
lateSettlement = Mathf.Clamp01(lateSettlement);
maxCoreCost = Mathf.Max(0f, maxCoreCost);
resonantRecovery = Mathf.Max(0f, resonantRecovery);
}
public CadenceEvaluation Evaluate(float elapsedTime)
{
if (TotalTime <= 0f)
{
return CadenceEvaluation.None;
}
var t = Mathf.Clamp01(elapsedTime / TotalTime);
if (t < StrainedEndP)
{
return new CadenceEvaluation(
t,
elapsedTime,
CadenceBand.Strained,
StrainedSettlement,
0f
);
}
if (t < AlignedEndP)
{
return new CadenceEvaluation(
t,
elapsedTime,
CadenceBand.Aligned,
AlignedSettlement,
0f
);
}
if (t < ResonantEndP)
{
return new CadenceEvaluation(
t,
elapsedTime,
CadenceBand.Resonant,
ResonantSettlement,
ResonantRecovery
);
}
return new CadenceEvaluation(
t,
elapsedTime,
CadenceBand.Late,
LateSettlement,
0f
);
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 8f4d583fd594418e8ecea0731be75f23
timeCreated: 1781091714

View File

@ -1,61 +1,95 @@
using System;
using Intrepid.Utilities;
using Sirenix.OdinInspector;
using UnityEngine;
namespace Intrepid.Gameplay.Entities.Player.Sentinel.Cadence
{
[Serializable]
[CreateAssetMenu(fileName = "Cadence Service", menuName = "GON/Services/Cadence Service")]
[CreateAssetMenu(fileName = "Cadence Service", menuName = "GON/Services/Cadence/Cadence Service")]
public class CadenceService : SingletonScriptableObject<CadenceService>
{
[GroupDebug, SerializeField, ReadOnly] private float currentCadence;
private Timer CadenceTimer { get; set; } = new();
private CadenceProfile CurrentProfile { get; set; }
public float CurrentCadence
{
get => currentCadence;
private set => currentCadence = value;
}
public bool IsCadenceRunning => CadenceTimer.IsRunning;
public float CadenceElapsedTime => (float)CadenceTimer.ElapsedTime;
public bool IsCadenceRunning => CadenceTotalTime > 0f && CadenceTimer.IsRunning;
private float CadenceTotalTime => CadenceContext is null ? 0f : 0.001f * CadenceContext.TotalTimeMS;
private CadenceContext CadenceContext { get; set; }
public float WarmCadencePercent => CadenceContext?.WarmTime / CadenceTotalTime ?? 0f;
public float OptimalCadenceMinPercent => CadenceContext?.OptimalMinTime / CadenceTotalTime ?? 0f;
public float OptimalCadenceMaxPercent => CadenceContext?.OptimalMaxTime / CadenceTotalTime ?? 0f;
private float _startedAt;
protected override void WhenInstantiate(CadenceService s)
{
base.WhenInstantiate(s);
StopCadence();
ResetCadence();
}
public void BeginCadence(CadenceProfile profile, float elapsedTime)
{
if (profile is null)
{
ResetCadence();
return;
}
_startedAt = elapsedTime;
CurrentProfile = profile;
CadenceTimer.Start();
}
public void StartCadence(CadenceContext ctx)
public CadenceEvaluation EvaluateAndConsumeAt(float inputTimeAt)
{
if (ctx is null || ctx.TotalTimeMS <= 0f) return;
CadenceTimer.Start();
CadenceContext = ctx;
if (!IsCadenceRunning || CurrentProfile is null)
{
return CadenceEvaluation.None;
}
var elapsedAtInput = Mathf.Max(0f, inputTimeAt - _startedAt);
var evaluation = CurrentProfile.Evaluate(elapsedAtInput);
ResetCadence();
return evaluation;
}
public void StopCadence()
public CadenceEvaluation EvaluateAndConsume()
{
if (!IsCadenceRunning || CurrentProfile is null)
{
return CadenceEvaluation.None;
}
var evaluation = CurrentProfile.Evaluate(CadenceElapsedTime);
ResetCadence();
return evaluation;
}
public bool UpdateCadence(out CadenceEvaluation autoSettlement)
{
autoSettlement = CadenceEvaluation.None;
if (!IsCadenceRunning || CurrentProfile is null)
{
return false;
}
CadenceTimer.Update(Timer.ScaledTime);
if (!CadenceTimer.Check(CurrentProfile.TotalTime))
{
return false;
}
autoSettlement = CurrentProfile.Evaluate(CadenceElapsedTime);
ResetCadence();
return true;
}
public void ResetCadence()
{
CadenceTimer.Stop();
CadenceContext = null;
}
public void UpdateCadence()
{
if (CadenceTimer.IsNotRunning) return;
CadenceTimer.Update(Timer.ScaledTime);
var totalTime = CadenceTotalTime;
CurrentCadence = CadenceElapsedTime / totalTime;
if (CadenceTimer.Check(totalTime))
{
StopCadence();
}
CurrentProfile = null;
}
}
}

View File

@ -0,0 +1,12 @@
using Intrepid.Utilities;
using UnityEngine;
namespace Intrepid.Gameplay.Entities.Player.Sentinel.Cadence
{
public class Cadences : MonoBehaviour
{
[GroupInjections, SerializeField] private CadenceProfile dashProfile;
public CadenceProfile DashProfile => dashProfile;
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: d17424d8ff134fd89c1bee1bfc94054e
timeCreated: 1781105637

View File

@ -0,0 +1,208 @@
using System;
using Intrepid.Utilities;
using UnityEngine;
namespace Intrepid.Gameplay.Entities.Player.Sentinel.Cadence
{
[Serializable]
[CreateAssetMenu(fileName = "Core Stability Service", menuName = "GON/Services/Sentinel/Core Stability Service")]
public class CoreStabilityService : SingletonScriptableObject<CoreStabilityService>
{
[GroupConfigurations, SerializeField, Min(1f)] private float maxStability = 100f;
[GroupConfigurations, SerializeField, Range(0f, 1f)] private float unstableThresholdP = 0.7f;
[GroupConfigurations, SerializeField, Range(0f, 1f)] private float criticalThresholdP = 0.35f;
[GroupConfigurations, SerializeField, Min(0f)] private float collapsedRecoveryStability = 25f;
[GroupConfigurations, SerializeField, Min(0f)] private float naturalRecoveryPerSecond = 3f;
public float MaxStability => maxStability;
public float EffectiveStability { get; private set; }
public float ProjectedStability { get; private set; }
public float EffectiveStabilityP => maxStability <= 0f
? 0f
: EffectiveStability / maxStability;
public float ProjectedStabilityP => maxStability <= 0f
? 0f
: ProjectedStability / maxStability;
private bool _hasProjectedCost;
public CoreStabilityState State
{
get
{
if (EffectiveStability <= 0f)
{
return CoreStabilityState.Collapsed;
}
var p = EffectiveStabilityP;
if (p <= criticalThresholdP)
{
return CoreStabilityState.Critical;
}
if (p <= unstableThresholdP)
{
return CoreStabilityState.Unstable;
}
return CoreStabilityState.Stable;
}
}
public bool IsCollapsed => State == CoreStabilityState.Collapsed;
protected override void WhenInstantiate(CoreStabilityService s)
{
base.WhenInstantiate(s);
ResetCore();
}
public void ResetCore()
{
EffectiveStability = maxStability;
ProjectedStability = maxStability;
_hasProjectedCost = false;
}
public void ProjectCost(float maxCost)
{
maxCost = Mathf.Max(0f, maxCost);
var baseStability = _hasProjectedCost
? Mathf.Min(EffectiveStability, ProjectedStability)
: EffectiveStability;
ProjectedStability = Mathf.Clamp(
baseStability - maxCost,
0f,
maxStability
);
_hasProjectedCost = true;
}
public void Settle(CadenceEvaluation evaluation)
{
var settlementFactor = Mathf.Clamp01(evaluation.SettlementFactor);
var recovery = Mathf.Max(0f, evaluation.Recovery);
EffectiveStability = Mathf.Lerp(
EffectiveStability,
ProjectedStability,
settlementFactor
);
if (recovery > 0f)
{
EffectiveStability = Mathf.Clamp(
EffectiveStability + recovery,
0f,
maxStability
);
}
ProjectedStability = EffectiveStability;
_hasProjectedCost = false;
}
public void ApplyDamage(float damage)
{
damage = Mathf.Max(0f, damage);
EffectiveStability = Mathf.Clamp(
EffectiveStability - damage,
0f,
maxStability
);
ProjectedStability = Mathf.Min(ProjectedStability, EffectiveStability);
}
public void Recover(float amount)
{
amount = Mathf.Max(0f, amount);
EffectiveStability = Mathf.Clamp(
EffectiveStability + amount,
0f,
maxStability
);
if (!_hasProjectedCost)
{
ProjectedStability = EffectiveStability;
}
}
public void RecoverFromCollapse()
{
EffectiveStability = Mathf.Clamp(
collapsedRecoveryStability,
0f,
maxStability
);
ProjectedStability = EffectiveStability;
_hasProjectedCost = false;
}
public void UpdateCore(float deltaTime)
{
if (deltaTime <= 0f)
{
return;
}
if (IsCollapsed)
{
return;
}
RecoverNaturally(deltaTime);
if (!_hasProjectedCost)
{
ProjectedStability = EffectiveStability;
}
}
private void RecoverNaturally(float deltaTime)
{
if (EffectiveStability >= maxStability)
{
return;
}
EffectiveStability = Mathf.Clamp(
EffectiveStability + naturalRecoveryPerSecond * deltaTime,
0f,
maxStability
);
}
private void OnValidate()
{
maxStability = Mathf.Max(1f, maxStability);
unstableThresholdP = Mathf.Clamp01(unstableThresholdP);
criticalThresholdP = Mathf.Clamp01(criticalThresholdP);
if (criticalThresholdP > unstableThresholdP)
{
criticalThresholdP = unstableThresholdP;
}
collapsedRecoveryStability = Mathf.Clamp(
collapsedRecoveryStability,
0f,
maxStability
);
naturalRecoveryPerSecond = Mathf.Max(0f, naturalRecoveryPerSecond);
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 8cb4003a571646f28d99cec70813327d
timeCreated: 1781094387

View File

@ -0,0 +1,10 @@
namespace Intrepid.Gameplay.Entities.Player.Sentinel.Cadence
{
public enum CoreStabilityState
{
Stable,
Unstable,
Critical,
Collapsed,
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 5dfa2f80ab9246ad96f7721fbd2316ac
timeCreated: 1781094456

View File

@ -1,4 +1,6 @@
using Intrepid.Gameplay.Entities.Player.Sentinel.Inputs;
using Intrepid.Utilities;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
@ -6,84 +8,21 @@ namespace Intrepid.Gameplay.Entities.Player.Sentinel.Cadence.UI
{
public class UICadence : MonoBehaviour
{
[GroupInjections, SerializeField] private Slider slider;
[GroupInjections, SerializeField] private RectTransform warmLimit;
[GroupInjections, SerializeField] private RectTransform optimal;
private bool IsActive { get; set; }
[GroupInjections, SerializeField] private SentinelControls controls;
[GroupInjections, SerializeField] private SentinelInputResolver inputResolver;
[GroupInjections, SerializeField] private TextMeshProUGUI cadenceTimer;
[GroupInjections, SerializeField] private TextMeshProUGUI btnElapsedTimes;
[GroupInjections, SerializeField] private Slider effective;
[GroupInjections, SerializeField] private Slider projection;
private void Update()
{
if (CadenceService.Instance.IsCadenceRunning)
{
if (!IsActive)
{
IsActive = true;
slider.gameObject.SetActive(true);
warmLimit.gameObject.SetActive(true);
optimal.gameObject.SetActive(true);
SetHorizontalRange(
optimal,
CadenceService.Instance.OptimalCadenceMinPercent,
CadenceService.Instance.OptimalCadenceMaxPercent
);
SetVerticalLine(
warmLimit,
CadenceService.Instance.WarmCadencePercent
);
}
slider.value = CadenceService.Instance.CurrentCadence;
return;
}
IsActive = false;
slider.value = 0f;
slider.gameObject.SetActive(false);
warmLimit.gameObject.SetActive(false);
optimal.gameObject.SetActive(false);
}
private static void SetHorizontalRange(
RectTransform rect,
float minPercent,
float maxPercent,
float height = 10f
)
{
minPercent = Mathf.Clamp01(minPercent);
maxPercent = Mathf.Clamp01(maxPercent);
if (maxPercent < minPercent)
{
(minPercent, maxPercent) = (maxPercent, minPercent);
}
rect.anchorMin = new Vector2(minPercent, 0.5f);
rect.anchorMax = new Vector2(maxPercent, 0.5f);
rect.pivot = new Vector2(0.5f, 0.5f);
rect.offsetMin = new Vector2(0f, -height * 0.5f);
rect.offsetMax = new Vector2(0f, height * 0.5f);
rect.anchoredPosition = Vector2.zero;
}
private static void SetVerticalLine(RectTransform rect, float percent)
{
percent = Mathf.Clamp01(percent);
rect.anchorMin = new Vector2(percent, 0f);
rect.anchorMax = new Vector2(percent, 1f);
rect.anchoredPosition = Vector2.zero;
rect.offsetMin = new Vector2(rect.offsetMin.x, 0f);
rect.offsetMax = new Vector2(rect.offsetMax.x, 0f);
cadenceTimer.text = CadenceService.Instance.IsCadenceRunning
? CadenceService.Instance.CadenceElapsedTime.ToString("F2")
: "--";
btnElapsedTimes.text = controls.CadenceConnectedCounter.ToString();
effective.value = CoreStabilityService.Instance.EffectiveStabilityP;
projection.value = CoreStabilityService.Instance.ProjectedStabilityP;
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: a1209b3e567948f5ad282f1f2028f91d
timeCreated: 1781108166

View File

@ -0,0 +1,36 @@
using System;
using Intrepid.Integration.Input;
using UnityEngine;
namespace Intrepid.Gameplay.Entities.Player.Sentinel.Inputs
{
public class SentinelInputActions : SentinelInputs
{
public Func<bool> Dash { get; set; }
public Func<Vector2> Movement { get; set; }
public Func<bool> Interact { get; set; }
public Func<bool> DPadLeft { get; set; }
public Func<bool> DPadRight { get; set; }
public Func<bool> Reset { get; set; }
public static SentinelInputActions BuildSentinelInputActions()
{
var locking = InputService.Instance.Locking;
var inputActions = InputService.Instance.InputActions.sentinel;
return new SentinelInputActions
{
Movement = () => locking.IsNotLocked
? inputActions.Movement.ReadValue<Vector2>()
: Vector2.zero,
Interact = () => locking.IsNotLocked && inputActions.Interact.WasPressedThisFrame(),
DPadLeft = () => locking.IsNotLocked && inputActions.DPadLeft.WasPressedThisFrame(),
DPadRight = () => locking.IsNotLocked && inputActions.DPadRight.WasPressedThisFrame(),
Dash = () => locking.IsNotLocked && inputActions.Dash.WasPressedThisFrame(),
// debug
Reset = () => locking.IsNotLocked && inputActions.Reset.WasPressedThisFrame(),
};
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: d2d27d34559344689042676574451a32
timeCreated: 1781114588

View File

@ -0,0 +1,92 @@
using System;
using System.Collections.Generic;
using Intrepid.Utilities;
using UnityEngine;
namespace Intrepid.Gameplay.Entities.Player.Sentinel.Inputs
{
internal enum SentinelBufferedActionType
{
None,
Dash,
}
[Serializable]
[CreateAssetMenu(fileName = "Sentinel Input Resolver", menuName = "GON/Services/Sentinel/Input Resolver")]
public sealed class SentinelInputResolver : SingletonScriptableObject<SentinelInputResolver>, SentinelInputs
{
[GroupConfigurations, SerializeField, Range(0, 5)] private float inputLifespan = .35f;
private Dictionary<SentinelBufferedActionType, float> IntentBuffer { get; set; }
private SentinelInputActions InputActions { get; set; }
protected override void WhenInstantiate(SentinelInputResolver singleton)
{
base.WhenInstantiate(singleton);
IntentBuffer = new Dictionary<SentinelBufferedActionType, float>();
InputActions = SentinelInputActions.BuildSentinelInputActions();
}
public void CaptureAndPrune(float elapsedTime)
{
IntentBuffer.RemoveAll((_, at) => elapsedTime - at > inputLifespan);
if (InputActions.Dash())
{
IntentBuffer.TryAdd(SentinelBufferedActionType.Dash, elapsedTime);
}
}
public void Clear()
{
InnerClear();
}
public void ClearDash()
{
InnerClear(SentinelBufferedActionType.Dash);
}
private void InnerClear(SentinelBufferedActionType bufferedActionType = SentinelBufferedActionType.None)
{
if (bufferedActionType == SentinelBufferedActionType.None)
{
IntentBuffer.Clear();
return;
}
IntentBuffer.Remove(bufferedActionType);
}
private bool TryPeek(SentinelBufferedActionType bufferedActionType, out float actionTypeTimeAt)
{
if (IntentBuffer.TryGetValue(bufferedActionType, out actionTypeTimeAt))
{
return true;
}
actionTypeTimeAt = 0f;
return false;
}
private bool TryConsume(SentinelBufferedActionType bufferedActionType, out float actionTypeTimeAt)
{
if (!TryPeek(bufferedActionType, out actionTypeTimeAt))
{
return false;
}
IntentBuffer.Remove(bufferedActionType);
return true;
}
// Buffered Input Actions
public bool TryPeekDash(out float dashTimeAt) => TryPeek(SentinelBufferedActionType.Dash, out dashTimeAt);
public bool TryConsumeDash(out float dashTimeAt) => TryConsume(SentinelBufferedActionType.Dash, out dashTimeAt);
// No Buffered Input Actions
public Func<Vector2> Movement => InputActions.Movement;
public Func<bool> Interact => InputActions.Interact;
public Func<bool> DPadLeft => InputActions.DPadLeft;
public Func<bool> DPadRight => InputActions.DPadRight;
public Func<bool> Reset => InputActions.Reset;
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 4042f157f2bd47e3938657e1c2fca5b0
timeCreated: 1781111024

View File

@ -0,0 +1,16 @@
using System;
using UnityEngine;
namespace Intrepid.Gameplay.Entities.Player.Sentinel.Inputs
{
public interface SentinelInputs
{
Func<Vector2> Movement { get; }
Func<bool> Interact { get; }
Func<bool> DPadLeft { get; }
Func<bool> DPadRight { get; }
// debug
public Func<bool> Reset { get; }
}
}

View File

@ -1,10 +1,12 @@
using System;
using Intrepid.Core.Entities;
using Intrepid.Core.Entities.Messages;
using Intrepid.Core.TimeControl;
using Intrepid.Diagnostics;
using Intrepid.Gameplay.Entities.Player.ActionBelt;
using Intrepid.Gameplay.Entities.Player.Sentinel.Cadence;
using Intrepid.Gameplay.Entities.Player.Sentinel.Inputs;
using Intrepid.Gameplay.Entities.Player.Sentinel.StateMachine;
using Intrepid.Integration.Input;
using Intrepid.Structures;
using Intrepid.Utilities;
using UnityEngine;
@ -13,20 +15,24 @@ namespace Intrepid.Gameplay.Entities.Player.Sentinel
{
public class SentinelBrain : EntityBrain
{
[GroupInjections, SerializeField] private ActionBeltManager actionBeltManager;
[GroupInjections, SerializeField] private SentinelInputResolver inputResolver;
[GroupInjections, SerializeField] private SentinelControls controls;
[GroupInjections, SerializeField] private ActionBeltManager actionBeltManager;
[GroupInjections, SerializeField] private Cadences cadences;
private bool StateFirstFrame { get; set; }
protected SentinelStateMachine StateMachine { get; private set; }
public SentinelEntity Entity => RootEntity as SentinelEntity;
public SentinelInputs Inputs { get; private set; }
public SentinelStateType CurrentState => StateMachine.CurrentImplementation.GetStateKey();
public ActionBeltManager ActionBeltManager => actionBeltManager;
public SentinelInputResolver InputResolver => inputResolver;
public SentinelControls Controls => controls;
public ActionBeltManager ActionBeltManager => actionBeltManager;
public Cadences Cadences => cadences;
public static bool IsCoreStable => !CoreStabilityService.Instance.IsCollapsed;
protected override void InitializeVariables()
{
Inputs = InputService.Instance.BuildSentinelInputs();
StateMachine = GetComponentInChildren<SentinelStateMachine>();
foreach (var sentinelState in GetComponentsInChildren<SentinelState>())
{
@ -37,6 +43,7 @@ namespace Intrepid.Gameplay.Entities.Player.Sentinel
protected override void OnBootstrap()
{
StateMachine.StartMachine();
ResetBrain();
}
protected override void Subscriptions()
@ -53,23 +60,59 @@ namespace Intrepid.Gameplay.Entities.Player.Sentinel
StateMachine.ChangeState(SentinelStateType.Fall);
});
}
public void ResetBrain()
{
}
public override void UpdateFromUpdatable()
{
CadenceService.Instance.UpdateCadence();
{
StateMachine.UpdateStateMachine();
InputResolver.CaptureAndPrune(TimeSimulation.Instance.ScaledElapsedTime);
// that is the right place?
if (Controls.OnCadence)
{
TimeSimulation.Instance.AsBreathMotion(.5f);
}
else
{
TimeSimulation.Instance.AsNormalMotion(.5f);
}
if (StateFirstFrame)
{
StateMachine.CurrentImplementation.RunOnFirstFrameOnly();
StateFirstFrame = false;
}
StateMachine.CurrentImplementation.RunBeforeUpdate();
StateMachine.CurrentImplementation.RunOnUpdate();
StateMachine.CurrentImplementation.RunAfterUpdate();
if (CadenceService.Instance.UpdateCadence(out var evaluation))
{
// DLogger.LogDebug(
// $"CADENCE EXPIRED: {evaluation.Band} " +
// $"t={evaluation.T * 100f}% " +
// $"elapsedAt={evaluation.ElapsedAt * 1000f}ms " +
// $"set={evaluation.SettlementFactor} " +
// $"rec={evaluation.Recovery}"
// );
Controls.ExpireCadence();
CoreStabilityService.Instance.Settle(evaluation);
}
CoreStabilityService.Instance.UpdateCore(Timer.ScaledTime);
// state machine could take this responsibility in the future...
if (Inputs.Reset())
if (InputResolver.Reset())
{
Entity.ResetLastSafeGroundPosition();
StateMachine.ChangeState(SentinelStateType.FallLanding);
}
}
public override void LateUpdateFromUpdatable()
{
StateMachine.CurrentImplementation.RunOnLateUpdate();
@ -82,7 +125,10 @@ namespace Intrepid.Gameplay.Entities.Player.Sentinel
public void ChangeState(SentinelStateType stateType, params Parameter[] ps)
{
StateMachine.ChangeState(stateType, ps);
if (StateMachine.ChangeState(stateType, ps))
{
StateFirstFrame = true;
}
}
}
}

View File

@ -1,22 +1,31 @@
using Intrepid.Gameplay.Entities.Player.Sentinel.Cadence;
using System;
using Intrepid.Utilities;
using Sirenix.OdinInspector;
using Unity.Collections;
using UnityEngine;
namespace Intrepid.Gameplay.Entities.Player.Sentinel
{
public class SentinelControls : MonoBehaviour
[Serializable]
[CreateAssetMenu(fileName = "Sentinel Controls", menuName = "GON/Services/Sentinel/Sentinel Controls")]
public class SentinelControls : ScriptableObject
{
[GroupConfigurations("Dash Context"), SerializeField, HideLabel] private CadenceContext dashContext;
[GroupConfigurations, SerializeField, Range(0, 5)] private int dashBreakingThreshold = 3;
[GroupConfigurations, SerializeField, Range(0, 10)] private int cadenceConnectedThreshold = 3;
[GroupDebug, SerializeField, ReadOnly] private int cadenceConnectedCounter;
[GroupDebug, SerializeField, ReadOnly] private int dashCounter;
[GroupConfigurations, SerializeField, Range(0, 3)] private int dashBreakingThreshold = 2;
[GroupDebug, SerializeField] private int dashCounter;
public CadenceContext DashContext => dashContext;
public int DashCounter => dashCounter;
public bool IsDashing => dashCounter > 0;
public int CadenceConnectedCounter => cadenceConnectedCounter;
public bool ShouldDashBreak => DashCounter >= dashBreakingThreshold;
public bool OnCadence => CadenceConnectedCounter > cadenceConnectedThreshold;
public bool IsDashing => DashCounter > 0;
public void Reset()
{
ResetDash();
ExpireCadence();
}
public void DashCounterInc()
{
dashCounter++;
@ -26,10 +35,15 @@ namespace Intrepid.Gameplay.Entities.Player.Sentinel
{
dashCounter = 0;
}
public bool DashShouldBreak()
public void CadenceConnectedCounterInc()
{
return DashCounter >= dashBreakingThreshold;
cadenceConnectedCounter++;
}
public void ExpireCadence()
{
cadenceConnectedCounter = 0;
}
}
}

View File

@ -32,6 +32,7 @@ namespace Intrepid.Gameplay.Entities.Player.Sentinel
public override void DeployEntity(Vector2 position)
{
Brain.ResetBrain();
RefreshMovementNormal();
}

View File

@ -1,17 +0,0 @@
using System;
using UnityEngine;
namespace Intrepid.Gameplay.Entities.Player.Sentinel
{
public class SentinelInputs
{
public Func<Vector2> Movement { get; set; }
public Func<bool> Interact { get; set; }
public Func<bool> DPadLeft { get; set; }
public Func<bool> DPadRight { get; set; }
public Func<bool> Dash { get; set; }
// debug
public Func<bool> Reset { get; set; }
}
}

View File

@ -10,9 +10,10 @@ namespace Intrepid.Gameplay.Entities.Player.Sentinel
Run = 30,
Dash = 40,
DashWindow = 41,
DashBreaking = 42,
DashImpulse = 40,
DashRecovery = 41,
DashLanding = 42,
DashBreaking = 43,
Knockdown = 80,

View File

@ -1,6 +1,9 @@
using Intrepid.Core.Entities;
using Intrepid.Core.TimeControl;
using Intrepid.Diagnostics;
using Intrepid.GameManagers;
using Intrepid.Gameplay.Entities.Player.ActionBelt.Events;
using Intrepid.Gameplay.Entities.Player.Sentinel.Cadence;
namespace Intrepid.Gameplay.Entities.Player.Sentinel.StateMachine
{
@ -15,6 +18,10 @@ namespace Intrepid.Gameplay.Entities.Player.Sentinel.StateMachine
{
return this;
}
public virtual void RunOnFirstFrameOnly()
{
}
public abstract void RunOnUpdate();
@ -38,7 +45,7 @@ namespace Intrepid.Gameplay.Entities.Player.Sentinel.StateMachine
{
if (!Brain.ActionBeltManager.IsOperational) return;
if (Brain.ActionBeltManager.IsEmpty) return;
if (Brain.Inputs.Interact())
if (Brain.InputResolver.Interact())
{
Proxy.Instance.EventBus.Raise(new ActionBeltTakeActionEvent
{
@ -47,8 +54,8 @@ namespace Intrepid.Gameplay.Entities.Player.Sentinel.StateMachine
return;
}
var left = Brain.Inputs.DPadLeft();
var right = Brain.Inputs.DPadRight();
var left = Brain.InputResolver.DPadLeft();
var right = Brain.InputResolver.DPadRight();
if (left && right) return;
if (left)
@ -66,5 +73,51 @@ namespace Intrepid.Gameplay.Entities.Player.Sentinel.StateMachine
});
}
}
protected void BeginCadencedActionAt(CadenceProfile profile, float actionTimeAt)
{
LogAndProjectCadence(profile, CadenceService.Instance.EvaluateAndConsumeAt(actionTimeAt));
}
protected void BeginCadencedAction(CadenceProfile profile)
{
LogAndProjectCadence(profile, CadenceService.Instance.EvaluateAndConsume());
}
private void LogAndProjectCadence(CadenceProfile profile, CadenceEvaluation previousEvaluation)
{
if (previousEvaluation.Band != CadenceBand.None)
{
// DLogger.LogDebug(
// $"CADENCE CONNECTED: {previousEvaluation.Band} " +
// $"t={previousEvaluation.T * 100f}% " +
// $"elapsedAt={previousEvaluation.ElapsedAt * 1000f}ms " +
// $"set={previousEvaluation.SettlementFactor} " +
// $"rec={previousEvaluation.Recovery}"
// );
Brain.Controls.CadenceConnectedCounterInc();
CoreStabilityService.Instance.Settle(previousEvaluation);
}
else
{
Brain.Controls.ExpireCadence();
}
CoreStabilityService.Instance.ProjectCost(profile.MaxCoreCost);
CadenceService.Instance.BeginCadence(profile, TimeSimulation.Instance.ScaledElapsedTime);
}
protected bool TryConsumeDash()
{
if (Brain.InputResolver.TryConsumeDash(out var dashTimeAt)) // dash with no ground: this could be a feature to be unlocked
//if (Brain.Entity.IsGrounded && Brain.InputResolver.TryConsumeDash(out var dashTimeAt))
{
BeginCadencedActionAt(Brain.Cadences.DashProfile, dashTimeAt);
return true;
}
return false;
}
}
}

View File

@ -1,4 +1,3 @@
using Intrepid.Gameplay.Entities.Player.Sentinel.Cadence;
using Intrepid.Gameplay.Messages.Events;
using Intrepid.Structures;
using Intrepid.Utilities;
@ -6,19 +5,21 @@ using UnityEngine;
namespace Intrepid.Gameplay.Entities.Player.Sentinel.StateMachine.States
{
public class SentinelStateDash : SentinelState
public class SentinelStateDashImpulse : SentinelState
{
[GroupConfigurations, SerializeField] private AnimationClip dash;
[GroupConfigurations, SerializeField, Range(0, 100)] private float speed = 35;
// [GroupConfigurations, SerializeField, Range(0, 500)] private float targetTimeMS = 100f;
[GroupConfigurations, SerializeField, Range(0, 5000)] private float targetTimeMS = 100f;
public float TargetTime => Brain.Controls.DashContext.WarmTime;
private Timer Timer { get; } = new();
public float TargetTime => targetTimeMS * 0.001f;
private Timer Timer { get; } = new();
public override bool CanEnterState => SentinelBrain.IsCoreStable;
public override SentinelStateType GetStateKey()
{
return SentinelStateType.Dash;
return SentinelStateType.DashImpulse;
}
protected override void Subscriptions()
@ -34,30 +35,32 @@ namespace Intrepid.Gameplay.Entities.Player.Sentinel.StateMachine.States
{
Brain.Controls.DashCounterInc();
Brain.Entity.RefreshMovementNormal();
Brain.Entity.ModelAnimation.CrossFade(dash, 0.01f);
Timer.Start();
CadenceService.Instance.StartCadence(Brain.Controls.DashContext);
Brain.Entity.ModelAnimation.CrossFade(dash, 0.05f);
}
protected override void RunOnExit()
{
Timer.Stop();
}
public override void RunOnFirstFrameOnly()
{
Timer.Start();
}
public override void RunOnUpdate()
{
if (Timer.IsNotRunning)
{
Brain.ChangeState(SentinelStateType.DashWindow);
return;
}
Timer.Update(Time.deltaTime);
if (Timer.Check(TargetTime))
Brain.Entity.DesiredVelocity = Brain.Entity.Forward * speed;
if (!Timer.Check(TargetTime))
{
Timer.Stop();
return;
}
Brain.Entity.DesiredVelocity = Brain.Entity.Forward * speed;
Timer.Stop();
Brain.ChangeState(SentinelStateType.DashRecovery);
}
}
}

View File

@ -0,0 +1,49 @@
using Intrepid.Structures;
using Intrepid.Utilities;
using UnityEngine;
namespace Intrepid.Gameplay.Entities.Player.Sentinel.StateMachine.States
{
public class SentinelStateDashLanding : SentinelState
{
[GroupConfigurations, SerializeField] private AnimationClip dashLanding;
[GroupConfigurations, SerializeField, Range(0, 500)] private float landingTimeMS = 100f;
private Timer Timer { get; } = new();
private float LandingTime => landingTimeMS * 0.001f;
public override SentinelStateType GetStateKey()
{
return SentinelStateType.DashLanding;
}
protected override void Subscriptions()
{
}
protected override void RunOnEnter(ParameterStore parameterStore)
{
Brain.Controls.ResetDash();
Brain.Entity.DesiredVelocity = Vector2.zero;
Brain.Entity.ModelAnimation.CrossFade(dashLanding, .1f);
Timer.Start();
}
protected override void RunOnExit()
{
Timer.Stop();
Brain.InputResolver.Clear();
}
public override void RunOnUpdate()
{
if (Timer.IsNotRunning) return;
Timer.Update(Timer.ScaledTime);
if (Timer.Check(LandingTime))
{
Timer.Stop();
Brain.ChangeState(SentinelStateType.Idle);
}
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: daca982531174c5e82acb0a9312913b1
timeCreated: 1781164678

View File

@ -4,19 +4,20 @@ using UnityEngine;
namespace Intrepid.Gameplay.Entities.Player.Sentinel.StateMachine.States
{
public class SentinelStateDashWindow : SentinelState
public class SentinelStateDashRecovery : SentinelState
{
[GroupConfigurations, SerializeField] private AnimationClip dashWindow;
[GroupConfigurations, SerializeField] private AnimationClip dashRecovery;
[GroupConfigurations, SerializeField, Range(0, 30)] private float speed = 10;
[GroupConfigurations, SerializeField, Range(15, 90)] private float dashMaxAngleFromForward = 60f;
// [GroupConfigurations, SerializeField, Range(0, 500)] private float targetTimeMS = 150f;
[GroupConfigurations, SerializeField, Range(0, 5000)] private float targetTimeMS = 200f;
[GroupConfigurations, SerializeField] private AnimationCurve speedCurve;
public float TargetTime => targetTimeMS * 0.001f;
private Timer Timer { get; } = new();
private float TargetTime => Brain.Controls.DashContext.ActivationTime;
public override SentinelStateType GetStateKey()
{
return SentinelStateType.DashWindow;
return SentinelStateType.DashRecovery;
}
protected override void Subscriptions()
@ -25,30 +26,38 @@ namespace Intrepid.Gameplay.Entities.Player.Sentinel.StateMachine.States
protected override void RunOnEnter(ParameterStore parameterStore)
{
Brain.Entity.ModelAnimation.Play(dashWindow);
Timer.Start();
Brain.Entity.ModelAnimation.CrossFade(dashRecovery, 0.05f);
}
protected override void RunOnExit()
{
Timer.Stop();
var movement = Brain.Inputs.Movement();
var forward = Brain.Entity.Forward;
var direction = GonFunctions
.ConstrainDirectionToForwardCone(movement, forward, dashMaxAngleFromForward);
Brain.Entity.ChangeOrientation(direction);
}
public override void RunOnFirstFrameOnly()
{
Timer.Start();
}
public override void RunOnUpdate()
{
if (CheckDash()) return;
if (Timer.IsNotRunning)
{
if (TryConsumeDash())
{
Brain.ChangeState(SentinelStateType.DashImpulse);
var movement = Brain.InputResolver.Movement();
var forward = Brain.Entity.Forward;
var direction = GonFunctions
.ConstrainDirectionToForwardCone(movement, forward, dashMaxAngleFromForward);
Brain.Entity.ChangeOrientation(direction);
return;
}
if (Brain.Entity.IsGrounded)
{
Brain.ChangeState(Brain.Controls.DashShouldBreak()
Brain.ChangeState(Brain.Controls.ShouldDashBreak
? SentinelStateType.DashBreaking
: SentinelStateType.Idle);
: SentinelStateType.DashLanding);
}
else
{
@ -62,24 +71,9 @@ namespace Intrepid.Gameplay.Entities.Player.Sentinel.StateMachine.States
Timer.Stop();
return;
}
Brain.Entity.DesiredVelocity = Brain.Entity.Forward * speed;
var speedFactor = 1f + speedCurve.Evaluate((float)Timer.ElapsedTime / TargetTime);
Brain.Entity.DesiredVelocity = Brain.Entity.Forward * (speed * speedFactor);
}
private bool CheckDash()
{
// this could be a feature to be unlocked
// if (Brain.Inputs.Dash())
if (Brain.Entity.IsGrounded && Brain.Inputs.Dash())
{
Brain.ChangeState(SentinelStateType.Dash);
return true;
}
return false;
}
}
}

View File

@ -1,5 +1,3 @@
using Intrepid.GameManagers;
using Intrepid.Gameplay.Entities.Player.ActionBelt.Events;
using Intrepid.Structures;
using Intrepid.Utilities;
using UnityEngine;
@ -31,20 +29,14 @@ namespace Intrepid.Gameplay.Entities.Player.Sentinel.StateMachine.States
public override void RunOnUpdate()
{
Movement = Brain.Inputs.Movement();
Movement = Brain.InputResolver.Movement();
VerifyInteraction();
if (CheckDash()) return;
if (CheckMovement()) return;
}
private bool CheckDash()
{
if (Brain.Inputs.Dash())
if (TryConsumeDash())
{
Brain.ChangeState(SentinelStateType.Dash);
return true;
Brain.ChangeState(SentinelStateType.DashImpulse);
return;
}
return false;
if (CheckMovement()) return;
}
private bool CheckMovement()

View File

@ -33,24 +33,18 @@ namespace Intrepid.Gameplay.Entities.Player.Sentinel.StateMachine.States
public override void RunOnUpdate()
{
Movement = Brain.Inputs.Movement();
Movement = Brain.InputResolver.Movement();
Brain.Entity.ChangeOrientation(Movement);
VerifyInteraction();
if (CheckDash()) return;
if (TryConsumeDash())
{
Brain.ChangeState(SentinelStateType.DashImpulse);
return;
}
if (CheckMovement()) return;
Brain.Entity.DesiredVelocity = Movement * speed;
}
private bool CheckDash()
{
if (Brain.Inputs.Dash())
{
Brain.ChangeState(SentinelStateType.Dash);
return true;
}
return false;
}
private bool CheckMovement()
{
switch (Movement.sqrMagnitude)

View File

@ -33,24 +33,18 @@ namespace Intrepid.Gameplay.Entities.Player.Sentinel.StateMachine.States
public override void RunOnUpdate()
{
Movement = Brain.Inputs.Movement();
Movement = Brain.InputResolver.Movement();
Brain.Entity.ChangeOrientation(Movement);
VerifyInteraction();
if (CheckDash()) return;
if (TryConsumeDash())
{
Brain.ChangeState(SentinelStateType.DashImpulse);
return;
}
if (CheckMovement()) return;
Brain.Entity.DesiredVelocity = Movement * speed;
}
private bool CheckDash()
{
if (Brain.Inputs.Dash())
{
Brain.ChangeState(SentinelStateType.Dash);
return true;
}
return false;
}
private bool CheckMovement()
{
switch (Movement.sqrMagnitude)

View File

@ -1,5 +1,5 @@
using Intrepid.Diagnostics;
using Intrepid.Gameplay.Entities.Player.Sentinel;
using Intrepid.Gameplay.Entities.Player.Sentinel.Inputs;
using Intrepid.Utilities;
using UnityEngine;
using UnityEngine.InputSystem;
@ -9,7 +9,7 @@ namespace Intrepid.Integration.Input
[CreateAssetMenu(fileName = "Input Action Service", menuName = "GON/Input Actions Service")]
public class InputService : SingletonScriptableObject<InputService>
{
private InputActions InputActions { get; set; }
public InputActions InputActions { get; private set; }
public InputSchema CurrentInputSchema { get; private set; }
public InputLocking Locking { get; private set; }
@ -54,21 +54,5 @@ namespace Intrepid.Integration.Input
}
}
public SentinelInputs BuildSentinelInputs()
{
return new SentinelInputs
{
Movement = () => Locking.IsNotLocked
? InputActions.sentinel.Movement.ReadValue<Vector2>()
: Vector2.zero,
Interact = () => Locking.IsNotLocked && InputActions.sentinel.Interact.WasPressedThisFrame(),
DPadLeft = () => Locking.IsNotLocked && InputActions.sentinel.DPadLeft.WasPressedThisFrame(),
DPadRight = () => Locking.IsNotLocked && InputActions.sentinel.DPadRight.WasPressedThisFrame(),
Dash = () => Locking.IsNotLocked && InputActions.sentinel.Dash.WasPressedThisFrame(),
// debug
Reset = () => Locking.IsNotLocked && InputActions.sentinel.Reset.WasPressedThisFrame(),
};
}
}
}

View File

@ -9,6 +9,8 @@ namespace Intrepid.Utilities
{
public const int ALAP = 10000; // As Last As Possible
public const int ASAP = -ALAP; // As Soon As Possible
public const int TimeSimulation = ASAP + 1; // Time Simulation updates first
public const int UpdatableManager = TimeSimulation + 1; // Updatables updates later
}
public static class Prototyping

View File

@ -2,7 +2,7 @@ using UnityEngine;
namespace Intrepid.Utilities.Updatable
{
[DefaultExecutionOrder(GonConstants.ExecutionOrder.ASAP)]
[DefaultExecutionOrder(GonConstants.ExecutionOrder.UpdatableManager)]
public class UpdatableManager : MonoBehaviour
{
private void FixedUpdate()

View File

@ -3,7 +3,12 @@
--- !u!5 &1
TimeManager:
m_ObjectHideFlags: 0
Fixed Timestep: 0.02
serializedVersion: 2
Fixed Timestep:
m_Count: 2822399
m_Rate:
m_Denominator: 1
m_Numerator: 141120000
Maximum Allowed Timestep: 0.33333334
m_TimeScale: 1
Maximum Particle Timestep: 0.03