239 lines
9.1 KiB
C#
239 lines
9.1 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("Dashes/Anim-Curve Wall Dash", "Wall Dash (Anim Curve)")]
|
|
[HelpURL("https://docs.neofps.com/manual/motiongraphref-mgs-animcurvewalldashstate.html")]
|
|
public class AnimCurveWallDashState : MotionGraphState
|
|
{
|
|
[SerializeField, Tooltip("The target speed for the dash to reach. This will be layered on top of the control speed.")]
|
|
private FloatDataReference m_DashSpeed = new FloatDataReference(50f);
|
|
[SerializeField, Tooltip("The wall normal parameter, as used by the wall run states.")]
|
|
private VectorParameter m_WallNormal = null;
|
|
[SerializeField, Tooltip("The amount of time it takes to reach the dash speed. At this point, the animation curve kicks in to ease out of the dash. A Dash In Time of 0 is instant.")]
|
|
private float m_DashInTime = 0.05f;
|
|
[SerializeField, Tooltip("The amount of time it takes to reach the dash speed. At this point, the animation curve kicks in to ease out of the dash. A Dash In Time of 0 is instant.")]
|
|
private float m_DashOutTime = 0.5f;
|
|
[SerializeField, Tooltip("The angle offset for the dash direction. For example, yaw relative and an angle of 90 will dash to the right. -90 will dash to the left.")]
|
|
private AnimationCurve m_DashOutCurve = new AnimationCurve(new Keyframe[]
|
|
{
|
|
new Keyframe(0f, 1f), new Keyframe(0.1f, 1f), new Keyframe(0.7f, -0.05f), new Keyframe(0.9f, 0.02f), new Keyframe(1f, 0f)
|
|
});
|
|
[SerializeField, Tooltip("Should the yaw direction of the character be turned if the character must change directions to curve with the wall. It's easy to become disoriented with this disabled.")]
|
|
private bool m_YawWithCurve = true;
|
|
|
|
private const float k_TinyValue = 0.001f;
|
|
|
|
private Vector3 m_DashHeading = Vector3.zero;
|
|
private Vector3 m_OutVelocity = Vector3.zero;
|
|
private float m_LerpIn = 0f;
|
|
private float m_LerpOut = 0f;
|
|
private float m_EntrySpeed = 0f;
|
|
private bool m_DashForwards = true;
|
|
private bool m_Completed = 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();
|
|
|
|
var keys = m_DashOutCurve.keys;
|
|
if (keys.Length < 2)
|
|
{
|
|
var newKeys = new Keyframe[2];
|
|
if (keys.Length == 1)
|
|
newKeys[0] = keys[0];
|
|
else
|
|
newKeys[0] = new Keyframe(0f, 1f);
|
|
newKeys[1] = new Keyframe(1f, 0f);
|
|
m_DashOutCurve.keys = newKeys;
|
|
}
|
|
else
|
|
{
|
|
keys[0].time = 0f;
|
|
keys[keys.Length - 1].time = 1f;
|
|
}
|
|
|
|
m_DashInTime = Mathf.Clamp(m_DashInTime, 0f, 10f);
|
|
m_DashOutTime = Mathf.Clamp(m_DashOutTime, 0.05f, 10f);
|
|
}
|
|
|
|
public override void OnEnter()
|
|
{
|
|
base.OnEnter();
|
|
|
|
m_LerpOut = 0f;
|
|
m_Completed = false;
|
|
m_DashForwards = true;
|
|
|
|
// Reset lerp in (skip if lerp takes less than 1 frame)
|
|
m_LerpIn = 0f;
|
|
|
|
// Get heading
|
|
m_DashHeading = characterController.velocity.normalized;
|
|
|
|
// Get velocity data
|
|
m_OutVelocity = Vector3.ProjectOnPlane(characterController.velocity, characterController.up);
|
|
if (m_WallNormal != null)
|
|
m_OutVelocity = Vector3.ProjectOnPlane(m_OutVelocity, m_WallNormal.value);
|
|
m_DashHeading = m_OutVelocity.normalized;
|
|
m_EntrySpeed = m_OutVelocity.magnitude;
|
|
}
|
|
|
|
public override void OnExit()
|
|
{
|
|
base.OnExit();
|
|
|
|
m_Completed = false;
|
|
m_DashHeading = Vector3.zero;
|
|
m_OutVelocity = Vector3.zero;
|
|
m_LerpOut = 0f;
|
|
}
|
|
|
|
public override void Initialise(IMotionController c)
|
|
{
|
|
base.Initialise(c);
|
|
m_DashOutCurve.preWrapMode = WrapMode.ClampForever;
|
|
m_DashOutCurve.postWrapMode = WrapMode.ClampForever;
|
|
}
|
|
|
|
public override void Update()
|
|
{
|
|
base.Update();
|
|
|
|
var up = characterController.up;
|
|
|
|
//Sort dash speed
|
|
float dashSpeed = 0f;
|
|
if (m_LerpIn < 1f)
|
|
{
|
|
if (m_DashInTime < Time.fixedDeltaTime)
|
|
m_LerpIn = 1f;
|
|
else
|
|
{
|
|
m_LerpIn += Time.deltaTime / m_DashInTime;
|
|
if (m_LerpIn > 1f)
|
|
m_LerpIn = 1f;
|
|
}
|
|
|
|
dashSpeed = EasingFunctions.EaseInQuadratic(m_LerpIn) * m_DashSpeed.value;
|
|
}
|
|
else
|
|
{
|
|
m_LerpOut += Time.deltaTime / m_DashOutTime;
|
|
if (m_LerpOut > 1f)
|
|
{
|
|
m_LerpOut = 1f;
|
|
m_Completed = true;
|
|
}
|
|
|
|
dashSpeed = m_DashOutCurve.Evaluate(m_LerpOut) * m_DashSpeed.value;
|
|
|
|
}
|
|
|
|
// Get new dash direction (based on curved walls)
|
|
if (m_WallNormal != null && m_WallNormal.value.sqrMagnitude > 0.25f)
|
|
{
|
|
// 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)
|
|
m_WallNormal.value = hit.normal;
|
|
}
|
|
|
|
// Rotated direction based on deflections, etc
|
|
var velocity = characterController.velocity;
|
|
if (m_DashForwards && velocity.sqrMagnitude > 0.001f)
|
|
{
|
|
Vector3 newHeading = Vector3.ProjectOnPlane(characterController.velocity, up).normalized;
|
|
|
|
// Turn character
|
|
if (m_YawWithCurve)
|
|
{
|
|
float angle = Vector3.SignedAngle(m_DashHeading, newHeading, up);
|
|
controller.aimController.AddYaw(angle);
|
|
}
|
|
|
|
m_DashHeading = newHeading;
|
|
}
|
|
|
|
// Record dash direction for next frame
|
|
m_DashForwards = (dashSpeed >= 0f);
|
|
|
|
// Get velocity from speed and heading
|
|
m_OutVelocity = m_DashHeading * (dashSpeed + m_EntrySpeed);
|
|
}
|
|
|
|
public override void CheckReferences(IMotionGraphMap map)
|
|
{
|
|
m_DashSpeed.CheckReference(map);
|
|
m_WallNormal = map.Swap(m_WallNormal);
|
|
|
|
base.CheckReferences(map);
|
|
}
|
|
|
|
#region SAVE / LOAD
|
|
|
|
private static readonly NeoSerializationKey k_HeadingKey = new NeoSerializationKey("heading");
|
|
private static readonly NeoSerializationKey k_OutVelKey = new NeoSerializationKey("outVel");
|
|
private static readonly NeoSerializationKey k_LerpInKey = new NeoSerializationKey("lerpIn");
|
|
private static readonly NeoSerializationKey k_LerpOutKey = new NeoSerializationKey("lerpOut");
|
|
private static readonly NeoSerializationKey k_EntrySpeedKey = new NeoSerializationKey("entrySpeed");
|
|
private static readonly NeoSerializationKey k_ForwardsKey = new NeoSerializationKey("forwards");
|
|
|
|
public override void WriteProperties(INeoSerializer writer)
|
|
{
|
|
base.WriteProperties(writer);
|
|
writer.WriteValue(k_HeadingKey, m_DashHeading);
|
|
writer.WriteValue(k_OutVelKey, m_OutVelocity);
|
|
writer.WriteValue(k_LerpInKey, m_LerpIn);
|
|
writer.WriteValue(k_LerpOutKey, m_LerpOut);
|
|
writer.WriteValue(k_EntrySpeedKey, m_EntrySpeed);
|
|
writer.WriteValue(k_ForwardsKey, m_DashForwards);
|
|
}
|
|
|
|
public override void ReadProperties(INeoDeserializer reader)
|
|
{
|
|
base.ReadProperties(reader);
|
|
reader.TryReadValue(k_HeadingKey, out m_DashHeading, m_DashHeading);
|
|
reader.TryReadValue(k_OutVelKey, out m_OutVelocity, m_OutVelocity);
|
|
reader.TryReadValue(k_LerpInKey, out m_LerpIn, m_LerpIn);
|
|
reader.TryReadValue(k_LerpOutKey, out m_LerpOut, m_LerpOut);
|
|
reader.TryReadValue(k_EntrySpeedKey, out m_EntrySpeed, m_EntrySpeed);
|
|
reader.TryReadValue(k_ForwardsKey, out m_DashForwards, m_DashForwards);
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
} |