gd,fix: fixed a bug where the dash could mantle you nowhere. Automatic mantle at the end of dash.
This commit is contained in:
@ -1,9 +1,10 @@
|
||||
using Godot;
|
||||
using RustyOptions;
|
||||
|
||||
namespace PolarBears.PlayerControllerAddon;
|
||||
|
||||
public record DashLocation(Result<Vector3, string> Result, bool HasHit);
|
||||
public record DashComputation(bool HasHit, Vector3 Location, Vector3 CollisionPoint, Vector3 CollisionNormal);
|
||||
|
||||
public record DashResolve(bool EndWithMantle, Vector3 DashLocation, Vector3 MantleLocation);
|
||||
|
||||
public partial class DashSystem: Node3D
|
||||
{
|
||||
@ -15,24 +16,35 @@ public partial class DashSystem: Node3D
|
||||
private Camera3D _camera;
|
||||
private MeshInstance3D _dashTarget;
|
||||
|
||||
private MantleSystem _mantleSystem;
|
||||
|
||||
public void Init(Node3D head, Camera3D camera)
|
||||
{
|
||||
_dashCast3D = GetNode<ShapeCast3D>("DashCast3D");
|
||||
_head = head;
|
||||
_camera = camera;
|
||||
|
||||
_mantleSystem = GetNode<MantleSystem>("MantleSystem");
|
||||
_mantleSystem.Init(this);
|
||||
|
||||
_dashTarget = GetNode<MeshInstance3D>("DashTarget");
|
||||
_dashTarget.SetVisible(false);
|
||||
}
|
||||
|
||||
private DashLocation ComputeDashLocation()
|
||||
private DashComputation ComputeDashLocation()
|
||||
{
|
||||
var dashLocation = _dashCast3D.IsColliding()
|
||||
? _dashCast3D.GetCollisionPoint(0)
|
||||
: _dashCast3D.ToGlobal(_dashCast3D.TargetPosition);
|
||||
return new DashLocation(Result.Ok(dashLocation), _dashCast3D.IsColliding());
|
||||
if (!_dashCast3D.IsColliding())
|
||||
{
|
||||
return new DashComputation(false, _dashCast3D.ToGlobal(_dashCast3D.TargetPosition), Vector3.Zero, Vector3.Zero);
|
||||
}
|
||||
var collisionPoint = _dashCast3D.GetCollisionPoint(0);
|
||||
var collisionNormal = _dashCast3D.GetCollisionNormal(0);
|
||||
var collisionShape = (SphereShape3D) _dashCast3D.GetShape();
|
||||
var centerSphereLocation = collisionPoint + collisionNormal * collisionShape.Radius;
|
||||
return new DashComputation(true, centerSphereLocation, collisionPoint, collisionNormal);
|
||||
}
|
||||
|
||||
public Result<Vector3, string> PrepareDash()
|
||||
public DashResolve PrepareDash()
|
||||
{
|
||||
_dashTarget.SetVisible(false);
|
||||
|
||||
@ -41,16 +53,23 @@ public partial class DashSystem: Node3D
|
||||
_head.Rotation.Y,
|
||||
_camera.Rotation.Z));
|
||||
|
||||
var (result, hasHit) = ComputeDashLocation();
|
||||
var (hasHit, location, collisionPoint, collisionNormal) = ComputeDashLocation();
|
||||
|
||||
var shouldMantle = false;
|
||||
var mantleLocation = Vector3.Zero;
|
||||
if (hasHit && Mathf.Abs(collisionNormal.Y) < 0.01f)
|
||||
{
|
||||
var mantleResult = _mantleSystem.FindMantleLocationAtPoint(collisionPoint, collisionNormal);
|
||||
shouldMantle = mantleResult.IsSome(out mantleLocation);
|
||||
}
|
||||
|
||||
var targetColor = hasHit ? new Color(0.2f, 0.2f, 1f) : new Color(1f, 1f, 1f);
|
||||
var targetColor = shouldMantle ? new Color(0.2f, 0.2f, 1f) : new Color(1f, 1f, 1f);
|
||||
var targetMaterial = (StandardMaterial3D) _dashTarget.GetSurfaceOverrideMaterial(0);
|
||||
targetMaterial.AlbedoColor = targetColor;
|
||||
|
||||
targetMaterial.SetAlbedo(targetColor);
|
||||
_dashTarget.SetVisible(true);
|
||||
_dashTarget.SetGlobalPosition(result.Unwrap());
|
||||
_dashTarget.SetGlobalPosition(location);
|
||||
|
||||
return result;
|
||||
return new DashResolve(shouldMantle, location, mantleLocation);
|
||||
}
|
||||
|
||||
public void Dash()
|
||||
|
@ -1,13 +1,14 @@
|
||||
using Godot;
|
||||
using System;
|
||||
using Godot;
|
||||
using RustyOptions;
|
||||
|
||||
namespace PolarBears.PlayerControllerAddon;
|
||||
|
||||
public partial class MantleSystem: Node3D
|
||||
{
|
||||
[Export(PropertyHint.Range, "0,2,0.1,or_greater")]
|
||||
[Export(PropertyHint.Range, "0,2,0.1,suffix:m,or_greater")]
|
||||
public float MantleEndLocationDistanceFromWall { get; set; } = 1f;
|
||||
[Export(PropertyHint.Range, "0,10,0.1,or_greater")]
|
||||
[Export(PropertyHint.Range, "0,10,0.1,suffix:m,or_greater")]
|
||||
public float MantleHeightCastStart { get; set; } = 2f;
|
||||
[Export(PropertyHint.Range, "0,10,0.01,suffix:m,or_greater")]
|
||||
public float MaxStepHeight = 0.5f;
|
||||
@ -15,15 +16,16 @@ public partial class MantleSystem: Node3D
|
||||
private Node3D _head;
|
||||
private ShapeCast3D _wallInFrontCast3D;
|
||||
private ShapeCast3D _mantleCast3D;
|
||||
private RayCast3D _mantleCheckCast3D;
|
||||
|
||||
public void Init(ShapeCast3D wallInFrontCast3D, Node3D head, ShapeCast3D mantleCast3D)
|
||||
public void Init(Node3D head)
|
||||
{
|
||||
_wallInFrontCast3D = wallInFrontCast3D;
|
||||
_head = head;
|
||||
_mantleCast3D = mantleCast3D;
|
||||
_wallInFrontCast3D = GetNode<ShapeCast3D>("WallInFrontCast3D");
|
||||
_mantleCast3D = GetNode<ShapeCast3D>("MantleCast3D");
|
||||
}
|
||||
|
||||
public Result<Vector3, string> CheckWallInFront()
|
||||
public Option<Vector3> FindMantleInFrontOfPlayer()
|
||||
{
|
||||
_wallInFrontCast3D.SetRotation(new Vector3(
|
||||
_wallInFrontCast3D.Rotation.X,
|
||||
@ -32,17 +34,25 @@ public partial class MantleSystem: Node3D
|
||||
|
||||
if (!_wallInFrontCast3D.IsColliding())
|
||||
{
|
||||
return Result.Err<Vector3, string>("No collision found");
|
||||
return Option<Vector3>.None;
|
||||
}
|
||||
|
||||
var collisionPoint = _wallInFrontCast3D.GetCollisionPoint(0);
|
||||
var horizontalEndLocation = collisionPoint - _wallInFrontCast3D.GetCollisionNormal(0) * MantleEndLocationDistanceFromWall;
|
||||
var collisionNormal = _wallInFrontCast3D.GetCollisionNormal(0);
|
||||
return FindMantleLocationAtPoint(collisionPoint, collisionNormal);
|
||||
}
|
||||
|
||||
public Option<Vector3> FindMantleLocationAtPoint(Vector3 point, Vector3 wallNormal)
|
||||
{
|
||||
var horizontalEndLocation = point - wallNormal * MantleEndLocationDistanceFromWall;
|
||||
var shapeCastStartLocation = horizontalEndLocation + Vector3.Up * MantleHeightCastStart;
|
||||
|
||||
_mantleCast3D.SetGlobalPosition(shapeCastStartLocation);
|
||||
var targetLocation = Vector3.Down * MantleHeightCastStart + Vector3.Up * MaxStepHeight;
|
||||
_mantleCast3D.SetTargetPosition(targetLocation);
|
||||
|
||||
return _mantleCast3D.IsColliding() ? Result.Ok(_mantleCast3D.GetCollisionPoint(0)) : Result.Err<Vector3, string>("No collision found");
|
||||
|
||||
if (_mantleCast3D.IsColliding() && _mantleCast3D.GetCollisionNormal(0).Y > 0.9f)
|
||||
return Option.Some(_mantleCast3D.GetCollisionPoint(0));
|
||||
return Option<Vector3>.None;
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices.JavaScript;
|
||||
using Godot;
|
||||
using RustyOptions;
|
||||
|
||||
namespace PolarBears.PlayerControllerAddon;
|
||||
|
||||
@ -34,7 +37,15 @@ public partial class PlayerController : CharacterBody3D
|
||||
|
||||
private bool _canDoubleJump = true;
|
||||
private bool _movementEnabled = true;
|
||||
|
||||
private bool _isTweening = false;
|
||||
private record TweenInputs(Vector3 Location, float Duration);
|
||||
|
||||
private Queue<TweenInputs> _tweenInputs = new Queue<TweenInputs>();
|
||||
|
||||
private bool _shouldMantle = false;
|
||||
private Vector3 _dashLocation = Vector3.Zero;
|
||||
private Vector3 _mantleLocation = Vector3.Zero;
|
||||
|
||||
private float _currentSpeed;
|
||||
|
||||
@ -65,8 +76,6 @@ public partial class PlayerController : CharacterBody3D
|
||||
|
||||
RayCast3D stairsBelowRayCast3D = GetNode<RayCast3D>("StairsBelowRayCast3D");
|
||||
RayCast3D stairsAheadRayCast3D = GetNode<RayCast3D>("StairsAheadRayCast3D");
|
||||
ShapeCast3D wallInFrontCast3D = GetNode<ShapeCast3D>("WallInFrontCast3D");
|
||||
ShapeCast3D mantleCast3D = GetNode<ShapeCast3D>("MantleCast3D");
|
||||
|
||||
Node3D cameraSmooth = GetNode<Node3D>("Head/CameraSmooth");
|
||||
|
||||
@ -100,7 +109,7 @@ public partial class PlayerController : CharacterBody3D
|
||||
StairsSystem.Init(stairsBelowRayCast3D, stairsAheadRayCast3D, cameraSmooth);
|
||||
|
||||
MantleSystem = GetNode<MantleSystem>("MantleSystem");
|
||||
MantleSystem.Init(wallInFrontCast3D, Head, mantleCast3D);
|
||||
MantleSystem.Init(Head);
|
||||
|
||||
DashSystem = GetNode<DashSystem>("DashSystem");
|
||||
DashSystem.Init(Head, camera);
|
||||
@ -139,32 +148,58 @@ public partial class PlayerController : CharacterBody3D
|
||||
_movementEnabled = true;
|
||||
}
|
||||
|
||||
private void TweenToLocation(Vector3 location, float duration)
|
||||
public void EndTween()
|
||||
{
|
||||
Tween tween = GetTree().CreateTween();
|
||||
var callback = new Callable(this, MethodName.EnableMovement);
|
||||
EnableMovement();
|
||||
_isTweening = false;
|
||||
}
|
||||
|
||||
private void TweenToLocation(TweenInputs inputs)
|
||||
{
|
||||
var (location, duration) = inputs;
|
||||
|
||||
var tween = GetTree().CreateTween();
|
||||
var callback = new Callable(this, MethodName.EndTween);
|
||||
|
||||
tween.TweenProperty(this, "position", location, duration);
|
||||
tween.TweenCallback(callback);
|
||||
|
||||
DisableMovement();
|
||||
_isTweening = true;
|
||||
tween.Play();
|
||||
}
|
||||
|
||||
private void QueueTween(TweenInputs inputs)
|
||||
{
|
||||
_tweenInputs.Enqueue(inputs);
|
||||
}
|
||||
|
||||
private void QueueTween(Vector3 location, float duration)
|
||||
{
|
||||
QueueTween(new TweenInputs(location, duration));
|
||||
}
|
||||
|
||||
public override void _PhysicsProcess(double delta)
|
||||
{
|
||||
if (_tweenInputs.Count > 0 && !_isTweening)
|
||||
TweenToLocation(_tweenInputs.Dequeue());
|
||||
|
||||
if (Input.IsActionPressed("aim_dash"))
|
||||
{
|
||||
_dashLocation = DashSystem.PrepareDash().Unwrap();
|
||||
(_shouldMantle, _dashLocation, _mantleLocation) = DashSystem.PrepareDash();
|
||||
}
|
||||
|
||||
if (Input.IsActionJustReleased("aim_dash"))
|
||||
{
|
||||
DashSystem.Dash();
|
||||
TweenToLocation(_dashLocation, 0.1f);
|
||||
QueueTween(_dashLocation, 0.1f);
|
||||
if (_shouldMantle)
|
||||
{
|
||||
QueueTween(_mantleLocation, 0.1f);
|
||||
}
|
||||
}
|
||||
|
||||
var mantleLocationResult = MantleSystem.CheckWallInFront();
|
||||
var mantleLocationResult = MantleSystem.FindMantleInFrontOfPlayer();
|
||||
if (isOnFloorCustom())
|
||||
{
|
||||
_lastFrameWasOnFloor = Engine.GetPhysicsFrames();
|
||||
@ -189,10 +224,10 @@ public partial class PlayerController : CharacterBody3D
|
||||
&& !doesCapsuleHaveCrouchingHeight
|
||||
&& !isPlayerDead)
|
||||
{
|
||||
if (mantleLocationResult.IsOk(out var mantleLocation))
|
||||
if (mantleLocationResult.IsSome(out var mantleLocation))
|
||||
{
|
||||
var duration = 0.1f * mantleLocation.DistanceTo(Position);
|
||||
TweenToLocation(mantleLocation, duration);
|
||||
QueueTween(mantleLocation, duration);
|
||||
}
|
||||
else if (isOnFloorCustom())
|
||||
{
|
||||
|
Reference in New Issue
Block a user