Added Falling and Falling To Abyss

This commit is contained in:
Nilton Constantino 2026-06-08 11:41:02 +01:00
parent aceb4677aa
commit e936db5567
No known key found for this signature in database
37 changed files with 9284 additions and 925 deletions

View File

@ -0,0 +1,38 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!21 &2100000
Material:
serializedVersion: 8
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: GroundCollider
m_Shader: {fileID: 203, guid: 0000000000000000f000000000000000, type: 0}
m_Parent: {fileID: 0}
m_ModifiedSerializedProperties: 0
m_ValidKeywords: []
m_InvalidKeywords: []
m_LightmapFlags: 4
m_EnableInstancingVariants: 0
m_DoubleSidedGI: 0
m_CustomRenderQueue: -1
stringTagMap: {}
disabledShaderPasses: []
m_LockedProperties:
m_SavedProperties:
serializedVersion: 3
m_TexEnvs:
- _MainTex:
m_Texture: {fileID: 2800000, guid: 7e4b97337334f42d2a700647fd938234, type: 3}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
m_Ints: []
m_Floats:
- _Cutoff: 0.5
- _InvFade: 1
m_Colors:
- _Color: {r: 0, g: 1, b: 0, a: 0.65882355}
- _TintColor: {r: 0, g: 0.48937988, b: 1, a: 1}
m_BuildTextureStacks: []
m_AllowLocking: 1

View File

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

View File

@ -4602,7 +4602,7 @@ MeshRenderer:
m_RenderingLayerMask: 1 m_RenderingLayerMask: 1
m_RendererPriority: 0 m_RendererPriority: 0
m_Materials: m_Materials:
- {fileID: 2100000, guid: bbb235fff4f48426b863b36058ab66d2, type: 2} - {fileID: 2100000, guid: e7a77724098fc4307a013f0b3c4464fe, type: 2}
m_StaticBatchInfo: m_StaticBatchInfo:
firstSubMesh: 0 firstSubMesh: 0
subMeshCount: 0 subMeshCount: 0

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: e89966f6cb1bb4daa86f5ab535b27089
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -4602,7 +4602,7 @@ MeshRenderer:
m_RenderingLayerMask: 1 m_RenderingLayerMask: 1
m_RendererPriority: 0 m_RendererPriority: 0
m_Materials: m_Materials:
- {fileID: 2100000, guid: bbb235fff4f48426b863b36058ab66d2, type: 2} - {fileID: 2100000, guid: e7a77724098fc4307a013f0b3c4464fe, type: 2}
m_StaticBatchInfo: m_StaticBatchInfo:
firstSubMesh: 0 firstSubMesh: 0
subMeshCount: 0 subMeshCount: 0

View File

@ -4602,7 +4602,7 @@ MeshRenderer:
m_RenderingLayerMask: 1 m_RenderingLayerMask: 1
m_RendererPriority: 0 m_RendererPriority: 0
m_Materials: m_Materials:
- {fileID: 2100000, guid: bbb235fff4f48426b863b36058ab66d2, type: 2} - {fileID: 2100000, guid: e7a77724098fc4307a013f0b3c4464fe, type: 2}
m_StaticBatchInfo: m_StaticBatchInfo:
firstSubMesh: 0 firstSubMesh: 0
subMeshCount: 0 subMeshCount: 0

View File

@ -301,7 +301,7 @@ MeshRenderer:
m_RenderingLayerMask: 1 m_RenderingLayerMask: 1
m_RendererPriority: 0 m_RendererPriority: 0
m_Materials: m_Materials:
- {fileID: 2100000, guid: bbb235fff4f48426b863b36058ab66d2, type: 2} - {fileID: 2100000, guid: e7a77724098fc4307a013f0b3c4464fe, type: 2}
m_StaticBatchInfo: m_StaticBatchInfo:
firstSubMesh: 0 firstSubMesh: 0
subMeshCount: 0 subMeshCount: 0

View File

@ -301,7 +301,7 @@ MeshRenderer:
m_RenderingLayerMask: 1 m_RenderingLayerMask: 1
m_RendererPriority: 0 m_RendererPriority: 0
m_Materials: m_Materials:
- {fileID: 2100000, guid: bbb235fff4f48426b863b36058ab66d2, type: 2} - {fileID: 2100000, guid: e7a77724098fc4307a013f0b3c4464fe, type: 2}
m_StaticBatchInfo: m_StaticBatchInfo:
firstSubMesh: 0 firstSubMesh: 0
subMeshCount: 0 subMeshCount: 0

View File

@ -301,7 +301,7 @@ MeshRenderer:
m_RenderingLayerMask: 1 m_RenderingLayerMask: 1
m_RendererPriority: 0 m_RendererPriority: 0
m_Materials: m_Materials:
- {fileID: 2100000, guid: bbb235fff4f48426b863b36058ab66d2, type: 2} - {fileID: 2100000, guid: e7a77724098fc4307a013f0b3c4464fe, type: 2}
m_StaticBatchInfo: m_StaticBatchInfo:
firstSubMesh: 0 firstSubMesh: 0
subMeshCount: 0 subMeshCount: 0

View File

@ -301,7 +301,7 @@ MeshRenderer:
m_RenderingLayerMask: 1 m_RenderingLayerMask: 1
m_RendererPriority: 0 m_RendererPriority: 0
m_Materials: m_Materials:
- {fileID: 2100000, guid: bbb235fff4f48426b863b36058ab66d2, type: 2} - {fileID: 2100000, guid: e7a77724098fc4307a013f0b3c4464fe, type: 2}
m_StaticBatchInfo: m_StaticBatchInfo:
firstSubMesh: 0 firstSubMesh: 0
subMeshCount: 0 subMeshCount: 0

File diff suppressed because one or more lines are too long

View File

@ -3,24 +3,19 @@ namespace Intrepid.Core.Entities
public readonly struct EntityMotorMoveOptions public readonly struct EntityMotorMoveOptions
{ {
public readonly bool KeepHeightWhenNoGround; public readonly bool KeepHeightWhenNoGround;
public readonly bool ProjectGapHeightFromLastGround;
public EntityMotorMoveOptions( public EntityMotorMoveOptions(
bool keepHeightWhenNoGround, bool keepHeightWhenNoGround)
bool projectGapHeightFromLastGround)
{ {
KeepHeightWhenNoGround = keepHeightWhenNoGround; KeepHeightWhenNoGround = keepHeightWhenNoGround;
ProjectGapHeightFromLastGround = projectGapHeightFromLastGround;
} }
public static EntityMotorMoveOptions Default => new( public static EntityMotorMoveOptions Default => new(
keepHeightWhenNoGround: false, keepHeightWhenNoGround: false
projectGapHeightFromLastGround: false
); );
public static EntityMotorMoveOptions GapCrossing => new( public static EntityMotorMoveOptions GapCrossing => new(
keepHeightWhenNoGround: true, keepHeightWhenNoGround: true
projectGapHeightFromLastGround: true
); );
} }
} }

View File

@ -1,36 +1,55 @@
using System; using System;
using Intrepid.Core.Entities.Messages;
using Intrepid.Core.Grounding; using Intrepid.Core.Grounding;
using Intrepid.Diagnostics;
using Intrepid.GameManagers;
using Intrepid.Utilities; using Intrepid.Utilities;
using Sirenix.OdinInspector;
using UnityEngine; using UnityEngine;
namespace Intrepid.Core.Entities namespace Intrepid.Core.Entities
{ {
public sealed class EntityMotorTopDown : MonoBehaviour public sealed class EntityMotorTopDown : MonoBehaviour
{ {
private const float MIN_VELOCITY_SQR_MAGNITUDE = 0.0001f; [GroupInjections, SerializeField] private Entity entity;
private const float MIN_PROJECTED_SQR_MAGNITUDE = 0.0001f;
[GroupConfigurations, SerializeField] private LayerMask groundMask; [GroupConfigurations, SerializeField] private LayerMask groundMask;
[GroupConfigurations, SerializeField] private float groundCheckHeight = 1.0f; [GroupConfigurations, SerializeField] private float groundCheckHeight = 1.0f;
[GroupConfigurations, SerializeField] private float groundCheckDistance = 3.0f; [GroupConfigurations, SerializeField] private float groundCheckDistance = 3.0f;
[GroupConfigurations, SerializeField] private float groundOffset; [GroupConfigurations, SerializeField] private float groundOffset;
[GroupConfigurations, SerializeField] private float maxSlopeAngle = 45f; [GroupConfigurations, SerializeField] private float maxSlopeAngle = 45f;
[GroupConfigurations, SerializeField] private float groundProbeRadius = 0.25f; [GroupConfigurations, SerializeField] private float groundProbeRadiusGrounded = 0.5f;
[GroupConfigurations, SerializeField] private float groundProbeRadiusFalling = 0.2f;
[GroupConfigurations, SerializeField] private float fallingGroundCheckDistance = 20f;
[GroupConfigurations, SerializeField] private float minFallHeight = 0.25f;
[GroupConfigurations, SerializeField] private float fallAcceleration = 35f;
[GroupConfigurations, SerializeField] private float maxFallSpeed = 18f;
private Vector3 Origin { get; set; } private Vector3 Origin { get; set; }
private Vector3 End { get; set; } private Vector3 End { get; set; }
private bool HasHit { get; set; } private bool HasHit { get; set; }
private float _fallVerticalSpeed;
private RaycastHit _groundHit; private RaycastHit _groundHit;
private float GroundProbeRadius => IsFalling || IsFallingToAbyss
private bool _hasStableGroundNormal; ? groundProbeRadiusFalling
private Vector3 _stableGroundNormal; : groundProbeRadiusGrounded;
public float MaxSlopeAngle => maxSlopeAngle; public float MaxSlopeAngle => maxSlopeAngle;
public bool HasStableGroundNormal => _hasStableGroundNormal; [GroupDebug, ShowInInspector, ReadOnly] public Vector3 LastSafeGroundPosition { get; private set; }
public Vector3 StableGroundNormal => _stableGroundNormal; public bool HasStableGroundNormal { get; private set; }
public Vector3 StableGroundNormal { get; private set; }
public bool IsFalling { get; private set; }
public bool IsFallingToAbyss { get; private set; }
public bool IsGrounded { get; private set; }
public void ResetFallingToAbyss()
{
IsFalling = false;
IsFallingToAbyss = false;
}
public void Move( public void Move(
Rigidbody body, Rigidbody body,
Vector3 velocity, Vector3 velocity,
@ -39,32 +58,165 @@ namespace Intrepid.Core.Entities
{ {
var current = body.position; var current = body.position;
if (IsFallingToAbyss)
{
return; // there is no movement from here....
}
if (IsFalling)
{
var nextFalling = ResolveFallingPosition(current, deltaTime);
ApplyMove(body, nextFalling);
return;
}
var movementVelocity = ResolveMovementVelocity(velocity); var movementVelocity = ResolveMovementVelocity(velocity);
var next = current + movementVelocity * deltaTime; var next = current + movementVelocity * deltaTime;
if (TryGetGroundSurfaceAt(next, out _groundHit, out var nextNormal)) if (TryGetGroundSurfaceAt(next, out _groundHit, out var nextNormal))
{ {
IsGrounded = true;
IsFallingToAbyss = false;
RegisterStableGroundNormal(nextNormal); RegisterStableGroundNormal(nextNormal);
next.y = _groundHit.point.y + groundOffset; next.y = _groundHit.point.y + groundOffset;
LastSafeGroundPosition = new Vector3(
next.x,
_groundHit.point.y + groundOffset,
next.z
);
if (IsFalling)
{
IsFalling = false;
Proxy.Instance.EventBus.Raise(new EntityRecoverStableGroundEvent
{
EntityId = entity.Id,
});
}
} }
else if (options.KeepHeightWhenNoGround) else if (options.KeepHeightWhenNoGround) // that is probable an ability like sentinel dash, or enemy jump...
{ {
IsGrounded = false;
IsFalling = false;
IsFallingToAbyss = false;
// Important: // Important:
// Do not set next.y = current.y here. // Do not set next.y = current.y here.
// The Y value has already been produced by the velocity projected onto the last stable ground normal. // The Y value has already been produced by the velocity projected onto the last stable ground normal.
// While crossing gaps, the entity keeps moving along the last GroundSurface plane. // While crossing gaps, the entity keeps moving along the last GroundSurface plane.
} }
else if (TryAnyGroundSurfaceBelowAt(next, out var fallHit, out _)
&& IsGroundMeaningfullyBelow(current, fallHit))
{
BeginFall();
next = current;
}
else else
{ {
BeginFallToAbyss();
next = current; next = current;
} }
ApplyMove(body, next);
}
private Vector3 ResolveFallingPosition(Vector3 current, float deltaTime)
{
IsGrounded = false;
IsFallingToAbyss = false;
_fallVerticalSpeed = Mathf.Min(
_fallVerticalSpeed + fallAcceleration * deltaTime,
maxFallSpeed
);
var next = current;
next.y -= _fallVerticalSpeed * deltaTime;
if (!TryAnyGroundSurfaceBelowAt(next, out var fallHit, out var fallNormal))
{
BeginFallToAbyss();
return current; // or next?
}
var targetY = fallHit.point.y + groundOffset;
if (next.y > targetY)
{
return next;
}
next.y = targetY;
RegisterStableGroundNormal(fallNormal);
// LastSafeGroundPosition = new Vector3(
// next.x,
// targetY,
// next.z
// );
IsGrounded = true;
IsFalling = false;
IsFallingToAbyss = false;
_fallVerticalSpeed = 0f;
Proxy.Instance.EventBus.Raise(new EntityRecoverStableGroundEvent
{
EntityId = entity.Id,
});
return next;
}
private void BeginFall()
{
if (IsFalling)
{
return;
}
IsGrounded = false;
IsFalling = true;
IsFallingToAbyss = false;
_fallVerticalSpeed = 0f;
Proxy.Instance.EventBus.Raise(new EntityLeaveStableGroundEvent
{
EntityId = entity.Id,
});
}
private void BeginFallToAbyss()
{
if (IsFallingToAbyss)
{
return;
}
DLogger.LogDebug("Real abyss fall");
IsGrounded = false;
IsFalling = false;
IsFallingToAbyss = true;
_fallVerticalSpeed = 0f;
Proxy.Instance.EventBus.Raise(new EntityFaillingToAbyssEvent
{
EntityId = entity.Id,
});
}
private static void ApplyMove(Rigidbody body, Vector3 next)
{
body.linearVelocity = Vector3.zero; body.linearVelocity = Vector3.zero;
body.angularVelocity = Vector3.zero; body.angularVelocity = Vector3.zero;
body.MovePosition(next); body.MovePosition(next);
} }
private bool IsGroundMeaningfullyBelow(Vector3 current, RaycastHit hit)
{
return hit.point.y + groundOffset < current.y - minFallHeight;
}
public void RefreshStableGroundNormalAt(Vector3 position) public void RefreshStableGroundNormalAt(Vector3 position)
{ {
if (!TryGetGroundSurfaceAt(position, out _, out var normal)) if (!TryGetGroundSurfaceAt(position, out _, out var normal))
@ -77,31 +229,31 @@ namespace Intrepid.Core.Entities
private Vector3 ResolveMovementVelocity(Vector3 velocity) private Vector3 ResolveMovementVelocity(Vector3 velocity)
{ {
if (velocity.sqrMagnitude <= MIN_VELOCITY_SQR_MAGNITUDE) if (velocity.sqrMagnitude <= GonConstants.CommonZeroSqrt)
{ {
return Vector3.zero; return Vector3.zero;
} }
if (!_hasStableGroundNormal) if (!HasStableGroundNormal)
{ {
return velocity; return velocity;
} }
return ProjectVelocityOnPlane( return ProjectVelocityOnPlane(
velocity, velocity,
_stableGroundNormal StableGroundNormal
); );
} }
private void RegisterStableGroundNormal(Vector3 normal) private void RegisterStableGroundNormal(Vector3 normal)
{ {
if (normal.sqrMagnitude <= MIN_VELOCITY_SQR_MAGNITUDE) if (normal.sqrMagnitude <= GonConstants.CommonZeroSqrt)
{ {
return; return;
} }
_stableGroundNormal = normal.normalized; StableGroundNormal = normal.normalized;
_hasStableGroundNormal = true; HasStableGroundNormal = true;
} }
public bool HasGroundSurfaceAt(Vector3 position) public bool HasGroundSurfaceAt(Vector3 position)
@ -145,7 +297,7 @@ namespace Intrepid.Core.Entities
HasHit = Physics.SphereCast( HasHit = Physics.SphereCast(
Origin, Origin,
groundProbeRadius, GroundProbeRadius,
Vector3.down, Vector3.down,
out hit, out hit,
groundCheckDistance, groundCheckDistance,
@ -156,6 +308,42 @@ namespace Intrepid.Core.Entities
return HasHit; return HasHit;
} }
private bool TryAnyGroundSurfaceBelowAt(
Vector3 position,
out RaycastHit hit,
out Vector3 normal)
{
normal = default;
var origin = position + Vector3.up * groundCheckHeight;
HasHit = Physics.SphereCast(
origin,
GroundProbeRadius,
Vector3.down,
out hit,
fallingGroundCheckDistance,
groundMask,
QueryTriggerInteraction.Ignore
);
if (!HasHit)
{
return false;
}
var surface = hit.collider.GetComponentInParent<GroundSurface>();
if (surface.IsNull())
{
return false;
}
normal = surface.Normal;
return IsWalkable(normal);
}
private bool IsWalkable(Vector3 normal) private bool IsWalkable(Vector3 normal)
{ {
var angle = Vector3.Angle(normal, Vector3.up); var angle = Vector3.Angle(normal, Vector3.up);
@ -171,7 +359,7 @@ namespace Intrepid.Core.Entities
planeNormal.normalized planeNormal.normalized
); );
if (projected.sqrMagnitude <= MIN_PROJECTED_SQR_MAGNITUDE) if (projected.sqrMagnitude <= GonConstants.CommonZeroSqrt)
{ {
return Vector3.zero; return Vector3.zero;
} }
@ -184,27 +372,27 @@ namespace Intrepid.Core.Entities
{ {
Gizmos.color = HasHit ? Color.green : Color.red; Gizmos.color = HasHit ? Color.green : Color.red;
Gizmos.DrawWireSphere(Origin, groundProbeRadius); Gizmos.DrawWireSphere(Origin, GroundProbeRadius);
Gizmos.DrawWireSphere(End, groundProbeRadius); Gizmos.DrawWireSphere(End, GroundProbeRadius);
Gizmos.DrawLine( Gizmos.DrawLine(
Origin + Vector3.right * groundProbeRadius, Origin + Vector3.right * GroundProbeRadius,
End + Vector3.right * groundProbeRadius End + Vector3.right * GroundProbeRadius
); );
Gizmos.DrawLine( Gizmos.DrawLine(
Origin - Vector3.right * groundProbeRadius, Origin - Vector3.right * GroundProbeRadius,
End - Vector3.right * groundProbeRadius End - Vector3.right * GroundProbeRadius
); );
Gizmos.DrawLine( Gizmos.DrawLine(
Origin + Vector3.forward * groundProbeRadius, Origin + Vector3.forward * GroundProbeRadius,
End + Vector3.forward * groundProbeRadius End + Vector3.forward * GroundProbeRadius
); );
Gizmos.DrawLine( Gizmos.DrawLine(
Origin - Vector3.forward * groundProbeRadius, Origin - Vector3.forward * GroundProbeRadius,
End - Vector3.forward * groundProbeRadius End - Vector3.forward * GroundProbeRadius
); );
Gizmos.color = Color.yellow; Gizmos.color = Color.yellow;
@ -223,15 +411,15 @@ namespace Intrepid.Core.Entities
Gizmos.color = Color.blue; Gizmos.color = Color.blue;
var sphereAtHit = Origin + Vector3.down * _groundHit.distance; var sphereAtHit = Origin + Vector3.down * _groundHit.distance;
Gizmos.DrawWireSphere(sphereAtHit, groundProbeRadius); Gizmos.DrawWireSphere(sphereAtHit, GroundProbeRadius);
} }
if (_hasStableGroundNormal) if (HasStableGroundNormal)
{ {
Gizmos.color = Color.white; Gizmos.color = Color.white;
Gizmos.DrawLine( Gizmos.DrawLine(
transform.position, transform.position,
transform.position + _stableGroundNormal * 1.25f transform.position + StableGroundNormal * 1.25f
); );
} }
} }

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: bc2e5fbe421d44e78cdc7f66839a97d3
timeCreated: 1780904937

View File

@ -0,0 +1,9 @@
using Intrepid.SimpleEvents;
namespace Intrepid.Core.Entities.Messages
{
public class EntityFaillingToAbyssEvent : IEventBase
{
public string EntityId { get; set; }
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 97b2e4a50ae5410fabbdff2346385f1b
timeCreated: 1780913231

View File

@ -0,0 +1,9 @@
using Intrepid.SimpleEvents;
namespace Intrepid.Core.Entities.Messages
{
public class EntityLeaveStableGroundEvent : IEventBase
{
public string EntityId { get; set; }
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 7d7b62e6ce704eae8efc346f32b38a34
timeCreated: 1780906213

View File

@ -0,0 +1,9 @@
using Intrepid.SimpleEvents;
namespace Intrepid.Core.Entities.Messages
{
public class EntityRecoverStableGroundEvent : IEventBase
{
public string EntityId { get; set; }
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 55a7aad450d241ef99ecd460c0507235
timeCreated: 1780904942

View File

@ -1,4 +1,6 @@
using System;
using Intrepid.Core.Entities; using Intrepid.Core.Entities;
using Intrepid.Core.Entities.Messages;
using Intrepid.Gameplay.Entities.Sentinel.StateMachine; using Intrepid.Gameplay.Entities.Sentinel.StateMachine;
using Intrepid.Integration.Input; using Intrepid.Integration.Input;
using Intrepid.Structures; using Intrepid.Structures;
@ -35,13 +37,31 @@ namespace Intrepid.Gameplay.Entities.Sentinel
protected override void Subscriptions() protected override void Subscriptions()
{ {
Subscribe<EntityFaillingToAbyssEvent>(e =>
{
if (!string.Equals(Entity.Id, e.EntityId, StringComparison.Ordinal)) return;
StateMachine.ChangeState(SentinelStateType.FallToAbyss);
});
Subscribe<EntityLeaveStableGroundEvent>(e =>
{
if (!string.Equals(Entity.Id, e.EntityId, StringComparison.Ordinal)) return;
if (CurrentState == SentinelStateType.Fall) return;
StateMachine.ChangeState(SentinelStateType.Fall);
});
} }
public override void UpdateFromUpdatable() public override void UpdateFromUpdatable()
{ {
StateMachine.CurrentImplementation.RunAfterUpdate(); StateMachine.CurrentImplementation.RunBeforeUpdate();
StateMachine.CurrentImplementation.RunOnUpdate(); StateMachine.CurrentImplementation.RunOnUpdate();
StateMachine.CurrentImplementation.RunAfterUpdate(); StateMachine.CurrentImplementation.RunAfterUpdate();
// state machine could take this responsibility in the future...
if (Inputs.Reset())
{
Entity.ResetLastSafeGroundPosition();
StateMachine.ChangeState(SentinelStateType.FallLanding);
}
} }
public override void LateUpdateFromUpdatable() public override void LateUpdateFromUpdatable()

View File

@ -28,6 +28,7 @@ namespace Intrepid.Gameplay.Entities.Sentinel
public Vector2 DesiredVelocity { get; set; } public Vector2 DesiredVelocity { get; set; }
public Vector3 LinearVelocity { get; private set; } public Vector3 LinearVelocity { get; private set; }
public Vector2 Forward => new Vector2(_desiredForward.x, _desiredForward.z).normalized; public Vector2 Forward => new Vector2(_desiredForward.x, _desiredForward.z).normalized;
public bool IsGrounded => MotorTopDown.IsGrounded;
public override void DeployEntity(Vector2 position) public override void DeployEntity(Vector2 position)
{ {
@ -98,5 +99,11 @@ namespace Intrepid.Gameplay.Entities.Sentinel
{ {
MotorTopDown.RefreshStableGroundNormalAt(UnityRigidbody.position); MotorTopDown.RefreshStableGroundNormalAt(UnityRigidbody.position);
} }
public void ResetLastSafeGroundPosition()
{
UnityRigidbody.position = MotorTopDown.LastSafeGroundPosition;
MotorTopDown.ResetFallingToAbyss();
}
} }
} }

View File

@ -7,5 +7,8 @@ namespace Intrepid.Gameplay.Entities.Sentinel
{ {
public Func<Vector2> Movement { get; set; } public Func<Vector2> Movement { get; set; }
public Func<bool> Dash { get; set; } public Func<bool> Dash { get; set; }
// debug
public Func<bool> Reset { get; set; }
} }
} }

View File

@ -18,5 +18,6 @@ namespace Intrepid.Gameplay.Entities.Sentinel
Fall = 90, Fall = 90,
FallLanding = 91, FallLanding = 91,
FallToAbyss = 92,
} }
} }

View File

@ -25,6 +25,7 @@ namespace Intrepid.Gameplay.Entities.Sentinel.StateMachine.States
protected override void RunOnEnter(ParameterStore parameterStore) protected override void RunOnEnter(ParameterStore parameterStore)
{ {
Brain.Controls.ResetDash();
Brain.Entity.ModelAnimation.Play(dashBreaking); Brain.Entity.ModelAnimation.Play(dashBreaking);
Timer.Stop(); Timer.Stop();
} }

View File

@ -4,7 +4,7 @@ using UnityEngine;
namespace Intrepid.Gameplay.Entities.Sentinel.StateMachine.States namespace Intrepid.Gameplay.Entities.Sentinel.StateMachine.States
{ {
public class SentinelStateDashLanding : SentinelState public class SentinelStateDashWindow : SentinelState
{ {
[GroupConfigurations, SerializeField] private AnimationClip dashWindow; [GroupConfigurations, SerializeField] private AnimationClip dashWindow;
[GroupConfigurations, SerializeField, Range(0, 30)] private float speed = 10; [GroupConfigurations, SerializeField, Range(0, 30)] private float speed = 10;
@ -44,9 +44,16 @@ namespace Intrepid.Gameplay.Entities.Sentinel.StateMachine.States
if (CheckDash()) return; if (CheckDash()) return;
if (Timer.IsNotRunning) if (Timer.IsNotRunning)
{ {
Brain.ChangeState(Brain.Controls.DashShouldBreak() if (Brain.Entity.IsGrounded)
? SentinelStateType.DashBreaking {
: SentinelStateType.Idle); Brain.ChangeState(Brain.Controls.DashShouldBreak()
? SentinelStateType.DashBreaking
: SentinelStateType.Idle);
}
else
{
Brain.ChangeState(SentinelStateType.Fall);
}
return; return;
} }
Timer.Update(Time.deltaTime); Timer.Update(Time.deltaTime);

View File

@ -0,0 +1,42 @@
using System;
using Intrepid.Core.Entities.Messages;
using Intrepid.Structures;
using Intrepid.Utilities;
using UnityEngine;
namespace Intrepid.Gameplay.Entities.Sentinel.StateMachine.States
{
public class SentinelStateFall : SentinelState
{
[GroupConfigurations, SerializeField] private AnimationClip fall;
public override SentinelStateType GetStateKey()
{
return SentinelStateType.Fall;
}
protected override void Subscriptions()
{
Subscribe<EntityRecoverStableGroundEvent>(e =>
{
if (!string.Equals(Brain.Entity.Id, e.EntityId, StringComparison.Ordinal)) return;
Brain.ChangeState(SentinelStateType.FallLanding);
});
}
protected override void RunOnEnter(ParameterStore ps)
{
Brain.Controls.ResetDash();
Brain.Entity.ModelAnimation.CrossFade(fall, .1f);
Brain.Entity.DesiredVelocity = Vector2.zero;
}
protected override void RunOnExit()
{
}
public override void RunOnUpdate()
{
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 05e6adb13632442f84e48179e5b50f39
timeCreated: 1780903889

View File

@ -0,0 +1,44 @@
using Intrepid.Structures;
using Intrepid.Utilities;
using UnityEngine;
namespace Intrepid.Gameplay.Entities.Sentinel.StateMachine.States
{
public class SentinelStateFallLanding : SentinelState
{
[GroupConfigurations, SerializeField] private AnimationClip fallLanding;
[GroupConfigurations, SerializeField, Range(0, 5)] private float fallLandingDuration = 2f;
private Timer Timer { get; } = new();
public override SentinelStateType GetStateKey()
{
return SentinelStateType.FallLanding;
}
protected override void Subscriptions()
{
}
protected override void RunOnEnter(ParameterStore ps)
{
Brain.Entity.ModelAnimation.CrossFade(fallLanding, .1f);
Brain.Entity.DesiredVelocity = Vector2.zero;
Timer.Start();
}
protected override void RunOnExit()
{
Timer.Stop();
}
public override void RunOnUpdate()
{
if (Timer.IsNotRunning) return;
Timer.Update(Timer.ScaledTime);
if (!Timer.Check(fallLandingDuration)) return;
Timer.Stop();
Brain.ChangeState(SentinelStateType.Idle);
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 91ab0d94e5374255ba65660f88b1564e
timeCreated: 1780903997

View File

@ -0,0 +1,35 @@
using Intrepid.Structures;
using Intrepid.Utilities;
using UnityEngine;
namespace Intrepid.Gameplay.Entities.Sentinel.StateMachine.States
{
public class SentinelStateFallToAbyss : SentinelState
{
[GroupConfigurations, SerializeField] private AnimationClip fallToAbyss;
public override SentinelStateType GetStateKey()
{
return SentinelStateType.FallToAbyss;
}
protected override void Subscriptions()
{
}
protected override void RunOnEnter(ParameterStore ps)
{
Brain.Controls.ResetDash();
Brain.Entity.ModelAnimation.CrossFade(fallToAbyss, .1f);
Brain.Entity.DesiredVelocity = Vector2.zero;
}
protected override void RunOnExit()
{
}
public override void RunOnUpdate()
{
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 087a6debf49045e7a827f2c6eabe08eb
timeCreated: 1780913440

View File

@ -109,6 +109,15 @@ public partial class @InputActions: IInputActionCollection2, IDisposable
""processors"": """", ""processors"": """",
""interactions"": """", ""interactions"": """",
""initialStateCheck"": false ""initialStateCheck"": false
},
{
""name"": ""Reset"",
""type"": ""Button"",
""id"": ""30a345d6-b3e2-4f9c-86cc-8998ab80ebd4"",
""expectedControlType"": """",
""processors"": """",
""interactions"": """",
""initialStateCheck"": false
} }
], ],
""bindings"": [ ""bindings"": [
@ -199,6 +208,28 @@ public partial class @InputActions: IInputActionCollection2, IDisposable
""action"": ""Dash"", ""action"": ""Dash"",
""isComposite"": false, ""isComposite"": false,
""isPartOfComposite"": false ""isPartOfComposite"": false
},
{
""name"": """",
""id"": ""4c704831-ecdf-4210-9e62-1893dc62274c"",
""path"": """",
""interactions"": """",
""processors"": """",
""groups"": """",
""action"": ""Reset"",
""isComposite"": false,
""isPartOfComposite"": false
},
{
""name"": """",
""id"": ""94bc0bb4-e442-42e3-b785-48f17ff1f9c2"",
""path"": ""<Keyboard>/r"",
""interactions"": """",
""processors"": """",
""groups"": "";Keyboard & Mouse"",
""action"": ""Reset"",
""isComposite"": false,
""isPartOfComposite"": false
} }
] ]
} }
@ -237,6 +268,7 @@ public partial class @InputActions: IInputActionCollection2, IDisposable
m_sentinel = asset.FindActionMap("sentinel", throwIfNotFound: true); m_sentinel = asset.FindActionMap("sentinel", throwIfNotFound: true);
m_sentinel_Movement = m_sentinel.FindAction("Movement", throwIfNotFound: true); m_sentinel_Movement = m_sentinel.FindAction("Movement", throwIfNotFound: true);
m_sentinel_Dash = m_sentinel.FindAction("Dash", throwIfNotFound: true); m_sentinel_Dash = m_sentinel.FindAction("Dash", throwIfNotFound: true);
m_sentinel_Reset = m_sentinel.FindAction("Reset", throwIfNotFound: true);
} }
~@InputActions() ~@InputActions()
@ -319,6 +351,7 @@ public partial class @InputActions: IInputActionCollection2, IDisposable
private List<ISentinelActions> m_SentinelActionsCallbackInterfaces = new List<ISentinelActions>(); private List<ISentinelActions> m_SentinelActionsCallbackInterfaces = new List<ISentinelActions>();
private readonly InputAction m_sentinel_Movement; private readonly InputAction m_sentinel_Movement;
private readonly InputAction m_sentinel_Dash; private readonly InputAction m_sentinel_Dash;
private readonly InputAction m_sentinel_Reset;
/// <summary> /// <summary>
/// Provides access to input actions defined in input action map "sentinel". /// Provides access to input actions defined in input action map "sentinel".
/// </summary> /// </summary>
@ -339,6 +372,10 @@ public partial class @InputActions: IInputActionCollection2, IDisposable
/// </summary> /// </summary>
public InputAction @Dash => m_Wrapper.m_sentinel_Dash; public InputAction @Dash => m_Wrapper.m_sentinel_Dash;
/// <summary> /// <summary>
/// Provides access to the underlying input action "sentinel/Reset".
/// </summary>
public InputAction @Reset => m_Wrapper.m_sentinel_Reset;
/// <summary>
/// Provides access to the underlying input action map instance. /// Provides access to the underlying input action map instance.
/// </summary> /// </summary>
public InputActionMap Get() { return m_Wrapper.m_sentinel; } public InputActionMap Get() { return m_Wrapper.m_sentinel; }
@ -370,6 +407,9 @@ public partial class @InputActions: IInputActionCollection2, IDisposable
@Dash.started += instance.OnDash; @Dash.started += instance.OnDash;
@Dash.performed += instance.OnDash; @Dash.performed += instance.OnDash;
@Dash.canceled += instance.OnDash; @Dash.canceled += instance.OnDash;
@Reset.started += instance.OnReset;
@Reset.performed += instance.OnReset;
@Reset.canceled += instance.OnReset;
} }
/// <summary> /// <summary>
@ -387,6 +427,9 @@ public partial class @InputActions: IInputActionCollection2, IDisposable
@Dash.started -= instance.OnDash; @Dash.started -= instance.OnDash;
@Dash.performed -= instance.OnDash; @Dash.performed -= instance.OnDash;
@Dash.canceled -= instance.OnDash; @Dash.canceled -= instance.OnDash;
@Reset.started -= instance.OnReset;
@Reset.performed -= instance.OnReset;
@Reset.canceled -= instance.OnReset;
} }
/// <summary> /// <summary>
@ -467,5 +510,12 @@ public partial class @InputActions: IInputActionCollection2, IDisposable
/// <seealso cref="UnityEngine.InputSystem.InputAction.performed" /> /// <seealso cref="UnityEngine.InputSystem.InputAction.performed" />
/// <seealso cref="UnityEngine.InputSystem.InputAction.canceled" /> /// <seealso cref="UnityEngine.InputSystem.InputAction.canceled" />
void OnDash(InputAction.CallbackContext context); void OnDash(InputAction.CallbackContext context);
/// <summary>
/// Method invoked when associated input action "Reset" is either <see cref="UnityEngine.InputSystem.InputAction.started" />, <see cref="UnityEngine.InputSystem.InputAction.performed" /> or <see cref="UnityEngine.InputSystem.InputAction.canceled" />.
/// </summary>
/// <seealso cref="UnityEngine.InputSystem.InputAction.started" />
/// <seealso cref="UnityEngine.InputSystem.InputAction.performed" />
/// <seealso cref="UnityEngine.InputSystem.InputAction.canceled" />
void OnReset(InputAction.CallbackContext context);
} }
} }

View File

@ -23,6 +23,15 @@
"processors": "", "processors": "",
"interactions": "", "interactions": "",
"initialStateCheck": false "initialStateCheck": false
},
{
"name": "Reset",
"type": "Button",
"id": "30a345d6-b3e2-4f9c-86cc-8998ab80ebd4",
"expectedControlType": "",
"processors": "",
"interactions": "",
"initialStateCheck": false
} }
], ],
"bindings": [ "bindings": [
@ -113,6 +122,28 @@
"action": "Dash", "action": "Dash",
"isComposite": false, "isComposite": false,
"isPartOfComposite": false "isPartOfComposite": false
},
{
"name": "",
"id": "4c704831-ecdf-4210-9e62-1893dc62274c",
"path": "",
"interactions": "",
"processors": "",
"groups": "",
"action": "Reset",
"isComposite": false,
"isPartOfComposite": false
},
{
"name": "",
"id": "94bc0bb4-e442-42e3-b785-48f17ff1f9c2",
"path": "<Keyboard>/r",
"interactions": "",
"processors": "",
"groups": ";Keyboard & Mouse",
"action": "Reset",
"isComposite": false,
"isPartOfComposite": false
} }
] ]
} }

View File

@ -62,6 +62,9 @@ namespace Intrepid.Integration.Input
? InputActions.sentinel.Movement.ReadValue<Vector2>() ? InputActions.sentinel.Movement.ReadValue<Vector2>()
: Vector2.zero, : Vector2.zero,
Dash = () => Locking.IsNotLocked && InputActions.sentinel.Dash.WasPressedThisFrame(), Dash = () => Locking.IsNotLocked && InputActions.sentinel.Dash.WasPressedThisFrame(),
// debug
Reset = () => Locking.IsNotLocked && InputActions.sentinel.Reset.WasPressedThisFrame(),
}; };
} }
} }