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.
464 lines
18 KiB
C#
464 lines
18 KiB
C#
|
|
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
using MalbersAnimations.Scriptables;
|
|
|
|
namespace MalbersAnimations.Controller
|
|
{
|
|
public class Jump : State
|
|
{
|
|
public override string StateName => "Jump/Root Motion Jump";
|
|
|
|
|
|
/// <summary>If the Jump input is pressed, the Animal will keep going Up while the Jump Animation is Playing</summary>
|
|
[Header("Jump Parameters")]
|
|
[Tooltip("If the Jump input is pressed, the Animal will keep going Up while the Jump Animation is Playing")]
|
|
public BoolReference JumpPressed;
|
|
[Tooltip("Check if the Animal can control the Forward Movement of the Jump")]
|
|
public BoolReference ForwardPressed;
|
|
|
|
/// <summary>If the Forward input is pressed, the Animal will keep going Forward while the Jump Animation is Playing</summary>
|
|
//[Tooltip("If the Forward input is pressed, the Animal will keep going Forward while the Jump Animation is Playing")]
|
|
//public bool JumpForwardPressed;
|
|
public float JumpPressedLerp = 5;
|
|
private float JumpPressHeight_Value = 1;
|
|
private float JumpPressForward = 1;
|
|
private float JumpPressForwardAdditive = 0;
|
|
|
|
//private float JumpPressForward_Value = 1;
|
|
public BoolReference AirControl = new BoolReference(true);
|
|
//private float JumpPressForward_Value = 1;
|
|
public FloatReference AirRotation = new FloatReference(10);
|
|
|
|
#if UNITY_2020_3_OR_NEWER
|
|
[NonReorderable]
|
|
#endif
|
|
public List<JumpProfile> jumpProfiles = new List<JumpProfile>();
|
|
protected MSpeed JumpSpeed;
|
|
|
|
protected bool OneCastingFall_Ray = false;
|
|
|
|
/// <summary> Current Jump Profile</summary>
|
|
protected JumpProfile activeJump;
|
|
private RaycastHit JumpRay;
|
|
|
|
private bool CanJumpAgain;
|
|
private Vector3 JumpStartDirection;
|
|
|
|
public override bool TryActivate() => InputValue && CanJumpAgain;
|
|
|
|
public override void ResetStateValues()
|
|
{
|
|
CanJumpAgain = true;
|
|
JumpPressHeight_Value = 1;
|
|
JumpPressForward = 1;
|
|
JumpPressForwardAdditive = 0;
|
|
OneCastingFall_Ray = false;
|
|
// GoingDown = false;
|
|
}
|
|
|
|
public override void AwakeState()
|
|
{
|
|
if (string.IsNullOrEmpty(EnterTag)) EnterTag.Value = "JumpStart";
|
|
if (string.IsNullOrEmpty(ExitTag)) ExitTag.Value = "JumpEnd";
|
|
|
|
base.AwakeState();
|
|
}
|
|
|
|
|
|
public override void Activate()
|
|
{
|
|
//SpeedSets = new List<MSpeedSet> { animal.CurrentSpeedSet }; //Store the Speed from the Last State
|
|
|
|
base.Activate();
|
|
|
|
IgnoreLowerStates = true; //Make sure while you are on Jump State above the list cannot check for Trying to activate State below him
|
|
// IsPersistent = true; //IMPORTANT!!!!! DO NOT ELIMINATE!!!!!
|
|
|
|
animal.currentSpeedModifier.animator = 1;
|
|
General.CustomRotation = true;
|
|
CanJumpAgain = false;
|
|
}
|
|
|
|
public override void EnterTagAnimation()
|
|
{
|
|
if (CurrentAnimTag == EnterTagHash)
|
|
{
|
|
Debugging($"[EnterTag - {EnterTag.Value}]");
|
|
|
|
animal.DeltaPos = Vector3.zero;
|
|
|
|
if (!animal.RootMotion)
|
|
{
|
|
var JumpStartSpeed = new MSpeed(animal.CurrentSpeedModifier)
|
|
{
|
|
name = "JumpStartSpeed",
|
|
position = animal.HorizontalSpeed,
|
|
Vertical = animal.CurrentSpeedModifier.Vertical.Value,
|
|
animator = 1,
|
|
rotation = AirRotation.Value,
|
|
};
|
|
|
|
animal.SetCustomSpeed(JumpStartSpeed,false); //Set the Current Speed to the Jump Speed Modifier
|
|
|
|
}
|
|
|
|
JumpStartDirection = animal.Forward;
|
|
|
|
if (animal.TerrainSlope > 0) //Means we are jumping uphill
|
|
animal.UseCustomAlign = true;
|
|
}
|
|
else if (CurrentAnimTag == ExitTagHash && animal.hash_StateOn == 0) //Do not do this on State ON Trigger
|
|
{
|
|
CanExit = true;
|
|
Debugging($"[EnterTag - {ExitTag.Value}] - Allow Exit");
|
|
AllowExit();
|
|
}
|
|
}
|
|
|
|
public override Vector3 Speed_Direction()
|
|
{
|
|
if (animal.HasExternalForce)
|
|
{
|
|
return Vector3.ProjectOnPlane(animal.ExternalForce, animal.UpVector);
|
|
}
|
|
else if (AirControl)
|
|
{
|
|
return base.Speed_Direction();
|
|
}
|
|
else
|
|
{
|
|
return JumpStartDirection;
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary> Make the Jump Start</summary>
|
|
public override void EnterCoreAnimation()
|
|
{
|
|
Debugging($"[Enter Core Tag - [Jump]");
|
|
|
|
FindJumpProfile();
|
|
|
|
OneCastingFall_Ray = false; //Reset Values IMPROTANT
|
|
JumpPressHeight_Value = 1;
|
|
JumpPressForward = 1;
|
|
JumpPressForwardAdditive = 0;
|
|
//IsPersistent = true;
|
|
animal.UseGravity = false;
|
|
animal.ResetGravityValues();
|
|
|
|
JumpSpeed = new MSpeed(animal.CurrentSpeedModifier) //Inherit the Vertical and the Lerps
|
|
{
|
|
name = $"Jump [{activeJump.name}]",
|
|
position = animal.RootMotion ? 0 : animal.HorizontalSpeed * activeJump.ForwardMultiplier, //Inherit the Horizontal Speed you have from the last state
|
|
animator = 1,
|
|
rotation = (AirRotation.Value),
|
|
lerpPosAnim = JumpPressedLerp,
|
|
lerpPosition = JumpPressedLerp,
|
|
};
|
|
|
|
|
|
animal.SetCustomSpeed(JumpSpeed); //Set the Current Speed to the Jump Speed Modifier
|
|
JumpStartDirection = animal.Forward;
|
|
|
|
if (animal.TerrainSlope > 0) //Means we are jumping uphill HACK
|
|
animal.UseCustomAlign = true;
|
|
}
|
|
|
|
private void FindJumpProfile()
|
|
{
|
|
activeJump = jumpProfiles != null ? jumpProfiles[0] : new JumpProfile();
|
|
|
|
foreach (var jump in jumpProfiles) //Save/Search the Current Jump Profile by the Lowest Speed available
|
|
{
|
|
if (jump.LastState == null)
|
|
{
|
|
if (jump.VerticalSpeed <= animal.VerticalSmooth)
|
|
{
|
|
activeJump = jump;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (jump.VerticalSpeed <= animal.VerticalSmooth && jump.LastState == animal.LastState.ID)
|
|
{
|
|
activeJump = jump;
|
|
}
|
|
}
|
|
}
|
|
|
|
Debugging($"Jump Profile: <B>[{ activeJump.name}]</B>");
|
|
}
|
|
|
|
public override void OnStateMove(float deltaTime)
|
|
{
|
|
if (InCoreAnimation)
|
|
{
|
|
IsPersistent = false; //HACK NO need to be persistant
|
|
|
|
if (activeJump.JumpLandDistance == 0) return; //Meaning is a false Jump Like neigh on the Horse IMPORTANT!!!!
|
|
|
|
if (JumpPressed)
|
|
{
|
|
JumpPressHeight_Value = Mathf.MoveTowards(JumpPressHeight_Value, InputValue ? 1 : 0, deltaTime * JumpPressedLerp);
|
|
}
|
|
|
|
if (ForwardPressed)
|
|
{
|
|
JumpPressForward = Mathf.MoveTowards(JumpPressForward, animal.MovementAxis.z, deltaTime * JumpPressedLerp);
|
|
JumpPressForwardAdditive = Mathf.MoveTowards(JumpPressForwardAdditive, animal.MovementAxis.z, deltaTime * JumpPressedLerp);
|
|
}
|
|
|
|
if (!General.RootMotion) //If the Jump is NOT Root Motion!!
|
|
{
|
|
Vector3 ExtraJumpHeight = (animal.UpVector * activeJump.HeightMultiplier);
|
|
animal.AdditivePosition += ExtraJumpHeight * deltaTime * JumpPressHeight_Value;
|
|
}
|
|
else //If the Jump IS Root Motion!! ***********************************************************************
|
|
{
|
|
Vector3 RootMotionUP = Vector3.Project(Anim.deltaPosition, animal.UpVector); //Get the Up vector of the Root Motion Animation
|
|
|
|
bool isGoingUp = Vector3.Dot(RootMotionUP, animal.Up) > 0; //Check if the Jump Root Animation is going UP;
|
|
|
|
if (isGoingUp)
|
|
{
|
|
//Remove the default Root Motion Jump
|
|
animal.AdditivePosition -= RootMotionUP;
|
|
|
|
//Add the New Root Motion Jump scaled by the Height Multiplier
|
|
animal.AdditivePosition +=
|
|
(RootMotionUP * activeJump.HeightMultiplier * JumpPressHeight_Value);
|
|
}
|
|
|
|
// Vector3 RootMotionForward = Vector3.ProjectOnPlane(Anim.deltaPosition, animal.Up);
|
|
Vector3 RootMotionForward = Anim.deltaPosition - RootMotionUP;
|
|
|
|
//Remove the default Root Motion Jump
|
|
animal.AdditivePosition -= RootMotionForward;
|
|
|
|
//If is cannot controlled in air?
|
|
if (!AirControl.Value)
|
|
{
|
|
//Add the New Root Motion Jump scaled by the Height Multiplier
|
|
animal.AdditivePosition +=
|
|
JumpStartDirection * RootMotionForward.magnitude * activeJump.ForwardMultiplier;
|
|
|
|
}
|
|
else
|
|
//Add the New Root Motion Jump scaled by the Height Multiplier
|
|
animal.AdditivePosition +=
|
|
(RootMotionForward * activeJump.ForwardMultiplier * JumpPressForward) +
|
|
(animal.Forward * activeJump.ForwardPressed * JumpPressForwardAdditive * deltaTime);
|
|
}
|
|
}
|
|
else if (ExitTagHash == CurrentAnimTag) //meaning is on Exit Jump
|
|
{
|
|
if (!animal.CheckIfGrounded())
|
|
{
|
|
animal.UseGravity = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
public override void TryExitState(float DeltaTime)
|
|
{
|
|
if (animal.StateTime >= activeJump.fallingTime && !OneCastingFall_Ray)
|
|
Check_for_Falling();
|
|
|
|
if (activeJump.ExitTime >= activeJump.fallingTime && animal.StateTime >= activeJump.ExitTime)
|
|
{
|
|
Debugging($"[Allow Exit] - Exit Time [{activeJump.ExitTime}] ");
|
|
animal.CheckIfGrounded();
|
|
AllowExit(); //Using Exit Time Internally * New
|
|
}
|
|
|
|
CheckForGround(animal.StateTime);
|
|
}
|
|
|
|
|
|
private void CheckForGround(float normalizedTime)
|
|
{
|
|
if (activeJump.CliffLandDistance == 0) return; //Do nothing if RayLenght is zero
|
|
|
|
var clifftime = activeJump.CliffTime != 0 ? activeJump.CliffTime : 0.333f;
|
|
|
|
if (normalizedTime > clifftime) //Need to happen the First 1/3 of the Root Jump Animation
|
|
{
|
|
var RayLength = activeJump.CliffLandDistance * ScaleFactor;
|
|
var MainPivot = animal.Main_Pivot_Point;
|
|
|
|
if (debug)
|
|
Debug.DrawRay(MainPivot, -animal.Up * RayLength, Color.black, 0.1f);
|
|
|
|
if (Physics.Raycast(MainPivot, -animal.Up, out JumpRay, RayLength, GroundLayer, IgnoreTrigger))
|
|
{
|
|
if (debug) MTools.DebugTriangle(JumpRay.point, 0.1f, Color.black);
|
|
|
|
var TerrainSlope = Vector3.Angle(JumpRay.normal, animal.UpVector);
|
|
var DeepSlope = TerrainSlope > animal.maxAngleSlope;
|
|
|
|
if (!DeepSlope) //Jump to a jumpable cliff not an inclined one
|
|
{
|
|
Debugging("[Allow Exit] Near Ground");
|
|
AllowExit();
|
|
animal.CheckIfGrounded();
|
|
// Debug.Break();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>Check if the animal can change to fall state if there's no future ground to land on</summary>
|
|
private void Check_for_Falling()
|
|
{
|
|
AllowExit(); //Set the Allow exit just in case
|
|
OneCastingFall_Ray = true;
|
|
|
|
if (activeJump.JumpLandDistance == 0)
|
|
{
|
|
animal.CheckIfGrounded(); //We are still on the ground
|
|
return; //Meaning that is a False Jump (like Neigh on the Horse)
|
|
}
|
|
|
|
float RayLength = animal.ScaleFactor * activeJump.JumpLandDistance; //Ray Distance with the Scale Factor
|
|
var MainPivot = animal.Main_Pivot_Point;
|
|
var Direction = -animal.Up;
|
|
|
|
|
|
if (activeJump.JumpLandDistance > 0) //greater than 0 it can complete the Jump on an even Ground
|
|
{
|
|
if (debug)
|
|
Debug.DrawRay(MainPivot, Direction * RayLength, Color.red, 0.25f);
|
|
|
|
if (Physics.Raycast(MainPivot, Direction, out JumpRay, RayLength, GroundLayer, IgnoreTrigger))
|
|
{
|
|
Debugging($"Min Distance to complete <B>[{ activeJump.name}]</B> -> { JumpRay.distance:F4}");
|
|
if (debug) MTools.DebugTriangle(JumpRay.point, 0.1f, Color.yellow);
|
|
|
|
var GroundSlope = Vector3.Angle(JumpRay.normal, animal.UpVector);
|
|
|
|
if (GroundSlope > animal.maxAngleSlope) //if we found something but there's a deep slope
|
|
{
|
|
Debugging($"[AllowExit] Try to Land but the Sloope was too Deep. Slope: {GroundSlope:F2}");
|
|
animal.UseGravity = General.Gravity;
|
|
return;
|
|
}
|
|
|
|
|
|
if (JumpRay.distance < animal.Height) //Meaning is near the ground
|
|
{
|
|
AllowExit();
|
|
return;
|
|
}
|
|
|
|
IgnoreLowerStates = true; //Means that it can complete the Jump Ignore Fall Locomotion and Idle
|
|
Debugging($"Can finish the Jump. Going to Jump End");
|
|
}
|
|
else
|
|
{
|
|
animal.UseGravity = General.Gravity;
|
|
Debugging($"[Allow Exit] - <B>Jump [{activeJump.name}] </B> Go to Fall..No Ground was found");
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
public override void NewActiveState(StateID newState)
|
|
{
|
|
if (newState.ID <= 1) CanJumpAgain = true; //Can Jump again if the states are Idle or Locomotion
|
|
}
|
|
|
|
|
|
|
|
#if UNITY_EDITOR
|
|
internal void Reset()
|
|
{
|
|
ID = MTools.GetInstance<StateID>("Jump");
|
|
Input = "Jump";
|
|
|
|
SleepFromState = new List<StateID>() { MTools.GetInstance<StateID>("Fall"), MTools.GetInstance<StateID>("Fly") };
|
|
SleepFromMode = new List<ModeID>() { MTools.GetInstance<ModeID>("Action"), MTools.GetInstance<ModeID>("Attack1") };
|
|
|
|
|
|
EnterTag.Value = "JumpStart";
|
|
ExitTag.Value = "JumpEnd";
|
|
|
|
General = new AnimalModifier()
|
|
{
|
|
RootMotion = true,
|
|
Grounded = false,
|
|
Sprint = false,
|
|
OrientToGround = false,
|
|
CustomRotation = true,
|
|
IgnoreLowerStates = true, //IMPORTANT!
|
|
Persistent = false,
|
|
AdditivePosition = true,
|
|
AdditiveRotation = true,
|
|
Gravity = false,
|
|
modify = (modifier)(-1),
|
|
};
|
|
|
|
ExitFrame = false;
|
|
|
|
jumpProfiles = new List<JumpProfile>()
|
|
{ new JumpProfile()
|
|
{ name = "Jump", fallingTime = 0.7f, CliffTime = 0.333f, ForwardMultiplier = 1, HeightMultiplier = 1, JumpLandDistance = 1.7f}
|
|
};
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>Different Jump parameters on different speeds</summary>
|
|
[System.Serializable]
|
|
public struct JumpProfile
|
|
{
|
|
/// <summary>Name to identify the Jump Profile</summary>
|
|
public string name;
|
|
|
|
/// <summary>Maximum Vertical Speed to Activate this Jump</summary>
|
|
[Tooltip("Minimal Vertical Speed to Activate this Jump")]
|
|
[Min(0)] public float VerticalSpeed;
|
|
|
|
/// <summary>Min Distance to Complete the Land when the Jump is on the Highest Point, this needs to be calculate manually</summary>
|
|
[Tooltip("Min Distance to Complete the Land when the Jump is on the Highest Point")]
|
|
[Min(0)] public float JumpLandDistance;
|
|
|
|
/// <summary>Animation normalized time to change to fall animation if the ray checks if the animal is falling </summary>
|
|
[Tooltip("Animation normalized time to change to fall animation if the ray checks if the animal is falling ")]
|
|
[Range(0, 1)]
|
|
public float fallingTime;
|
|
|
|
|
|
/// <summary>Animation normalized time to change to fall animation if the ray checks if the animal is falling </summary>
|
|
[Tooltip("Set Allow Exit to the Jump Profile (*New)")]
|
|
[Range(0, 1)]
|
|
public float ExitTime;
|
|
|
|
|
|
[Tooltip("Animation normalized time to check if we can end the jump sooner. if its set to zero I will use 0.333 normalize value as default")]
|
|
[Range(0, 1)]
|
|
public float CliffTime;
|
|
|
|
/// <summary>Maximum distance to land on a Cliff </summary>
|
|
[Tooltip("Maximum distance to land on a Cliff")]
|
|
[Min(0)] public float CliffLandDistance;
|
|
|
|
/// <summary>Height multiplier to increase/decrease the Height Jump</summary>
|
|
public float HeightMultiplier;
|
|
///// <summary>Forward multiplier to increase/decrease the Forward Speed of the Jump</summary>
|
|
[Tooltip("Forward multiplier to increase/decrease the Forward Rootmotion speed of the Jump")]
|
|
public float ForwardMultiplier;
|
|
|
|
[Tooltip("Extra forward Movement to move the Animal Forward")]
|
|
public float ForwardPressed;
|
|
|
|
[Tooltip("Last State the animal was before making the Jump")]
|
|
public StateID LastState;
|
|
}
|
|
}
|