diff --git a/Assets/Scripts/Intrepid/Core/Entities/EntityMotorTopDown.cs b/Assets/Scripts/Intrepid/Core/Entities/EntityMotorTopDown.cs index 7cd35a0..f27775c 100644 --- a/Assets/Scripts/Intrepid/Core/Entities/EntityMotorTopDown.cs +++ b/Assets/Scripts/Intrepid/Core/Entities/EntityMotorTopDown.cs @@ -14,22 +14,22 @@ namespace Intrepid.Core.Entities [GroupInjections, SerializeField] private Entity entity; [GroupConfigurations, SerializeField] private LayerMask groundMask; + [GroupConfigurations, SerializeField] private bool inheritGroundMotionY; [GroupConfigurations, SerializeField] private float groundCheckHeight = 1.0f; [GroupConfigurations, SerializeField] private float groundCheckDistance = 3.0f; [GroupConfigurations, SerializeField] private float groundOffset; [GroupConfigurations, SerializeField] private float maxSlopeAngle = 45f; [GroupConfigurations, SerializeField] private float groundProbeRadiusGrounded = 0.5f; [GroupConfigurations, SerializeField] private float groundProbeRadiusFalling = 0.2f; - [GroupConfigurations, SerializeField] private float fallingGroundCheckDistance = 20f; + [GroupConfigurations, SerializeField] private float fallingGroundCheckDistance = 150f; [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 End { get; set; } private bool HasHit { get; set; } - + private GroundProfiles _currentGroundProfiles = GroundProfiles.None; private float _fallVerticalSpeed; private RaycastHit _groundHit; private float GroundProbeRadius => IsFalling || IsFallingToAbyss @@ -48,6 +48,7 @@ namespace Intrepid.Core.Entities { IsFalling = false; IsFallingToAbyss = false; + _currentGroundProfiles = GroundProfiles.None; } public void Move( @@ -60,9 +61,9 @@ namespace Intrepid.Core.Entities if (IsFallingToAbyss) { - return; // there is no movement from here.... + return; } - + if (IsFalling) { var nextFalling = ResolveFallingPosition(current, deltaTime); @@ -71,19 +72,29 @@ namespace Intrepid.Core.Entities } var movementVelocity = ResolveMovementVelocity(velocity); - var next = current + movementVelocity * deltaTime; - if (TryGetGroundSurfaceAt(next, out _groundHit, out var nextNormal)) + var groundMotionDelta = ResolveGroundMotionDelta(); + + var next = current + + groundMotionDelta + + movementVelocity * deltaTime; + + if (TryGetGroundSurfaceAt(next, out _groundHit, out var nextNormal, out var groundProfiles)) { IsGrounded = true; IsFallingToAbyss = false; - + + _currentGroundProfiles = groundProfiles; + RegisterStableGroundNormal(nextNormal); + next.y = _groundHit.point.y + groundOffset; + LastSafeGroundPosition = new Vector3( next.x, - _groundHit.point.y + groundOffset, + next.y, next.z ); + if (IsFalling) { IsFalling = false; @@ -93,17 +104,14 @@ namespace Intrepid.Core.Entities }); } } - else if (options.KeepHeightWhenNoGround) // that is probable an ability like sentinel dash, or enemy jump... + else if (options.KeepHeightWhenNoGround) { IsGrounded = false; IsFalling = false; IsFallingToAbyss = false; - // Important: - // 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. - // While crossing gaps, the entity keeps moving along the last GroundSurface plane. + _currentGroundProfiles = GroundProfiles.None; } - else if (TryAnyGroundSurfaceBelowAt(next, out var fallHit, out _) + else if (TryAnyGroundSurfaceBelowAt(next, out var fallHit, out _, out _) && IsGroundMeaningfullyBelow(current, fallHit)) { BeginFall(); @@ -118,6 +126,23 @@ namespace Intrepid.Core.Entities ApplyMove(body, next); } + private Vector3 ResolveGroundMotionDelta() + { + if (!IsGrounded || !_currentGroundProfiles.TryGetGroundProfileMotion(out var motion)) + { + return Vector3.zero; + } + + var delta = motion.DeltaPosition; + + if (inheritGroundMotionY) + { + return delta; + } + + return new Vector3(delta.x, 0f, delta.z); + } + private Vector3 ResolveFallingPosition(Vector3 current, float deltaTime) { IsGrounded = false; @@ -131,7 +156,10 @@ namespace Intrepid.Core.Entities var next = current; next.y -= _fallVerticalSpeed * deltaTime; - if (!TryAnyGroundSurfaceBelowAt(next, out var fallHit, out var fallNormal)) + if (!TryAnyGroundSurfaceBelowAt(next, + out var fallHit, + out var fallNormal, + out var fallProfiles)) { BeginFallToAbyss(); return current; // or next? @@ -148,17 +176,12 @@ namespace Intrepid.Core.Entities RegisterStableGroundNormal(fallNormal); - // LastSafeGroundPosition = new Vector3( - // next.x, - // targetY, - // next.z - // ); - IsGrounded = true; IsFalling = false; IsFallingToAbyss = false; _fallVerticalSpeed = 0f; - + _currentGroundProfiles = fallProfiles; + Proxy.Instance.EventBus.Raise(new EntityRecoverStableGroundEvent { EntityId = entity.Id, @@ -178,7 +201,8 @@ namespace Intrepid.Core.Entities IsFalling = true; IsFallingToAbyss = false; _fallVerticalSpeed = 0f; - + _currentGroundProfiles = GroundProfiles.None; + Proxy.Instance.EventBus.Raise(new EntityLeaveStableGroundEvent { EntityId = entity.Id, @@ -198,6 +222,7 @@ namespace Intrepid.Core.Entities IsFalling = false; IsFallingToAbyss = true; _fallVerticalSpeed = 0f; + _currentGroundProfiles = GroundProfiles.None; Proxy.Instance.EventBus.Raise(new EntityFaillingToAbyssEvent { @@ -219,12 +244,15 @@ namespace Intrepid.Core.Entities public void RefreshStableGroundNormalAt(Vector3 position) { - if (!TryGetGroundSurfaceAt(position, out _, out var normal)) + if (!TryGetGroundSurfaceAt(position, out _, out var normal, out var groundProfiles)) { return; } - RegisterStableGroundNormal(normal); + if (groundProfiles.IsSafe) + { + RegisterStableGroundNormal(normal); + } } private Vector3 ResolveMovementVelocity(Vector3 velocity) @@ -258,15 +286,17 @@ namespace Intrepid.Core.Entities public bool HasGroundSurfaceAt(Vector3 position) { - return TryGetGroundSurfaceAt(position, out _, out _); + return TryGetGroundSurfaceAt(position, out _, out _, out _); } private bool TryGetGroundSurfaceAt( Vector3 position, out RaycastHit hit, - out Vector3 normal) + out Vector3 normal, + out GroundProfiles groundProfiles) { normal = default; + groundProfiles = GroundProfiles.None; if (!TryGetGroundAt(position, out hit)) { @@ -287,6 +317,8 @@ namespace Intrepid.Core.Entities return false; } + groundProfiles = surface.GroundProfiles; + return true; } @@ -311,9 +343,11 @@ namespace Intrepid.Core.Entities private bool TryAnyGroundSurfaceBelowAt( Vector3 position, out RaycastHit hit, - out Vector3 normal) + out Vector3 normal, + out GroundProfiles groundProfiles) { normal = default; + groundProfiles = GroundProfiles.None; var origin = position + Vector3.up * groundCheckHeight; @@ -341,7 +375,14 @@ namespace Intrepid.Core.Entities normal = surface.Normal; - return IsWalkable(normal); + if (!IsWalkable(normal)) + { + return false; + } + + groundProfiles = surface.GroundProfiles; + + return true; } private bool IsWalkable(Vector3 normal) diff --git a/Assets/Scripts/Intrepid/Core/Grounding/GroundProfile.cs b/Assets/Scripts/Intrepid/Core/Grounding/GroundProfile.cs new file mode 100644 index 0000000..30148e4 --- /dev/null +++ b/Assets/Scripts/Intrepid/Core/Grounding/GroundProfile.cs @@ -0,0 +1,9 @@ +using Intrepid.Utilities; + +namespace Intrepid.Core.Grounding +{ + public abstract class GroundProfile : Identification + { + + } +} \ No newline at end of file diff --git a/Assets/Scripts/Intrepid/Core/Grounding/GroundProfile.cs.meta b/Assets/Scripts/Intrepid/Core/Grounding/GroundProfile.cs.meta new file mode 100644 index 0000000..36c5182 --- /dev/null +++ b/Assets/Scripts/Intrepid/Core/Grounding/GroundProfile.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: fdc7306926734d3bb5b74cc1107abb58 +timeCreated: 1781275820 \ No newline at end of file diff --git a/Assets/Scripts/Intrepid/Core/Grounding/GroundProfileMotion.cs b/Assets/Scripts/Intrepid/Core/Grounding/GroundProfileMotion.cs new file mode 100644 index 0000000..a744055 --- /dev/null +++ b/Assets/Scripts/Intrepid/Core/Grounding/GroundProfileMotion.cs @@ -0,0 +1,11 @@ +using UnityEngine; + +namespace Intrepid.Core.Grounding +{ + public abstract class GroundProfileMotion : GroundProfile + { + public abstract Vector3 Velocity { get; } + public abstract Vector3 DeltaPosition { get; } + public abstract bool IsMoving { get; } + } +} \ No newline at end of file diff --git a/Assets/Scripts/Intrepid/Core/Grounding/GroundProfileMotion.cs.meta b/Assets/Scripts/Intrepid/Core/Grounding/GroundProfileMotion.cs.meta new file mode 100644 index 0000000..cd6368b --- /dev/null +++ b/Assets/Scripts/Intrepid/Core/Grounding/GroundProfileMotion.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: e0bf2067f5ce4269a39772ba51ee47e2 +timeCreated: 1781275853 \ No newline at end of file diff --git a/Assets/Scripts/Intrepid/Core/Grounding/GroundProfiles.cs b/Assets/Scripts/Intrepid/Core/Grounding/GroundProfiles.cs new file mode 100644 index 0000000..eef4ec0 --- /dev/null +++ b/Assets/Scripts/Intrepid/Core/Grounding/GroundProfiles.cs @@ -0,0 +1,35 @@ +using System; + +namespace Intrepid.Core.Grounding +{ + public readonly struct GroundProfiles + { + public static readonly GroundProfiles None = new(false); + + public bool IsSafe { get; } + private GroundProfileMotion GroundProfileMotion { get; } + + public GroundProfiles(bool isSafe, GroundProfileMotion groundProfileMotion = null) + { + IsSafe = isSafe; + GroundProfileMotion = groundProfileMotion; + } + + private bool TryGet(T value, out T profile) where T: GroundProfile + { + if (value.IsNull()) + { + profile = null; + return false; + } + + profile = value; + return true; + } + + public bool TryGetGroundProfileMotion(out GroundProfileMotion profile) + { + return TryGet(GroundProfileMotion, out profile); + } + } +} \ No newline at end of file diff --git a/Assets/Scripts/Intrepid/Core/Grounding/GroundProfiles.cs.meta b/Assets/Scripts/Intrepid/Core/Grounding/GroundProfiles.cs.meta new file mode 100644 index 0000000..94cba5c --- /dev/null +++ b/Assets/Scripts/Intrepid/Core/Grounding/GroundProfiles.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 2bcaf8255e9847b8b25727c33769f037 +timeCreated: 1781276176 \ No newline at end of file diff --git a/Assets/Scripts/Intrepid/Core/Grounding/GroundSurface.cs b/Assets/Scripts/Intrepid/Core/Grounding/GroundSurface.cs index 85a707d..6f73302 100644 --- a/Assets/Scripts/Intrepid/Core/Grounding/GroundSurface.cs +++ b/Assets/Scripts/Intrepid/Core/Grounding/GroundSurface.cs @@ -12,21 +12,33 @@ namespace Intrepid.Core.Grounding public class GroundSurface : MonoBehaviour { [GroupInjections, SerializeField] private BoxCollider boxCollider; + [GroupConfigurations("Profiles"), SerializeField] private bool safe = true; + [GroupConfigurations("Profiles"), SerializeField] private GroundProfileMotion groundProfileMotion; + [GroupDebug, ShowInInspector, ReadOnly] public Vector3 Position { get; private set; } [GroupDebug, ShowInInspector, ReadOnly] public Vector3 Normal { get; private set; } + public GroundProfiles GroundProfiles { get; private set; } = GroundProfiles.None; private void OnValidate() { if (boxCollider.IsNull()) return; DetectPosition(); DetectNormal(); + RefreshProfiles(); } private void Awake() { DetectPosition(); DetectNormal(); + RefreshProfiles(); + } + + [GroupDebug, Button(ButtonSizes.Medium)] + private void RefreshProfiles() + { + GroundProfiles = new GroundProfiles(safe, groundProfileMotion); } [Button(ButtonSizes.Medium)] diff --git a/Assets/Scripts/Intrepid/Core/Grounding/MovingGroundSurface.cs b/Assets/Scripts/Intrepid/Core/Grounding/MovingGroundSurface.cs deleted file mode 100644 index 01c3fa1..0000000 --- a/Assets/Scripts/Intrepid/Core/Grounding/MovingGroundSurface.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Intrepid.Core.Grounding -{ - public class MovingGroundSurface : GroundSurface - { - - } -} \ No newline at end of file diff --git a/Assets/Scripts/Intrepid/Core/Grounding/MovingGroundSurface.cs.meta b/Assets/Scripts/Intrepid/Core/Grounding/MovingGroundSurface.cs.meta deleted file mode 100644 index a52387c..0000000 --- a/Assets/Scripts/Intrepid/Core/Grounding/MovingGroundSurface.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: 488d97fcd10d484b8d9e50393014a05c -timeCreated: 1781271534 \ No newline at end of file