You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

371 lines
14 KiB
C#

using MalbersAnimations.Utilities;
using MalbersAnimations.Scriptables;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
using UnityEngine.Serialization;
//FROM MOTH
namespace MalbersAnimations.Controller
{
public class LedgeGrab : State
{
public override string StateName => "Ledge Grab";
/// <summary>Air Resistance while falling</summary>
[Header("Ledge Parameters"), Space]
[Tooltip("Layer to identify climbable surfaces")]
public LayerReference LedgeLayer = new LayerReference(1);
[Tooltip("Climb the Ledge automatically when is near a climbable surface")]
public BoolReference automatic = new BoolReference();
[Tooltip("Set the Animal Rigidbody to Kinematic while is on this state. This avoid the colliders to interfiere with ledge.")]
public BoolReference Kinematic = new BoolReference(true);
[Tooltip("Correct Distance from the wall to the character")]
[Min(0)] public float wallDistance = 0.5f;
[Tooltip("Distance required to check a wall in front of the character")]
[Min(0)] public float ForwardLength = 1f;
//[Tooltip("Length of the Ledge Ray when pointing Down")]
//[Min(0)] public float DownLength = 1f;
[Tooltip("Correct Distance from the wall to the character")]
[Min(0)] public float WallChecker = 0.1f;
[Tooltip("Smoothness value to align the animal to the wall")]
[Min(0)] public float AlignSmoothness = 10f;
//[Tooltip("Time to align the animal to the wall")]
//public float AlignTime = 0.2f;
public List<LedgeProfiles> profiles = new List<LedgeProfiles>();
/// <summary>Aligmnet offset found from the character to the ledge</summary>
private Vector3 AlignmentOffset;
private float AngleDifference;
private Vector3 StartPosition;
private Vector3 TargetPosition;
/// <summary> Store the Current Ledge Profile </summary>
private LedgeProfiles LedgeProfile;
private RaycastHit FoundLedgeHit;
private RaycastHit FoundWallHit;
public override bool TryActivate()
{
if (automatic || InputValue) return FindLedge();
return false;
}
public bool FindLedge()
{
foreach (var p in profiles)
{
//Check if we are in Vertical Speed Range
if (p.MaxVSpeed == 0 || p.MaxVSpeed <= animal.VerticalSmooth)
{
var LedgeForwardPoint1 = transform.TransformPoint(new Vector3(0, p.Height, 0));
var WallPoint1 = animal.transform.TransformPoint(new Vector3(0, p.Height - p.LedgeExitDistance - WallChecker, 0));
var ForwardDistance = ForwardLength * ScaleFactor * p.ForwardMultiplier;
var LedgeExitDistance = p.LedgeExitDistance * ScaleFactor;
var LedgeDownPoint1 = LedgeForwardPoint1 + (Forward * ForwardDistance);
if (animal.debugGizmos)
{
Debug.DrawRay(LedgeForwardPoint1, (Forward * ForwardDistance), Color.green);
Debug.DrawRay(WallPoint1, (Forward * ForwardDistance), Color.yellow);
Debug.DrawRay(LedgeDownPoint1, -Up * LedgeExitDistance, Color.red);
}
//Cast the first Ray--- to see if there nothing in front of the character
if (Physics.Raycast(LedgeForwardPoint1, Forward, out _, ForwardDistance, LedgeLayer.Value, IgnoreTrigger) == false) //No walls poiting forward
{
//Check Ledge Pointing Down the Second First Ray
if (Physics.Raycast(LedgeDownPoint1, -Up, out FoundLedgeHit, LedgeExitDistance, LedgeLayer.Value, IgnoreTrigger))
{
//Do not Grab ledge on a Slope Angle
if ((Vector3.Angle(FoundLedgeHit.normal, Up) < animal.maxAngleSlope)
//We need to not find wall
&& (Physics.Raycast(WallPoint1, Forward, out FoundWallHit, ForwardDistance, LedgeLayer.Value, IgnoreTrigger)))
{
animal.SetPlatform(FoundLedgeHit.transform);
LedgeProfile = p; //Store the current Ledge Profile
//var w0 = transform.position - (Up * 10);
//var w1 = transform.position + (Up * 10);
//var point = MTools.ClosestPointOnLine(w0, w1, FoundLedgeHit.point);
//MTools.DrawWireSphere(point, Color.white, 0.01f, 1f);
//MTools.DrawWireSphere(transform.position, Color.white, 0.01f, 1f);
//Debug.DrawLine(w0, w1, Color.red,2);
var LedgeHitDifference = (LedgeExitDistance - (FoundLedgeHit.distance)) * animal.UpVector;
var WallHitDifference = (wallDistance - (FoundWallHit.distance)) * -animal.Forward;
AlignmentOffset = LedgeHitDifference + (WallHitDifference);
AngleDifference = Vector3.SignedAngle(Forward, -FoundWallHit.normal, Up);
AlignmentOffset += LedgeProfile.AlingOffset;
animal.InertiaPositionSpeed = Vector3.zero; //Remove internia
animal.AdditivePosition = Vector3.zero; //Remove additive
CheckKinematic();
// animal.transform.position += AlignmentOffset;
StartPosition = animal.transform.position;
TargetPosition = animal.transform.position + AlignmentOffset;
MTools.DrawWireSphere(StartPosition, Color.white, 0.01f, 1f);
MTools.DrawWireSphere(TargetPosition, Color.green, 0.01f, 1f);
Debugging($"Try [Ledge-Grab] Wall and Ledge found. <B>[{p.name}]</B>. Wall-Hit Difference: [{WallHitDifference}]");
return true;
}
}
}
}
}
return false;
}
public override Vector3 Speed_Direction() => Vector3.zero; //This State does not require a speed
public override void Activate()
{
base.Activate();
CheckKinematic();
SetEnterStatus(LedgeProfile.EnterStatus);
}
private void CheckKinematic()
{
animal.InertiaPositionSpeed = Vector3.zero; //Remove internia
animal.DeltaPos = Vector3.zero; //Remove Delta position
if (Kinematic.Value)
{
animal.RB.collisionDetectionMode = CollisionDetectionMode.ContinuousSpeculative;
animal.RB.isKinematic = true;
}
}
private bool InTransition;
private bool ExitTransition;
public override void OnStateMove(float deltatime)
{
if (InCoreAnimation)
{
InTransition = false;
if (Anim.IsInTransition(0))
{
var TransTime = Anim.GetAnimatorTransitionInfo(0).normalizedTime;
animal.AdditivePosition = Vector3.zero;
//var offset = Vector3.Lerp(Vector3.zero, AlignmentOffset, deltatime * AlignSmoothness);
//animal.AdditivePosition += offset;
//AlignmentOffset -= offset;
animal.transform.position = Vector3.Lerp(StartPosition, TargetPosition, TransTime);
InTransition = true;
// Debug.Log("TransTime = " + TransTime);
}
if (!InTransition && !ExitTransition && IsActiveState)
{
animal.transform.position = TargetPosition;
ExitTransition = true;
//Debug.Log("ExitTransition");
}
animal.InertiaPositionSpeed = Vector3.zero; //Remove internia
animal.PlatformMovement();
if (LedgeProfile != null)
{
if (LedgeProfile.Orient)
{
float DeltaAngle = Mathf.Lerp(0, AngleDifference, deltatime * AlignSmoothness * 2f);
AngleDifference -= DeltaAngle;
//animal.AdditiveRotation *= Quaternion.Euler(0, DeltaAngle, 0); //NOT WORKING DON't KNWO WHY
animal.transform.rotation *= Quaternion.Euler(0, DeltaAngle, 0);
}
if (LedgeProfile.AdditivePosition)
{
var time = animal.AnimState.normalizedTime;
// Debug.Log($"animal { time:F3}");
animal.AdditivePosition += Up * LedgeProfile.HeightCurve.Evaluate(time) * LedgeProfile.HeightSpeed * deltatime;
animal.AdditivePosition += Forward * LedgeProfile.ForwardCurve.Evaluate(time) * LedgeProfile.ForwardSpeed * deltatime;
}
}
}
}
public override void TryExitState(float DeltaTime)
{
if (animal.AnimState.normalizedTime > LedgeProfile.ExitTime) //Exit after the Current Ledge Profile time
{
AllowExit();
animal.CheckIfGrounded();
Debugging($"Allow Exit - {LedgeProfile.name} After Exit Time {animal.AnimState.normalizedTime:F3} > {LedgeProfile.ExitTime}");
}
}
public override void ResetStateValues()
{
LedgeProfile = null;
InTransition = false;
ExitTransition = false;
StartPosition = Vector3.zero;
TargetPosition = Vector3.zero;
AngleDifference = 0;
AlignmentOffset = Vector3.zero;
FoundLedgeHit = new RaycastHit();
FoundWallHit = new RaycastHit();
if (Kinematic.Value && animal) animal.RB.isKinematic = false;
}
public override void StateGizmos(MAnimal animal)
{
if (Application.isPlaying) return;
foreach (var p in profiles)
{
var point1 = animal.transform.TransformPoint(new Vector3(0, p.Height, 0));
var pointWall1 = animal.transform.TransformPoint(new Vector3(0, p.Height - p.LedgeExitDistance - WallChecker, 0));
var scale = animal.ScaleFactor;
var dir = animal.Forward * ForwardLength * scale * p.ForwardMultiplier;
var dirWall = animal.Forward * wallDistance * scale;
var point2 = point1 + dir;
var downExit = -animal.Up * p.LedgeExitDistance * scale;
Gizmos.color = Color.green;
Gizmos.DrawRay(point1, dir);
// Gizmos.DrawRay(point2, downDir);
Gizmos.color = Color.yellow;
Gizmos.DrawRay(pointWall1, dir);
Gizmos.color = Color.red;
Gizmos.DrawRay(pointWall1, dirWall);
Gizmos.color = Color.red;
Gizmos.DrawRay(point2, downExit);
}
}
#if UNITY_EDITOR
void Reset()
{
//Surface = MTools.GetResource<PhysicMaterial>("Climbable");
ID = MTools.GetInstance<StateID>("LedgeGrab");
automatic.Value = true;
General = new AnimalModifier()
{
modify = (modifier)(-1),
RootMotion = true,
AdditivePosition = true,
AdditiveRotation = false,
Grounded = false,
Sprint = true,
OrientToGround = false,
Gravity = false,
CustomRotation = false,
FreeMovement = false,
IgnoreLowerStates = true,
};
profiles = new List<LedgeProfiles>();
var prof = new LedgeProfiles();
profiles.Add(prof);
Input = "Jump";
Editor_Tabs1 = 3;
}
#endif
}
[System.Serializable]
public class LedgeProfiles
{
public string name = "Ledge Grab";
[Tooltip("State Enter Status to Activate while")]
public int EnterStatus = 0;
[Tooltip("Max Vertical Speed Needed to Check this Profile")]
public float MaxVSpeed = 0;
[Tooltip("Forward Length Multiplier applied to the Global Length")]
public float ForwardMultiplier = 1;
[Tooltip("Height Offset to cast the Ray for checking a ledge")]
[Min(0)] public float Height = 1.5f;
[Tooltip("Ray to check if we have found a ledge")]
[Min(0)] public float LedgeExitDistance = 0.25f;
[Tooltip("If the Animation Normalized Time of this state (Ledge Grab) is greater Exit Animation time,\n" +
" the State will Allow Exit()... so other states can try activate themselves.")]
[Range(0,1)] public float ExitTime = 0.9f;
[Tooltip("Offset added to Align to the Ledge")]
public Vector3 AlingOffset;
[Tooltip("Align the Animal to the Wall's normal direction")]
public bool Orient = true;
public bool AdditivePosition = false;
[Hide("AdditivePosition",true,false)]
[Min(0)] public float HeightSpeed = 0.5f;
[Hide("AdditivePosition",true,false)]
[Min(0)] public float ForwardSpeed = 0.5f;
[Hide("AdditivePosition",true,false)]
public AnimationCurve HeightCurve = new AnimationCurve(
new Keyframe(0, 1), new Keyframe(0.45f, 1), new Keyframe(0.55f, 0f), new Keyframe(1, 0f)
);
[Hide("AdditivePosition",true,false)]
public AnimationCurve ForwardCurve = new AnimationCurve(
new Keyframe(0, 0), new Keyframe(0.45f, 0), new Keyframe(0.55f, 1f), new Keyframe(1, 1f)
);
}
}