595 lines
16 KiB
C#
595 lines
16 KiB
C#
using UnityEngine;
|
|
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
|
|
public class handsOnSurfaceIKSystem : OnAnimatorIKComponent
|
|
{
|
|
[Header ("Main Settings")]
|
|
[Space]
|
|
|
|
public bool adjustHandsToSurfacesEnabled = true;
|
|
|
|
public LayerMask layerToAdjustHandsToSurfaces;
|
|
|
|
public float movementSpeedLerpSpeed = 2;
|
|
|
|
public float waitTimeAfterGroundToCheckSurface = 1.5f;
|
|
public float waitTimeAfterCrouchToCheckSurface = 1.5f;
|
|
|
|
public float weightLerpSpeed = 6;
|
|
|
|
public float maxHandsDistance = 3;
|
|
|
|
public float minMovingInputToUseWalkSpeed = 0.5f;
|
|
public float minIKWeightToUseWalkSpeed = 0.4f;
|
|
|
|
public bool pauseHandsOnSurfaceIfCloseCombatActive;
|
|
|
|
[Space]
|
|
[Header ("Hands Info List Settings")]
|
|
[Space]
|
|
|
|
public List<handsToSurfaceInfo> handsToSurfaceInfoList = new List<handsToSurfaceInfo> ();
|
|
|
|
[Space]
|
|
[Header ("Debug")]
|
|
[Space]
|
|
|
|
public bool usingHands;
|
|
public bool adjustHandsPaused;
|
|
|
|
public bool smoothBusyDisableActive;
|
|
|
|
public bool handsCheckPausedWithDelayActive;
|
|
|
|
public bool isBusy;
|
|
|
|
[Space]
|
|
[Header ("Gizmo Settings")]
|
|
[Space]
|
|
|
|
public bool showGizmo;
|
|
public Color gizmoColor = Color.white;
|
|
public float gizmoRadius;
|
|
|
|
[Space]
|
|
[Header ("Component Elements")]
|
|
[Space]
|
|
|
|
public Animator animator;
|
|
public IKSystem IKManager;
|
|
public playerController playerControllerManager;
|
|
public Transform playerTransform;
|
|
|
|
handsToSurfaceInfo currentHandInfo;
|
|
Vector3 rayDirection;
|
|
|
|
RaycastHit hit;
|
|
float movementSpeed;
|
|
float currentMovementSpeed;
|
|
|
|
bool isThirdPersonView;
|
|
|
|
bool onGround;
|
|
|
|
bool movingOnPlatformActive;
|
|
|
|
bool dead;
|
|
|
|
bool groundChecked;
|
|
float lastTimeOnGround;
|
|
|
|
bool crouchingChecked;
|
|
float lastTimeCrouch;
|
|
|
|
Vector3 currentPositionCenter;
|
|
|
|
float currentRotationSpeed;
|
|
|
|
float originalWeightLerpSpeed;
|
|
|
|
bool crouching;
|
|
|
|
Transform rightHandTransform;
|
|
Transform leftHandTransform;
|
|
|
|
bool isRightHand;
|
|
|
|
multipleRayInfo currentMultipleRayInfo;
|
|
|
|
Vector3 currentTransformUp;
|
|
|
|
Vector3 currentTransformForward;
|
|
|
|
Vector3 vector3Zero = Vector3.zero;
|
|
|
|
Quaternion targetRotation;
|
|
|
|
Vector3 currentHandPosition;
|
|
|
|
float currentWeightToApply;
|
|
|
|
AvatarIKGoal currentIKGoal;
|
|
|
|
int handsToSurfaceInfoListCount;
|
|
|
|
float minIKValue = 0.001f;
|
|
|
|
float currentDeltaTime;
|
|
|
|
Coroutine delayToResumeStateCoroutine;
|
|
|
|
|
|
void Start ()
|
|
{
|
|
originalWeightLerpSpeed = weightLerpSpeed;
|
|
|
|
rightHandTransform = animator.GetBoneTransform (HumanBodyBones.RightHand);
|
|
|
|
leftHandTransform = animator.GetBoneTransform (HumanBodyBones.LeftHand);
|
|
|
|
handsToSurfaceInfoListCount = handsToSurfaceInfoList.Count;
|
|
}
|
|
|
|
void FixedUpdate ()
|
|
{
|
|
if (!adjustHandsToSurfacesEnabled) {
|
|
return;
|
|
}
|
|
|
|
isThirdPersonView = !playerControllerManager.isPlayerOnFirstPerson ();
|
|
|
|
usingHands = IKManager.getUsingHands ();
|
|
|
|
onGround = playerControllerManager.isPlayerOnGround ();
|
|
|
|
movingOnPlatformActive = playerControllerManager.isMovingOnPlatformActive ();
|
|
|
|
isBusy = playerIsBusy ();
|
|
|
|
crouching = playerControllerManager.isCrouching ();
|
|
|
|
dead = playerControllerManager.isPlayerDead ();
|
|
|
|
if (groundChecked != onGround) {
|
|
groundChecked = onGround;
|
|
|
|
if (onGround) {
|
|
lastTimeOnGround = Time.time;
|
|
}
|
|
}
|
|
|
|
if (crouchingChecked != crouching) {
|
|
crouchingChecked = crouching;
|
|
|
|
if (!crouching) {
|
|
lastTimeCrouch = Time.time;
|
|
}
|
|
}
|
|
|
|
if (adjustHandsToSurfacesEnabled) {
|
|
if (!isBusy || smoothBusyDisableActive) {
|
|
|
|
currentTransformUp = playerTransform.up;
|
|
currentTransformForward = playerTransform.forward;
|
|
|
|
for (int j = 0; j < handsToSurfaceInfoListCount; j++) {
|
|
|
|
currentHandInfo = handsToSurfaceInfoList [j];
|
|
|
|
currentHandInfo.surfaceFound = false;
|
|
currentHandInfo.multipleRaycastHitNormal = vector3Zero;
|
|
currentHandInfo.multipleRaycastHitPoint = vector3Zero;
|
|
currentHandInfo.numberOfSurfacesFound = 0;
|
|
currentHandInfo.multipleRaycastDirection = vector3Zero;
|
|
|
|
for (int k = 0; k < currentHandInfo.raycastTransformList.Count; k++) {
|
|
currentMultipleRayInfo = currentHandInfo.raycastTransformList [k];
|
|
|
|
rayDirection = currentMultipleRayInfo.raycastTransform.forward;
|
|
|
|
if (currentMultipleRayInfo.raycastEnabled &&
|
|
Physics.Raycast (currentMultipleRayInfo.raycastTransform.position, rayDirection, out hit,
|
|
currentMultipleRayInfo.rayCastDistance, layerToAdjustHandsToSurfaces)) {
|
|
|
|
currentMultipleRayInfo.hitPoint = hit.point;
|
|
currentMultipleRayInfo.hitNormal = hit.normal;
|
|
currentHandInfo.surfaceFound = true;
|
|
currentHandInfo.multipleRaycastHitNormal += hit.normal;
|
|
currentHandInfo.multipleRaycastHitPoint += hit.point;
|
|
|
|
currentHandInfo.numberOfSurfacesFound++;
|
|
currentHandInfo.multipleRaycastDirection += rayDirection;
|
|
|
|
if (showGizmo) {
|
|
Debug.DrawRay (currentMultipleRayInfo.raycastTransform.position, hit.distance * rayDirection, Color.green);
|
|
}
|
|
} else {
|
|
if (showGizmo) {
|
|
Debug.DrawRay (currentMultipleRayInfo.raycastTransform.position,
|
|
currentMultipleRayInfo.rayCastDistance * rayDirection, Color.red);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (smoothBusyDisableActive) {
|
|
currentHandInfo.surfaceFound = false;
|
|
|
|
if (currentWeightInBothHandsEqualTo (0)) {
|
|
smoothBusyDisableActive = false;
|
|
}
|
|
}
|
|
|
|
if (currentHandInfo.surfaceFound) {
|
|
currentHandInfo.handPosition =
|
|
(currentHandInfo.multipleRaycastHitPoint / currentHandInfo.numberOfSurfacesFound) +
|
|
(currentHandInfo.handSurfaceOffset * currentHandInfo.multipleRaycastDirection / currentHandInfo.numberOfSurfacesFound);
|
|
|
|
targetRotation = Quaternion.LookRotation (currentHandInfo.multipleRaycastDirection / currentHandInfo.numberOfSurfacesFound);
|
|
|
|
currentHandInfo.handRotation =
|
|
Quaternion.FromToRotation (currentTransformUp, currentHandInfo.multipleRaycastHitNormal / currentHandInfo.numberOfSurfacesFound) * targetRotation;
|
|
|
|
currentHandInfo.elbowPosition =
|
|
((currentHandInfo.multipleRaycastHitPoint / currentHandInfo.numberOfSurfacesFound) + currentHandInfo.raycastTransform.position) / 2 - currentTransformUp * 0.1f;
|
|
|
|
currentHandInfo.handWeight = 1;
|
|
|
|
if (currentHandInfo.useMinDistance) {
|
|
currentHandInfo.currentDistance = GKC_Utils.distance (currentHandInfo.raycastTransform.position, currentHandInfo.handPosition);
|
|
|
|
if (currentHandInfo.currentDistance < currentHandInfo.minDistance) {
|
|
currentHandInfo.surfaceFound = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (handsCheckPausedWithDelayActive ||
|
|
!currentHandInfo.surfaceFound ||
|
|
movingOnPlatformActive ||
|
|
!onGround ||
|
|
Time.time < waitTimeAfterGroundToCheckSurface + lastTimeOnGround ||
|
|
crouching ||
|
|
Time.time < waitTimeAfterCrouchToCheckSurface + lastTimeCrouch ||
|
|
dead) {
|
|
|
|
currentHandInfo.handPosition = currentHandInfo.noSurfaceHandPosition.position;
|
|
currentHandInfo.handRotation = Quaternion.LookRotation (currentTransformForward);
|
|
|
|
currentHandInfo.elbowPosition = currentHandInfo.noSurfaceElbowPosition.position;
|
|
|
|
currentHandInfo.handWeight = 0;
|
|
}
|
|
|
|
if (playerControllerManager.isPlayerMoving (minMovingInputToUseWalkSpeed) &&
|
|
currentHandInfo.surfaceFound && currentHandInfo.currentHandWeight > minIKWeightToUseWalkSpeed) {
|
|
movementSpeed = currentHandInfo.walkingMovementSpeed;
|
|
} else {
|
|
movementSpeed = currentHandInfo.movementSpeed;
|
|
}
|
|
|
|
currentDeltaTime = Time.deltaTime;
|
|
|
|
currentMovementSpeed = Mathf.Lerp (currentMovementSpeed, movementSpeed, currentDeltaTime * movementSpeedLerpSpeed);
|
|
|
|
currentHandInfo.currentHandPosition = Vector3.Lerp (currentHandInfo.currentHandPosition, currentHandInfo.handPosition, currentDeltaTime * currentMovementSpeed);
|
|
|
|
currentRotationSpeed = currentDeltaTime * currentHandInfo.rotationSpeed;
|
|
|
|
if (currentRotationSpeed > 0) {
|
|
currentHandInfo.currentHandRotation = Quaternion.Lerp (currentHandInfo.currentHandRotation, currentHandInfo.handRotation, currentRotationSpeed);
|
|
}
|
|
|
|
currentHandInfo.currentElbowPosition = Vector3.Lerp (currentHandInfo.currentElbowPosition, currentHandInfo.elbowPosition, currentDeltaTime * currentMovementSpeed);
|
|
|
|
currentHandInfo.currentHandWeight = Mathf.Lerp (currentHandInfo.currentHandWeight, currentHandInfo.handWeight, currentDeltaTime * weightLerpSpeed);
|
|
|
|
if (currentHandInfo.currentHandWeight < 0.01f) {
|
|
currentHandInfo.handPosition = currentHandInfo.noSurfaceHandPosition.position;
|
|
currentHandInfo.handRotation = Quaternion.LookRotation (currentTransformForward);
|
|
|
|
currentHandInfo.elbowPosition = currentHandInfo.noSurfaceElbowPosition.position;
|
|
|
|
currentHandInfo.currentHandPosition = currentHandInfo.handPosition;
|
|
|
|
currentHandInfo.currentHandRotation = currentHandInfo.handRotation;
|
|
|
|
currentHandInfo.currentElbowPosition = currentHandInfo.elbowPosition;
|
|
}
|
|
}
|
|
} else {
|
|
|
|
currentTransformForward = playerTransform.forward;
|
|
|
|
for (int j = 0; j < handsToSurfaceInfoListCount; j++) {
|
|
|
|
currentHandInfo = handsToSurfaceInfoList [j];
|
|
|
|
currentHandInfo.surfaceFound = false;
|
|
|
|
currentHandInfo.handWeight = 0;
|
|
|
|
currentHandInfo.currentHandWeight = 0;
|
|
currentHandInfo.handPosition = currentHandInfo.noSurfaceHandPosition.position;
|
|
currentHandInfo.handRotation = Quaternion.LookRotation (currentTransformForward);
|
|
|
|
currentHandInfo.elbowPosition = currentHandInfo.noSurfaceElbowPosition.position;
|
|
|
|
currentHandInfo.currentHandPosition = currentHandInfo.handPosition;
|
|
|
|
currentHandInfo.currentHandRotation = currentHandInfo.handRotation;
|
|
|
|
currentHandInfo.currentElbowPosition = currentHandInfo.elbowPosition;
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
public bool currentWeightInBothHandsEqualTo (float weightValue)
|
|
{
|
|
int numberOfHands = 0;
|
|
|
|
for (int j = 0; j < handsToSurfaceInfoListCount; j++) {
|
|
if (handsToSurfaceInfoList [j].currentHandWeight == weightValue) {
|
|
numberOfHands++;
|
|
}
|
|
}
|
|
|
|
if (numberOfHands == 2) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public override void updateOnAnimatorIKState ()
|
|
{
|
|
if (adjustHandsToSurfacesEnabled) {
|
|
|
|
if (!isBusy || smoothBusyDisableActive) {
|
|
|
|
currentPositionCenter = playerTransform.position + playerTransform.up;
|
|
|
|
for (int j = 0; j < handsToSurfaceInfoListCount; j++) {
|
|
|
|
currentHandInfo = handsToSurfaceInfoList [j];
|
|
|
|
isRightHand = currentHandInfo.isRightHand;
|
|
|
|
currentHandPosition = currentHandInfo.currentHandPosition;
|
|
|
|
if (float.IsNaN (currentHandPosition.x)) {
|
|
if (isRightHand) {
|
|
currentHandPosition.x = rightHandTransform.position.x;
|
|
} else {
|
|
currentHandPosition.x = leftHandTransform.position.x;
|
|
}
|
|
}
|
|
|
|
if (float.IsNaN (currentHandPosition.y)) {
|
|
if (isRightHand) {
|
|
currentHandPosition.y = rightHandTransform.position.y;
|
|
} else {
|
|
currentHandPosition.y = leftHandTransform.position.y;
|
|
}
|
|
}
|
|
|
|
if (float.IsNaN (currentHandPosition.z)) {
|
|
if (isRightHand) {
|
|
currentHandPosition.z = rightHandTransform.position.z;
|
|
} else {
|
|
currentHandPosition.z = leftHandTransform.position.z;
|
|
}
|
|
}
|
|
|
|
currentHandPosition.x =
|
|
Mathf.Clamp (currentHandPosition.x, currentPositionCenter.x - maxHandsDistance, currentPositionCenter.x + maxHandsDistance);
|
|
currentHandPosition.y =
|
|
Mathf.Clamp (currentHandPosition.y, currentPositionCenter.y - maxHandsDistance, currentPositionCenter.y + maxHandsDistance);
|
|
currentHandPosition.z =
|
|
Mathf.Clamp (currentHandPosition.z, currentPositionCenter.z - maxHandsDistance, currentPositionCenter.z + maxHandsDistance);
|
|
|
|
currentHandInfo.currentHandPosition = currentHandPosition;
|
|
|
|
|
|
currentWeightToApply = currentHandInfo.currentHandWeight;
|
|
|
|
if (currentWeightToApply > minIKValue) {
|
|
|
|
currentIKGoal = currentHandInfo.IKGoal;
|
|
|
|
animator.SetIKRotationWeight (currentIKGoal, currentWeightToApply);
|
|
animator.SetIKPositionWeight (currentIKGoal, currentWeightToApply);
|
|
|
|
animator.SetIKPosition (currentIKGoal, currentHandInfo.currentHandPosition);
|
|
animator.SetIKRotation (currentIKGoal, currentHandInfo.currentHandRotation);
|
|
|
|
animator.SetIKHintPositionWeight (currentHandInfo.IKHint, currentWeightToApply);
|
|
animator.SetIKHintPosition (currentHandInfo.IKHint, currentHandInfo.currentElbowPosition);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public bool playerIsBusy ()
|
|
{
|
|
return
|
|
|
|
usingHands ||
|
|
!isThirdPersonView ||
|
|
playerControllerManager.isPlayerOnFFOrZeroGravityModeOn () ||
|
|
(playerControllerManager.isUsingCloseCombatActive () && pauseHandsOnSurfaceIfCloseCombatActive) ||
|
|
playerControllerManager.iscloseCombatAttackInProcess () ||
|
|
playerControllerManager.isActionActive () ||
|
|
adjustHandsPaused ||
|
|
playerControllerManager.isUsingJetpack () ||
|
|
playerControllerManager.isFlyingActive () ||
|
|
playerControllerManager.isSwimModeActive () ||
|
|
playerControllerManager.isCarryingPhysicalObject ();
|
|
}
|
|
|
|
public void setWeightLerpSpeedValue (float newValue)
|
|
{
|
|
weightLerpSpeed = newValue;
|
|
}
|
|
|
|
public void setOriginalWeightLerpSpeed ()
|
|
{
|
|
setWeightLerpSpeedValue (originalWeightLerpSpeed);
|
|
}
|
|
|
|
public void setAdjustHandsPausedState (bool state)
|
|
{
|
|
adjustHandsPaused = state;
|
|
}
|
|
|
|
public void setSmoothBusyDisableActiveState (bool state)
|
|
{
|
|
smoothBusyDisableActive = state;
|
|
}
|
|
|
|
public void setAdjustHandsToSurfacesEnabledState (bool state)
|
|
{
|
|
adjustHandsToSurfacesEnabled = state;
|
|
}
|
|
|
|
|
|
public void enableHandsCheckPausedWithDelayActive (float duration)
|
|
{
|
|
stopUpdateDelayToResumeStateCoroutine ();
|
|
|
|
delayToResumeStateCoroutine = StartCoroutine (updateDelayToResumeStateCoroutine (duration));
|
|
}
|
|
|
|
public void stopUpdateDelayToResumeStateCoroutine ()
|
|
{
|
|
if (delayToResumeStateCoroutine != null) {
|
|
StopCoroutine (delayToResumeStateCoroutine);
|
|
}
|
|
|
|
handsCheckPausedWithDelayActive = false;
|
|
}
|
|
|
|
IEnumerator updateDelayToResumeStateCoroutine (float duration)
|
|
{
|
|
handsCheckPausedWithDelayActive = true;
|
|
|
|
WaitForSeconds delay = new WaitForSeconds (duration);
|
|
|
|
yield return delay;
|
|
|
|
handsCheckPausedWithDelayActive = false;
|
|
}
|
|
|
|
|
|
//EDITOR FUNCTIONS
|
|
public void setAdjustHandsToSurfacesEnabledStateFromEditor (bool state)
|
|
{
|
|
setAdjustHandsToSurfacesEnabledState (state);
|
|
|
|
updateComponent ();
|
|
}
|
|
|
|
void updateComponent ()
|
|
{
|
|
GKC_Utils.updateComponent (this);
|
|
}
|
|
|
|
#if UNITY_EDITOR
|
|
void OnDrawGizmos ()
|
|
{
|
|
if (!showGizmo) {
|
|
return;
|
|
}
|
|
|
|
if (GKC_Utils.isCurrentSelectionActiveGameObject (gameObject)) {
|
|
DrawGizmos ();
|
|
}
|
|
}
|
|
|
|
void OnDrawGizmosSelected ()
|
|
{
|
|
DrawGizmos ();
|
|
}
|
|
|
|
void DrawGizmos ()
|
|
{
|
|
if (showGizmo) {
|
|
for (int j = 0; j < handsToSurfaceInfoList.Count; j++) {
|
|
Gizmos.color = Color.green;
|
|
Gizmos.DrawSphere (handsToSurfaceInfoList [j].handPosition, gizmoRadius);
|
|
|
|
Gizmos.color = Color.yellow;
|
|
Gizmos.DrawSphere (handsToSurfaceInfoList [j].elbowPosition, gizmoRadius);
|
|
|
|
Gizmos.color = gizmoColor;
|
|
Gizmos.DrawSphere (handsToSurfaceInfoList [j].currentHandPosition, gizmoRadius);
|
|
Gizmos.DrawSphere (handsToSurfaceInfoList [j].currentElbowPosition, gizmoRadius);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
[System.Serializable]
|
|
public class handsToSurfaceInfo
|
|
{
|
|
public string Name;
|
|
public Transform raycastTransform;
|
|
|
|
public bool useMultipleRaycast;
|
|
public List<multipleRayInfo> raycastTransformList = new List<multipleRayInfo> ();
|
|
|
|
public AvatarIKHint IKHint;
|
|
[HideInInspector] public Vector3 elbowPosition;
|
|
[HideInInspector] public Vector3 currentElbowPosition;
|
|
|
|
public bool isRightHand;
|
|
public AvatarIKGoal IKGoal;
|
|
public float handSurfaceOffset;
|
|
public float rayCastDistance;
|
|
|
|
public float currentHandWeight;
|
|
|
|
public float movementSpeed;
|
|
public float rotationSpeed;
|
|
|
|
public float walkingMovementSpeed;
|
|
|
|
[HideInInspector] public bool surfaceFound;
|
|
|
|
public bool useMinDistance;
|
|
public float minDistance;
|
|
|
|
[HideInInspector]public float currentDistance;
|
|
|
|
[HideInInspector] public Vector3 multipleRaycastDirection;
|
|
|
|
[HideInInspector] public int numberOfSurfacesFound;
|
|
[HideInInspector] public Vector3 multipleRaycastHitNormal;
|
|
[HideInInspector] public Vector3 multipleRaycastHitPoint;
|
|
|
|
[HideInInspector] public float handWeight;
|
|
[HideInInspector] public Vector3 handPosition;
|
|
[HideInInspector] public Quaternion handRotation;
|
|
|
|
[HideInInspector] public Vector3 currentHandPosition;
|
|
[HideInInspector] public Quaternion currentHandRotation;
|
|
|
|
public Transform noSurfaceHandPosition;
|
|
public Transform noSurfaceElbowPosition;
|
|
}
|
|
|
|
[System.Serializable]
|
|
public class multipleRayInfo
|
|
{
|
|
public Transform raycastTransform;
|
|
public bool surfaceFound;
|
|
[HideInInspector] public Vector3 hitPoint;
|
|
[HideInInspector] public Vector3 hitNormal;
|
|
public float rayCastDistance;
|
|
public bool raycastEnabled = true;
|
|
}
|
|
} |