Files
beyond/Assets/Scripts/Invector/vFootbridgeAction.cs
2024-12-18 15:25:43 +01:00

595 lines
22 KiB
C#

using Invector.vCharacterController.vActions;
using System.Collections;
using System.Collections.Generic;
using Invector;
using Invector.vCharacterController;
using UnityEngine;
using UnityEngine.Events;
namespace Beyond
{
[vClassHeader("Footbridge Action", false)]
public class vFootbridgeAction : vActionListener
{
private static readonly int FootbridgeBalans = Animator.StringToHash("FootbridgeBalans");
#region public variables
[vEditorToolbar("Settings", overrideChildOrder: true, order = 0)]
public float balanceSpeed = 0.5f;
public float playerBalanceStrength = 0.2f;
[Tooltip("Tag of the object you want to access")]
public string actionTag = "FootbridgeTrigger";
[vHelpBox("Disable Selected HUD. Script ActionTriggerEvent.cs")]
public bool disableHUD = true;
[Tooltip("Speed multiplier for the climb ladder animations")]
public float footbridgeSpeed = 1f;
[Tooltip("Input to use going forward or backwards")]
public GenericInput verticallInput = new GenericInput("Vertical", "LeftAnalogVertical", "Vertical");
[Tooltip("Input to use Balance")]
public GenericInput horizontalInput = new GenericInput("Horizontal", "RightAnalogVertical", "Horizontal");
[Tooltip("Input to enter")]
public GenericInput enterInput = new GenericInput("E", "A", "A");
[Tooltip("Input to exit")]
public GenericInput exitInput = new GenericInput("Space", "X", "B");
[Tooltip("whether we allow exit from interaction on request.")]
public bool allowExit;
public bool snapLocalPositionX;
public bool snapLocalPositionY;
public bool snapLocalPositionZ;
[Min(0f)]
public float snapPositionSpeed = 1f;
public Vector3 snapLocalPosition = Vector3.zero;
public bool snapLocalRotationToParent;
[Min(0f)]
public float snapRotationSpeed = 1f;
[vEditorToolbar("Events")]
public UnityEvent OnEnter;
public UnityEvent OnExit;
public UnityEvent OnEnterTrigger;
public UnityEvent OnExitTrigger;
[vEditorToolbar("Debug")]
public bool debugMode;
[vReadOnly(false)]
[SerializeField]
protected float balance = 0f;
[vReadOnly(false)]
[SerializeField]
public float playerBalanceInput;
private float balanceX;
[vReadOnly(false)]
[SerializeField]
protected vTriggerFootbridgeAction targetAction;
[vReadOnly(false)]
[SerializeField]
protected vTriggerFootbridgeAction currentAction;
[vReadOnly(false)]
[SerializeField]
protected List<vTriggerFootbridgeAction> actionTriggers = new List<vTriggerFootbridgeAction>();
[vReadOnly(false)]
[SerializeField]
protected bool isUsing;
[vReadOnly(false)]
[SerializeField]
protected bool triggerEnterOnce;
[vReadOnly(false)]
[SerializeField]
protected bool triggerExitOnce;
[vReadOnly(false)]
[SerializeField]
protected bool enterStarted;
[vReadOnly(false)]
[SerializeField]
protected bool inEnterAnimation;
[vReadOnly(false)]
[SerializeField]
protected bool inExitingAnimation;
[vReadOnly(false)]
[SerializeField]
protected float currentSpeed;
[vReadOnly(false)]
[SerializeField]
protected float speed;
#endregion
protected vThirdPersonInput tpInput;
private TriggerDescriptor m_descriptor;
protected virtual void Awake()
{
actionStay = true;
actionExit = true;
m_descriptor = new TriggerDescriptor(gameObject, TriggerDescriptor.TriggerType.Generic);
}
private void OnFootbridgeEnter(GameObject gameObject)
{
if (disableHUD)
{
balanceX = 0f;
//ActionTriggerEvent.EnterFootbridge?.Invoke(gameObject);
ActionTriggerEvent.ActionTriggerEnter?.Invoke(m_descriptor);
}
}
private void OnFootbridgeExit(GameObject gameObject)
{
if (disableHUD)
{
//ActionTriggerEvent.ExitFootbridge?.Invoke(gameObject);
ActionTriggerEvent.ActionTriggerExit?.Invoke(m_descriptor);
}
}
protected override void Start()
{
base.Start();
OnEnter.AddListener(() => OnFootbridgeEnter(gameObject));
OnExit.AddListener(() => OnFootbridgeExit(gameObject));
tpInput = GetComponent<vThirdPersonInput>();
if (tpInput)
{
tpInput.onUpdate -= UpdateBehavior;
tpInput.onUpdate += UpdateBehavior;
tpInput.onAnimatorMove -= Using;
tpInput.onAnimatorMove += Using;
}
}
protected virtual void UpdateBehavior()
{
AutoEnter();
EnterInput();
ExitInput();
SnapPlayerPosition();
SnapPlayerRotation();
UpdateBalans();
}
private void UpdateBalans()
{
if (tpInput == null) return;
if (!isUsing) return;
playerBalanceInput = horizontalInput.GetAxis() * playerBalanceStrength;
balanceX += Time.deltaTime * balanceSpeed + playerBalanceInput;
balance = 1.1f * Mathf.Sin(balanceX); // hack 1.1f* because balance balue must be greater than 1 or less than -1
tpInput.cc.animator.SetFloat(FootbridgeBalans, balance);
}
private void SnapPlayerRotation()
{
if (tpInput == null) return;
if (isUsing && snapLocalRotationToParent && tpInput.cc.IsAnimatorTag("Snap"))
{
transform.localRotation = Quaternion.Lerp(transform.localRotation, transform.parent.localRotation, Time.deltaTime * snapRotationSpeed);
}
}
private void SnapPlayerPosition()
{
if (tpInput == null) return;
if (isUsing && (snapLocalPositionX || snapLocalPositionY || snapLocalPositionZ) && tpInput.cc.IsAnimatorTag("Snap"))
{
Vector3 curentLocalPosition = transform.localPosition;
if (snapLocalPositionX)
{
curentLocalPosition.x = Mathf.Lerp(curentLocalPosition.x, snapLocalPosition.x, Time.deltaTime * snapPositionSpeed);
}
if (snapLocalPositionY)
{
curentLocalPosition.y = Mathf.Lerp(curentLocalPosition.y, snapLocalPosition.y, Time.deltaTime * snapPositionSpeed);
}
if (snapLocalPositionZ)
{
curentLocalPosition.z = Mathf.Lerp(curentLocalPosition.z, snapLocalPosition.z, Time.deltaTime * snapPositionSpeed);
}
transform.localPosition = curentLocalPosition;
}
}
protected virtual void EnterInput()
{
if (targetAction == null || tpInput.cc.customAction || tpInput.cc.isJumping || !tpInput.cc.isGrounded || tpInput.cc.isRolling)
{
return;
}
if (enterInput.GetButtonDown() && !enterStarted && !isUsing && !targetAction.autoAction)
{
TriggerEnter();
}
}
protected virtual void ExitInput()
{
if (!isUsing)
{
return;
}
if (tpInput.cc.baseLayerInfo.IsName("EnterFootbridge"))
{
return;
}
if (targetAction == null && allowExit)
{
if (tpInput.cc.IsAnimatorTag("Footbridge"))
{
// exit at any moment by pressing the cancelInput
if (exitInput.GetButtonDown())
{
if (debugMode)
{
Debug.Log("Quick Exit..." + currentAction.name + "_" + currentAction.transform.parent.gameObject.name);
}
tpInput.cc.animator.speed = 1;
tpInput.cc.animator.CrossFadeInFixedTime("QuickExitFootbridge", 0.1f);
Invoke("ResetPlayerSettings", .5f);
}
}
}
else if (targetAction != null)
{
currentAction = targetAction;
var animationClip = targetAction.Entrance;
if (animationClip)
{
// exit when reach the start by pressing the cancelInput or pressing down at
if (exitInput.GetButtonDown() && !triggerExitOnce || (speed <= -0.05f && !triggerExitOnce) && targetAction != null && !triggerExitOnce)
{
if (debugMode)
{
Debug.Log("Exit Start..." + currentAction.name + "_" + currentAction.transform.parent.gameObject.name);
}
triggerExitOnce = true;
tpInput.cc.animator.CrossFadeInFixedTime(targetAction.exitAnimationStart, targetAction.exitStartTransitionDuration); // trigger the animation clip
}
}
else if (!animationClip && tpInput.cc.IsAnimatorTag("Footbridge")) // exit from the end
{
if ((speed >= 0.05f) && !triggerExitOnce && !tpInput.cc.animator.IsInTransition(0)) // trigger the exit animation by pressing up
{
if (debugMode)
{
Debug.Log("Exit End..." + currentAction.name + "_" + currentAction.transform.parent.gameObject.name);
}
triggerExitOnce = true;
tpInput.cc.animator.CrossFadeInFixedTime(targetAction.exitAnimationEnd, targetAction.exitEndTransitionDuration); // trigger the animation clip
}
}
}
}
public virtual void ResetPlayerSettings()
{
if (debugMode)
{
Debug.Log("Reset Player Settings");
}
speed = 0f;
targetAction = null;
isUsing = false;
OnExit.Invoke();
triggerExitOnce = false;
triggerEnterOnce = false;
inEnterAnimation = false;
enterStarted = false;
tpInput.cc.animator.SetInteger(vAnimatorParameters.ActionState, 0);
tpInput.cc.EnableGravityAndCollision();
tpInput.SetLockAllInput(false);
tpInput.cc.StopCharacter();
tpInput.cc.disableAnimations = false;
tpInput.cc.animator.updateMode = AnimatorUpdateMode.Fixed;
if (transform.parent != null)
{
transform.parent = null;
}
}
protected virtual void AutoEnter()
{
if (targetAction == null || !targetAction.autoAction)
{
return;
}
if (tpInput.cc.customAction || isUsing || tpInput.cc.animator.IsInTransition(0))
{
return;
}
// enter the ladder automatically if checked with autoAction
if (targetAction.autoAction && tpInput.cc.input != Vector3.zero && !tpInput.cc.customAction)
{
var inputDir = Camera.main.transform.TransformDirection(new Vector3(tpInput.cc.input.x, 0f, tpInput.cc.input.z));
inputDir.y = 0f;
var dist = Vector3.Distance(inputDir.normalized, targetAction.transform.forward);
if (dist < 0.8f)
{
TriggerEnter();
}
}
}
protected virtual void TriggerEnter()
{
if (debugMode)
{
Debug.Log("Enter Footbridge");
}
OnExitTrigger.Invoke();
if (targetAction.targetCharacterParent)
{
transform.parent = targetAction.targetCharacterParent;
}
targetAction.Entrance = true;
tpInput.cc.isCrouching = false;
tpInput.cc.ControlCapsuleHeight();
tpInput.UpdateCameraStates();
tpInput.cc.UpdateAnimator();
OnEnter.Invoke();
triggerEnterOnce = true;
enterStarted = true;
tpInput.cc.animator.SetInteger(vAnimatorParameters.ActionState, 1); // set actionState 1 to avoid falling transitions
tpInput.SetLockAllInput(true);
tpInput.cc.ResetInputAnimatorParameters();
targetAction.OnDoAction.Invoke();
currentAction = targetAction;
tpInput.cc.animator.updateMode = AnimatorUpdateMode.Normal;
if (!string.IsNullOrEmpty(currentAction.playAnimation))
{
if (debugMode)
{
Debug.Log("TriggerAnimation " + currentAction.name + "_" + currentAction.transform.parent.gameObject.name);
}
tpInput.cc.animator.CrossFadeInFixedTime(currentAction.playAnimation, currentAction.playTransitionDuration); // trigger the action animation clip
isUsing = true;
tpInput.cc.disableAnimations = true;
tpInput.cc.StopCharacter();
}
}
protected virtual void Using()
{
if (!isUsing)
{
return;
}
// update the base layer to know what animations are being played
tpInput.cc.AnimatorLayerControl();
tpInput.cc.ActionsControl();
// update camera movement
tpInput.CameraInput();
// go forward or backwards
currentSpeed = footbridgeSpeed;
speed = verticallInput.GetAxis();
tpInput.cc.animator.SetFloat(vAnimatorParameters.InputVertical, speed, 0.1f, Time.deltaTime);
if (speed >= 0.05f || speed <= -0.05f)
{
tpInput.cc.animator.speed = Mathf.Lerp(tpInput.cc.animator.speed, currentSpeed, 2f * Time.deltaTime);
}
else
{
tpInput.cc.animator.speed = Mathf.Lerp(tpInput.cc.animator.speed, 1f, 2f * Time.deltaTime);
}
// enter behaviour
var _inEnterAnimation = tpInput.cc.baseLayerInfo.IsName("EnterFootbridge") && !tpInput.cc.animator.IsInTransition(0);
if (_inEnterAnimation)
{
inEnterAnimation = true;
tpInput.cc.DisableGravityAndCollision(); // disable gravity & turn collision trigger
// disable ingame hud
//if (currentAction != null)
//{
// //currentAction.OnPlayerExit.Invoke();
//}
if (currentAction.useTriggerRotation)
{
if (debugMode)
{
Debug.Log("Rotating to target..." + currentAction.name + "_" + currentAction.transform.parent.gameObject.name);
}
EvaluateToRotation(currentAction.enterRotationCurve, currentAction.matchTarget.transform.rotation, tpInput.cc.baseLayerInfo.normalizedTime);
}
if (currentAction.matchTarget != null)
{
if (transform.parent != currentAction.targetCharacterParent)
{
transform.parent = currentAction.targetCharacterParent;
}
if (debugMode)
{
Debug.Log("Match Target to Enter..." + currentAction.name + "_" + currentAction.transform.parent.gameObject.name);
}
EvaluateToPosition(currentAction.enterPositionXZCurve, currentAction.enterPositionYCurve, currentAction.matchTarget.position, tpInput.cc.baseLayerInfo.normalizedTime);
}
}
if (!_inEnterAnimation && inEnterAnimation)
{
enterStarted = false;
inEnterAnimation = false;
}
TriggerExit();
}
protected virtual void TriggerExit()
{
// exit ladder behaviour
inExitingAnimation = tpInput.cc.IsAnimatorTag("Exit");
if (inExitingAnimation)
{
tpInput.cc.animator.speed = 1;
if (currentAction.exitMatchTarget != null && !tpInput.cc.IsAnimatorTag("Exit"))
{
if (debugMode)
{
Debug.Log("Match Target to exit..." + currentAction.name + "_" + currentAction.transform.parent.gameObject.name);
}
EvaluateToPosition(currentAction.exitPositionXZCurve, currentAction.exitPositionYCurve, currentAction.exitMatchTarget.position, tpInput.cc.baseLayerInfo.normalizedTime);
}
var newRot = new Vector3(0, tpInput.animator.rootRotation.eulerAngles.y, 0);
EvaluateToRotation(currentAction.exitRotationCurve, Quaternion.Euler(newRot), tpInput.cc.baseLayerInfo.normalizedTime);
if (tpInput.cc.baseLayerInfo.normalizedTime >= 0.8f)
{
// after playing the animation we reset some values
ResetPlayerSettings();
}
}
}
protected virtual void EvaluateToPosition(AnimationCurve XZ, AnimationCurve Y, Vector3 targetPosition, float normalizedTime)
{
Vector3 rootPosition = tpInput.cc.animator.rootPosition;
float evaluatedXZ = XZ.Evaluate(normalizedTime);
float evaluatedY = Y.Evaluate(normalizedTime);
if (evaluatedXZ < 1f)
{
rootPosition.x = Mathf.Lerp(rootPosition.x, targetPosition.x, evaluatedXZ);
rootPosition.z = Mathf.Lerp(rootPosition.z, targetPosition.z, evaluatedXZ);
}
if (evaluatedY < 1f)
{
rootPosition.y = Mathf.Lerp(rootPosition.y, targetPosition.y, evaluatedY);
}
transform.position = rootPosition;
}
protected virtual void EvaluateToRotation(AnimationCurve curve, Quaternion targetRotation, float normalizedTime)
{
Quaternion rootRotation = tpInput.cc.animator.rootRotation;
float evaluatedCurve = curve.Evaluate(normalizedTime);
if (evaluatedCurve < 1)
{
rootRotation = Quaternion.Lerp(rootRotation, targetRotation, evaluatedCurve);
}
transform.rotation = rootRotation;
}
protected virtual void CheckForTriggerAction(Collider other)
{
// assign the component - it will be null when he exit the trigger area
var _Action = other.GetComponent<vTriggerFootbridgeAction>();
if (!_Action)
{
return;
}
// check the maxAngle too see if the character can do the action
var dist = Vector3.Distance(transform.forward, _Action.transform.forward);
if (isUsing && _Action != null)
{
if (targetAction != _Action)
{
targetAction = _Action;
if (!actionTriggers.Contains(targetAction))
{
actionTriggers.Add(targetAction);
}
}
}
else if ((_Action.activeFromForward == false || dist <= 0.8f) && !isUsing)
{
AddTrigger(_Action);
OnEnterTrigger.Invoke();
}
else
{
RemoveTrigger(_Action);
}
}
protected virtual void RemoveTrigger(vTriggerFootbridgeAction _Action)
{
if (_Action == targetAction)
{
targetAction = null;
}
if (actionTriggers.Contains(_Action))
{
actionTriggers.Remove(_Action);
_Action.OnPlayerExit.Invoke();
}
}
protected virtual void AddTrigger(vTriggerFootbridgeAction _Action)
{
if (targetAction != _Action)
{
targetAction = _Action;
if (debugMode)
{
Debug.Log("TriggerStay " + targetAction.name + "_" + targetAction.transform.parent.gameObject.name);
}
}
if (!actionTriggers.Contains(targetAction))
{
actionTriggers.Add(targetAction);
targetAction.OnPlayerEnter.Invoke();
}
}
public override void OnActionStay(Collider other)
{
if (other.gameObject.CompareTag(actionTag) && !enterStarted)
{
CheckForTriggerAction(other);
}
}
public override void OnActionExit(Collider other)
{
if (other.gameObject.CompareTag(actionTag))
{
var _ladderAction = other.GetComponent<vTriggerFootbridgeAction>();
if (!_ladderAction)
{
return;
}
RemoveTrigger(_ladderAction);
if (debugMode)
{
Debug.Log("TriggerExit " + other.name + "_" + other.transform.parent.gameObject.name);
}
OnExitTrigger.Invoke();
}
}
}
}