projectEli/Assets/NeoFPS/Core/MotionGraphs/States/WallRunState.cs
2022-11-06 20:28:33 -05:00

298 lines
13 KiB
C#

#if !NEOFPS_FORCE_QUALITY && (UNITY_ANDROID || UNITY_IOS || UNITY_TIZEN || (UNITY_WSA && NETFX_CORE) || NEOFPS_FORCE_LIGHTWEIGHT)
#define NEOFPS_LIGHTWEIGHT
#endif
using UnityEngine;
using NeoFPS.CharacterMotion.MotionData;
using NeoFPS.CharacterMotion.Parameters;
using NeoSaveGames.Serialization;
namespace NeoFPS.CharacterMotion.States
{
[MotionGraphElement("Wall Movement/Wall Run", "WallRun")]
[HelpURL("https://docs.neofps.com/manual/motiongraphref-mgs-wallrunstate.html")]
public class WallRunState : MotionGraphState
{
[SerializeField, Tooltip("The vector parameter containing the wall normal. This will be read AND written to each frame")]
private VectorParameter m_WallNormal = null;
[SerializeField, Tooltip("A multiplier applied to gravity acceleration when moving up the wall.")]
private FloatDataReference m_ClimbGravityMultiplier = new FloatDataReference(1f);
[SerializeField, Tooltip("A multiplier applied to gravity acceleration when moving down the wall. If set to 0, the vertical character velocity will be clamped to >= 0")]
private FloatDataReference m_FallGravityMultiplier = new FloatDataReference(0.25f);
[SerializeField, Tooltip("How the vertical speed is calculated when entering the wall run.")]
private VerticalStartSpeed m_VerticalMode = VerticalStartSpeed.VerticalBoost;
[SerializeField, Tooltip("The target vertical speed.")]
private FloatDataReference m_VerticalTarget = new FloatDataReference(0f);
[SerializeField, Tooltip("An upward speed boost when first entering the state.")]
private FloatDataReference m_VerticalBoost = new FloatDataReference(2f);
[SerializeField, Tooltip("The maximum downward speed the character can reach while wall running.")]
private FloatDataReference m_MaxFallSpeed = new FloatDataReference(20f);
[SerializeField, Tooltip("Should the downwards speed be limited.")]
private bool m_CapFallSpeed = false;
[SerializeField, Tooltip("How the horizontal wall run speed is calculated.")]
private HorizontalSpeed m_HorizontalMode = HorizontalSpeed.MaintainExisting;
[SerializeField, Tooltip("The target horizontal speed.")]
private FloatDataReference m_HorizontalSpeed = new FloatDataReference(10f);
[SerializeField, Tooltip("The acceleration up to target horizontal speed.")]
private FloatDataReference m_Acceleration = new FloatDataReference(50f);
[SerializeField, Tooltip("The deceleration down to target horizontal speed.")]
private FloatDataReference m_Deceleration = new FloatDataReference(10f);
[SerializeField, Range(0f, 1f), Tooltip("The amount of damping to apply when changing direction or speed")]
private float m_HorizontalDamping = 0.25f;
public enum VerticalStartSpeed
{
VerticalBoost,
CappedBoost,
Minimum,
MaintainExisting,
FixedSpeed
}
public enum HorizontalSpeed
{
MaintainExisting,
TargetSpeed,
MinimumSpeed
}
private Vector3 m_MotorAcceleration = Vector3.zero;
private Vector3 m_OutVelocity = Vector3.zero;
private bool m_Completed = false;
private bool m_ContactFrame = false;
public override bool completed
{
get { return m_Completed; }
}
public override Vector3 moveVector
{
get { return m_OutVelocity * Time.deltaTime; }
}
public override bool applyGravity
{
get { return false; }
}
public override bool applyGroundingForce
{
get { return false; }
}
public override bool ignorePlatformMove
{
get { return false; }
}
public override void OnValidate()
{
base.OnValidate();
}
public override void OnEnter()
{
base.OnEnter();
m_Completed = false;
m_ContactFrame = true;
}
public override void OnExit()
{
base.OnExit();
m_Completed = false;
m_OutVelocity = Vector3.zero;
}
public override void Update()
{
base.Update();
// Check if valid
if (m_WallNormal == null)
{
m_Completed = true;
return;
}
if (m_WallNormal.value.sqrMagnitude < 0.25f)
{
Debug.LogError("Zero wall normal. ID: " + m_WallNormal.GetInstanceID());
return;
}
// Perform cast to check for contact
RaycastHit hit;
bool didHit = controller.characterController.RayCast(0.25f, -m_WallNormal.value, Space.World, out hit, PhysicsFilter.Masks.CharacterBlockers, QueryTriggerInteraction.Ignore);
if (didHit)
{
//Debug.Log("didHit = true");
m_WallNormal.value = hit.normal;
m_OutVelocity = characterController.velocity;
var wallUp = Vector3.ProjectOnPlane(characterController.up, m_WallNormal.value).normalized;
if (m_ContactFrame)
{
switch (m_VerticalMode)
{
case VerticalStartSpeed.VerticalBoost:
// Add the vertical boost
m_OutVelocity += wallUp * m_VerticalBoost.value;
break;
case VerticalStartSpeed.CappedBoost:
{
// Check if up-speed is below target and boost up to the target
Vector3 up = characterController.up;
float upSpeed = Vector3.Dot(m_OutVelocity, up);
if (upSpeed < m_VerticalTarget.value)
{
m_OutVelocity -= up * upSpeed;
m_OutVelocity += up * Mathf.Min(upSpeed + m_VerticalBoost.value, m_VerticalTarget.value);
}
}
break;
case VerticalStartSpeed.Minimum:
{
// Check if up-speed is below target and add the difference if so
Vector3 up = characterController.up;
float upSpeed = Vector3.Dot(m_OutVelocity, up);
if (upSpeed < m_VerticalTarget.value)
m_OutVelocity += up * (m_VerticalTarget.value - upSpeed);
}
break;
case VerticalStartSpeed.FixedSpeed:
{
// Set up speed to fixed values
Vector3 up = characterController.up;
float upSpeed = Vector3.Dot(m_OutVelocity, up);
m_OutVelocity += up * (m_VerticalTarget.value - upSpeed);
}
break;
}
m_ContactFrame = false;
}
else
{
// Decompose veocity
Vector3 up = characterController.up;
float upSpeed = Vector3.Dot(m_OutVelocity, up);
Vector3 horizontal = m_OutVelocity - up * upSpeed;
// Get custom gravity effect
if (upSpeed <= 0.0001f)
{
if (m_FallGravityMultiplier.value > 0.0001f)
{
// Check if the fall speed is capped
if (m_CapFallSpeed && upSpeed < -m_MaxFallSpeed.value)
{
upSpeed = -m_MaxFallSpeed.value;
m_OutVelocity = up * upSpeed;
}
else
{
m_OutVelocity = up * upSpeed;
m_OutVelocity += Vector3.Project(characterController.gravity, wallUp) * Time.deltaTime * m_FallGravityMultiplier.value;
}
}
else
m_OutVelocity = Vector3.zero;
}
else
{
if (m_ClimbGravityMultiplier.value > 0.0001f)
{
m_OutVelocity = up * upSpeed;
m_OutVelocity += Vector3.Project(characterController.gravity, wallUp) * Time.deltaTime * m_ClimbGravityMultiplier.value;
}
else
m_OutVelocity = Vector3.zero;
}
// Calculate horizontal velocity
switch (m_HorizontalMode)
{
case HorizontalSpeed.TargetSpeed:
{
// Damp the velocity to the target
float hSpeed = horizontal.magnitude;
float target = m_HorizontalSpeed.value;
float acceleration = 0f;
if (hSpeed < target)
acceleration = m_Acceleration.value;
else
acceleration = m_Deceleration.value;
Vector3 targetHorizontal = horizontal * (target / hSpeed);
horizontal = Vector3.SmoothDamp(horizontal, targetHorizontal, ref m_MotorAcceleration, Mathf.Lerp(0.05f, 0.25f, m_HorizontalDamping), acceleration);
}
break;
case HorizontalSpeed.MinimumSpeed:
{
// Boost the velocity if below the target
float hSpeed = horizontal.magnitude;
float target = m_HorizontalSpeed.value;
if (hSpeed < target)
{
Vector3 targetHorizontal = horizontal * (target / hSpeed);
horizontal = Vector3.SmoothDamp(horizontal, targetHorizontal, ref m_MotorAcceleration, Mathf.Lerp(0.05f, 0.25f, m_HorizontalDamping), m_Acceleration.value);
}
}
break;
}
// Add the revised horizontal back onto the velocity
m_OutVelocity += horizontal;
}
}
else
{
//Debug.Log("didHit = false");
m_Completed = true;
m_OutVelocity = characterController.velocity;
}
}
public override void CheckReferences(IMotionGraphMap map)
{
m_WallNormal = map.Swap(m_WallNormal);
m_ClimbGravityMultiplier.CheckReference(map);
m_FallGravityMultiplier.CheckReference(map);
m_VerticalTarget.CheckReference(map);
m_VerticalBoost.CheckReference(map);
m_MaxFallSpeed.CheckReference(map);
m_HorizontalSpeed.CheckReference(map);
m_Acceleration.CheckReference(map);
m_Deceleration.CheckReference(map);
base.CheckReferences(map);
}
#region SAVE / LOAD
private static readonly NeoSerializationKey k_CompletedKey = new NeoSerializationKey("completed");
private static readonly NeoSerializationKey k_VelocityKey = new NeoSerializationKey("velocity");
public override void WriteProperties(INeoSerializer writer)
{
base.WriteProperties(writer);
writer.WriteValue(k_VelocityKey, m_OutVelocity);
writer.WriteValue(k_CompletedKey, m_Completed);
}
public override void ReadProperties(INeoDeserializer reader)
{
base.ReadProperties(reader);
reader.TryReadValue(k_VelocityKey, out m_OutVelocity, m_OutVelocity);
reader.TryReadValue(k_CompletedKey, out m_Completed, m_Completed);
}
#endregion
}
}