Files
Robii Aragon fd87a6ffd5 add ckg
plantilla base para movimiento básico
2026-02-05 05:07:55 -08:00

2228 lines
77 KiB
C#

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
public class climbLedgeSystem : MonoBehaviour
{
[Header ("Main Settings")]
[Space]
public bool climbLedgeActive;
public bool useHangFromLedgeIcon;
public bool useFixedDeviceIconPosition;
public bool keepWeaponsOnLedgeDetected;
public bool drawWeaponsAfterClimbLedgeIfPreviouslyCarried;
[Space]
[Header ("Climb Animator Settings")]
[Space]
public int climbLedgeActionID = 1;
public string holdOnLedgeActionName = "Hold On Ledge";
public string actionActiveAnimatorName = "Action Active";
public string actionIDAnimatorName = "Action ID";
public float matchStartValue;
public float matchEndValue;
public Vector3 matchMaskValue;
public float matchMaskRotationValue;
public int baseLayerIndex = 0;
[Space]
[Header ("Raycast Ledge Detection Settings")]
[Space]
public float climbLedgeRayForwardDistance = 1;
public float climbLedgeRayDownDistance = 1;
public LayerMask layerMaskToCheck;
[Space]
[Header ("Other Settings")]
[Space]
public bool onlyGrabLedgeIfMovingForward;
public float minWaitToClimbAfterHoldOnLedge = 0.6f;
public float adjustToHoldOnLedgePositionSpeed = 3;
public float adjustToHoldOnLedgeRotationSpeed = 10;
public Vector3 holdOnLedgeOffset;
public Vector3 holdOnLedgeOffsetFBA = new Vector3 (0, -1.65f, -0.8f);
[Space]
public Vector3 climbLedgeTargetPositionOffsetThirdPerson;
public Vector3 climdLedgeTargetPositionOffsetFirstPerson;
public Vector3 climdLedgeTargetPositionOffsetFirstFBA = new Vector3 (0, 0.64f, 0.15f);
[Space]
public float handOffset = 0.2f;
public float timeToClimbLedgeThirdPerson = 2;
public float timeToClimbLedgeFirstPerson = 1;
public float climbLedgeSpeedFirstPerson = 1;
public bool climbIfSurfaceFoundBelowPlayer;
public bool checkForLedgeZonesActive = true;
[Space]
[Header ("Ledge Below Check Settings")]
[Space]
public bool checkForHangFromLedgeOnGround;
public bool checkLedgeZoneDetectedByRaycast;
public float raycastRadiusToCheckSurfaceBelowPlayer;
public float checkForHangFromLedgeOnGroundRaycastDistance;
public bool onlyHangFromLedgeIfPlayerIsNotMoving;
public float timeToCancelHangFromLedgeIfNotFound = 3;
public bool canCancelHangFromLedge;
public bool hasToLookAtLedgePositionOnFirstPerson;
public bool useMaxDistanceToCameraCenter;
public float maxDistanceToCameraCenter;
[Space]
[Header ("Auto Climb Ledge Settings")]
[Space]
public bool autoClimbInThirdPerson;
public bool autoClimbInFirstPerson;
[Space]
[Header ("Jump Settings")]
[Space]
public bool canJumpWhenHoldLedge;
public float jumpForceWhenHoldLedge = 10;
public ForceMode jumpForceMode;
[Space]
[Header ("Grab Surface On Air Settings")]
[Space]
public bool canGrabToSurfaceOnAirInputEnabled;
public bool canGrabAnySurfaceOnAir;
public bool useGrabSurfaceAmountLimit;
public int grabSurfaceAmountLimit;
public int currentGrabSurfaceAmount;
[Space]
public bool useSurfacesToCheckTagsOnGrabSurfaceByInput;
public List<string> surfacesToCheckTagsGrabSurfaceByInputList = new List<string> ();
[Space]
public bool checkMoveInputDirectionOnJump;
public float jumpRotationSpeedThirdPerson = 1;
public float jumpRotationSpeedFirstPerson = 0.5f;
public float jumpForceWhenHoldLedgeOnOppositeDirection = 15;
[Space]
[Header ("Event Settings")]
[Space]
public bool useEvents;
public UnityEvent eventOnLedgeGrabbed;
public UnityEvent eventOnLedgeLose;
public UnityEvent eventOnLedgeClimbed;
public UnityEvent eventOnStartLedgeClimb;
public UnityEvent eventOnLedgeJump;
[Space]
[Header ("Gizmo Settings")]
[Space]
public bool showGizmo;
public Color gizmoColor;
public float gizmoRadius;
[Space]
[Header ("Debug")]
[Space]
public bool showDebugPrint;
public bool avoidPlayerGrabLedge;
public bool ledgeZoneFound;
public bool activateClimbAction;
public bool canStartToClimbLedge;
public bool climbingLedge;
public bool canUseClimbLedge;
public bool canClimbCurrentLedgeZone = true;
public bool stopGrabLedge;
public float directionAngle;
public bool surfaceToHangOnGroundFound;
public bool movingTowardSurfaceToHang;
public bool previouslyMovingTowardSurfaceToHang;
public bool onAirWhileSearchingLedgeToHang;
public bool ledgeZoneCloseEnoughToScreenCenter;
public float currentDistanceToTarget;
public bool canCheckForHangFromLedgeOnGround = true;
public bool climbLedgeActionActivated;
public bool loseLedgeActionActivated;
public bool grabbingSurface;
public bool climbLedgeActionPaused;
public bool otherClimbActionActive;
[Space]
[Header ("Climb Elements")]
[Space]
public Transform playerTransform;
public Transform climbLedgeRayPosition;
public Transform climbLedgeRayDownPosition;
public Transform holdLedgeZone;
public Transform climbLedgZone;
public Transform hangFromLedgeOnGroundRaycastPosition;
public GameObject hangFromLedgeIcon;
public RectTransform hangFromLedgeIconRectTransform;
[Space]
[Header ("Components")]
[Space]
public playerController playerControllerManager;
public playerInputManager playerInput;
public playerWeaponsManager weaponsManager;
public otherPowers powersManager;
public headBob headBobManager;
public gravitySystem gravityManager;
public Camera mainCamera;
public playerCamera mainPlayerCamera;
public Rigidbody mainRigidbody;
public Animator animator;
public Collider MainCollider;
public usingDevicesSystem usingDevicesManager;
bool canGrabAnySurfaceOnAirActive = true;
bool groundChecked;
float lastTimeOnGround;
Vector3 surfaceFoundNormal;
RaycastHit hit;
Coroutine holdOnLedgeCoroutine;
Coroutine climbLedgeOnFirstPersonCoroutine;
float lastTimeHoldOnEdge;
float lastTimeLedgeChecked;
bool playerOnGround;
bool carryingWeaponsPreviously;
bool pauseAllPlayerDownForces;
bool wallRunningActive;
bool freeClimbActive;
bool swimModeActive;
bool flyModeActive;
bool waitingForMinTimeToCheckForLedge;
Vector3 currentRayPosition;
Vector3 currentRayDirection;
Vector3 playerTargetRotation;
bool firstPersonActive;
float lastTimeClimbLedge;
float originalClimbLedgeRayForwardDistance;
float originalClimbLedgeRayDownDistance;
Transform previousParent;
parentAssignedSystem currentParentAssignedSystem;
Ray ray;
bool surfaceBelowPlayerDetected;
Vector3 checkDownRaycastOffset;
GameObject currentSurfaceFound;
GameObject previousSurfaceFound;
GameObject currentSurfaceFoundBelow;
GameObject previousSurfaceFoundBelow;
bool stopHangFromLedgeAction;
float lastTimeVerticalInputPressed;
bool movementInputPressed;
bool movementInputReleased;
float lastTimeMovingTowardSurfaceToHang;
float lastTimeLedgeToHangFound;
Vector3 surfaceToHangNormal;
Vector3 screenPoint;
Vector2 iconPosition2d;
bool usingScreenSpaceCamera;
Vector2 mainCanvasSizeDelta;
Vector2 halfMainCanvasSizeDelta;
bool targetOnScreen;
Vector3 currentIconPosition;
Vector3 originalDeviceIconPosition;
Vector3 lastMovementDirection;
Vector2 rawMovementAxis;
Coroutine rotateToLedgeOnFirstPersonCoroutine;
bool rotationToLedgeOnFirstPersonActive;
bool playerRotatedToLedgeOnFirstPerson;
bool originalOnlyHangFromLedgeIfPlayerIsNotMovingValue;
int actionActiveAnimatorID;
int actionIDAnimatorID;
float screenWidth;
float screenHeight;
Coroutine climbLedgeActionPausedCoroutine;
bool grabToSurfaceInputPaused;
bool callingActivateGrabToSurfaceActionExternally;
Transform newLedgeTransformToGrabAutomatically;
Vector3 currentDirectionToRotatePlayer;
void Start ()
{
actionActiveAnimatorID = Animator.StringToHash (actionActiveAnimatorName);
actionIDAnimatorID = Animator.StringToHash (actionIDAnimatorName);
climbLedgZone.SetParent (null);
holdLedgeZone.SetParent (null);
originalClimbLedgeRayForwardDistance = climbLedgeRayForwardDistance;
originalClimbLedgeRayDownDistance = climbLedgeRayDownDistance;
mainCanvasSizeDelta = mainPlayerCamera.getMainCanvasSizeDelta ();
halfMainCanvasSizeDelta = mainCanvasSizeDelta * 0.5f;
usingScreenSpaceCamera = mainPlayerCamera.isUsingScreenSpaceCamera ();
originalDeviceIconPosition = hangFromLedgeIconRectTransform.position;
originalOnlyHangFromLedgeIfPlayerIsNotMovingValue = onlyHangFromLedgeIfPlayerIsNotMoving;
if (playerTransform == null) {
playerTransform = transform;
}
if (mainCamera == null) {
mainCamera = mainPlayerCamera.getMainCamera ();
}
}
void FixedUpdate ()
{
if (!climbLedgeActive) {
return;
}
if (avoidPlayerGrabLedge) {
return;
}
if (!canUseClimbLedge) {
return;
}
if (climbLedgeActionPaused) {
return;
}
if (otherClimbActionActive) {
return;
}
if (playerControllerManager.isIgnoreExternalActionsActiveState ()) {
return;
}
pauseAllPlayerDownForces = playerControllerManager.isPauseAllPlayerDownForcesActive ();
if (pauseAllPlayerDownForces) {
return;
}
wallRunningActive = playerControllerManager.isWallRunningActive ();
swimModeActive = playerControllerManager.isSwimModeActive ();
freeClimbActive = playerControllerManager.isFreeClimbActive ();
flyModeActive = playerControllerManager.isFlyingActive ();
if (lastTimeClimbLedge == 0) {
waitingForMinTimeToCheckForLedge = false;
} else {
waitingForMinTimeToCheckForLedge = (Time.time < lastTimeClimbLedge + 0.7f);
}
if (wallRunningActive) {
return;
}
if (swimModeActive) {
return;
}
if (freeClimbActive) {
return;
}
if (flyModeActive) {
return;
}
if (waitingForMinTimeToCheckForLedge) {
return;
}
//the auto hang from ledge is active and in process
if (checkForHangFromLedgeOnGround) {
//override player input and make him move toward the position to hang from the ledge detected
if (movingTowardSurfaceToHang) {
if (playerOnGround) {
playerInput.overrideInputValues (new Vector2 (0, -1), true);
if (onAirWhileSearchingLedgeToHang) {
stopHangFromLedge ();
}
} else {
onAirWhileSearchingLedgeToHang = true;
if (!climbingLedge) {
if (firstPersonActive) {
if (!playerRotatedToLedgeOnFirstPerson) {
rotateTowardLedgeOnFirstPerson ();
}
if (!rotationToLedgeOnFirstPersonActive) {
playerInput.overrideInputValues (new Vector2 (0, 1), true);
}
} else {
playerInput.overrideInputValues (new Vector2 (0, 1), true);
}
} else {
stopHangFromLedge ();
}
}
//if the auto hang from ledge time is over of the player moves while he is moving automatically, cancel the auto hang action
if (Time.time > lastTimeMovingTowardSurfaceToHang + timeToCancelHangFromLedgeIfNotFound ||
(canCancelHangFromLedge && onlyHangFromLedgeIfPlayerIsNotMoving && Time.time > lastTimeMovingTowardSurfaceToHang + 0.5f && playerInput.getAuxRawMovementAxis () != Vector2.zero)) {
stopHangFromLedge ();
}
} else {
//if the player only hangs from the ledge when he is not moving and he is on the air, cancel the auto hang action
if (onlyHangFromLedgeIfPlayerIsNotMoving && surfaceToHangOnGroundFound) {
if (!playerOnGround) {
stopHangFromLedge ();
return;
}
}
}
}
}
void Update ()
{
if (climbLedgeActive) {
pauseAllPlayerDownForces = playerControllerManager.isPauseAllPlayerDownForcesActive ();
if (pauseAllPlayerDownForces ||
wallRunningActive ||
swimModeActive ||
freeClimbActive ||
flyModeActive ||
waitingForMinTimeToCheckForLedge ||
playerControllerManager.isIgnoreExternalActionsActiveState () ||
climbLedgeActionPaused) {
if (surfaceToHangOnGroundFound) {
stopHangFromLedge ();
}
return;
}
playerOnGround = playerControllerManager.isPlayerOnGround ();
firstPersonActive = playerControllerManager.isPlayerOnFirstPerson ();
if (groundChecked != playerOnGround) {
groundChecked = playerOnGround;
if (playerOnGround) {
currentGrabSurfaceAmount = 0;
}
}
}
//if the player is climbing a ledge, then
if (climbLedgeActive && climbingLedge) {
//match animation with player controller position
if (!firstPersonActive) {
if (playerControllerManager.getCurrentNormal () == Vector3.up) {
if (Time.time < lastTimeClimbLedge + timeToClimbLedgeThirdPerson) {
MatchTarget (climbLedgZone.position + handOffset * playerTransform.forward,
climbLedgZone.rotation,
AvatarTarget.RightHand,
new MatchTargetWeightMask (matchMaskValue, matchMaskRotationValue),
matchStartValue,
matchEndValue);
}
}
}
//check to disable the climb action and resume the player state once the ledge is climbed, with different values according to camera view, first or third person
if (activateClimbAction) {
if (canStartToClimbLedge) {
if (firstPersonActive) {
if (Time.time > (lastTimeClimbLedge + timeToClimbLedgeFirstPerson)) {
resumePlayer ();
return;
}
} else {
if (Time.time > (lastTimeClimbLedge + timeToClimbLedgeThirdPerson)) {
resumePlayer ();
return;
}
}
}
} else {
//prevent the player to rotate due to current idle animation
if (!firstPersonActive) {
playerTransform.eulerAngles = playerTargetRotation;
}
}
//if the player is hanging from the ledge, then
if (!activateClimbAction) {
if (Time.time > lastTimeHoldOnEdge + minWaitToClimbAfterHoldOnLedge) {
//if some object is detected below the player, activate automatically the climb to avoid that object from moving through the player
if (climbIfSurfaceFoundBelowPlayer) {
ray = new Ray (playerTransform.position, -playerTransform.up);
if (Physics.SphereCast (ray, raycastRadiusToCheckSurfaceBelowPlayer, raycastRadiusToCheckSurfaceBelowPlayer, layerMaskToCheck)) {
surfaceBelowPlayerDetected = true;
} else {
surfaceBelowPlayerDetected = false;
}
}
//check for movement input, so if the direction of the movement is the same direction of the player forward direction accoding to camera facing direction, then
rawMovementAxis = playerInput.getPlayerRawMovementAxis ();
if (rawMovementAxis != Vector2.zero) {
Vector3 movementDirection = rawMovementAxis.y * playerControllerManager.getCurrentForwardDirection () + rawMovementAxis.x * playerControllerManager.getCurrentRightDirection ();
directionAngle = Vector3.SignedAngle (movementDirection, playerTransform.forward, playerTransform.up);
if (Mathf.Abs (directionAngle) < 90) {
if (canClimbCurrentLedgeZone) {
climbLedgeActionActivated = true;
}
} else {
loseLedgeActionActivated = true;
}
}
if (canClimbCurrentLedgeZone) {
if (firstPersonActive) {
if (autoClimbInFirstPerson) {
climbLedgeActionActivated = true;
}
} else {
if (autoClimbInThirdPerson) {
climbLedgeActionActivated = true;
}
}
}
//climb the ledge
if (climbLedgeActionActivated || surfaceBelowPlayerDetected) {
startClimbLedgeAction ();
} else if (loseLedgeActionActivated) {
//lose the ledge
startLoseLedgeAction ();
}
}
}
}
//if the option to auto hang from ledge is active and only when the player is not moving, then
if (playerOnGround && checkForHangFromLedgeOnGround && surfaceToHangOnGroundFound && !movingTowardSurfaceToHang && onlyHangFromLedgeIfPlayerIsNotMoving) {
//place an UI element which follows the position of the ledge found
if (useHangFromLedgeIcon) {
currentIconPosition = holdLedgeZone.position;
if (!usingScreenSpaceCamera) {
screenWidth = Screen.width;
screenHeight = Screen.height;
}
if (usingScreenSpaceCamera) {
screenPoint = mainCamera.WorldToViewportPoint (currentIconPosition);
targetOnScreen = screenPoint.z > 0 && screenPoint.x > 0 && screenPoint.x < 1 && screenPoint.y > 0 && screenPoint.y < 1;
} else {
screenPoint = mainCamera.WorldToScreenPoint (currentIconPosition);
targetOnScreen = screenPoint.z > 0 && screenPoint.x > 0 && screenPoint.x < screenWidth && screenPoint.y > 0 && screenPoint.y < screenHeight;
}
if (targetOnScreen) {
if (useFixedDeviceIconPosition) {
hangFromLedgeIcon.transform.position = originalDeviceIconPosition;
} else {
if (usingScreenSpaceCamera) {
iconPosition2d = new Vector2 ((screenPoint.x * mainCanvasSizeDelta.x) - halfMainCanvasSizeDelta.x,
(screenPoint.y * mainCanvasSizeDelta.y) - halfMainCanvasSizeDelta.y);
hangFromLedgeIconRectTransform.anchoredPosition = iconPosition2d;
} else {
hangFromLedgeIcon.transform.position = new Vector3 (screenPoint.x, screenPoint.y, 0);
}
}
enableOrDisableHangFromLedgeIcon (true);
} else {
enableOrDisableHangFromLedgeIcon (false);
}
}
if (hasToLookAtLedgePositionOnFirstPerson && firstPersonActive) {
if (targetOnScreen) {
if (useMaxDistanceToCameraCenter) {
currentDistanceToTarget = GKC_Utils.distance (hangFromLedgeIconRectTransform.localPosition, Vector3.zero);
if (currentDistanceToTarget < maxDistanceToCameraCenter) {
ledgeZoneCloseEnoughToScreenCenter = true;
} else {
ledgeZoneCloseEnoughToScreenCenter = false;
}
} else {
ledgeZoneCloseEnoughToScreenCenter = true;
}
} else {
ledgeZoneCloseEnoughToScreenCenter = false;
}
enableOrDisableHangFromLedgeIcon (ledgeZoneCloseEnoughToScreenCenter);
}
}
if (!climbLedgeActive) {
return;
}
if (avoidPlayerGrabLedge) {
return;
}
//check if the player can use the climb ledge system right now
canUseClimbLedge = !playerControllerManager.isPlayerDead () &&
!playerControllerManager.isPlayerDriving () &&
!playerControllerManager.isPlayerRotatingToSurface () &&
!playerControllerManager.isPlayerOnFFOrZeroGravityModeOn () &&
!gravityManager.isPlayerSearchingGravitySurface ();
if (!canUseClimbLedge) {
return;
}
if (playerOnGround) {
//if the auto hang action check is active, then
if (checkForHangFromLedgeOnGround) {
//search a surface in front of the player and in down direction, according to hangFromLedgeOnGroundRaycastPosition transform forward direction
if (!surfaceToHangOnGroundFound) {
//if the player is moving or if he only auto hang from ledge when not moving, to detect it first, then
if (Time.time > lastTimeLedgeToHangFound + 1 && (!onlyHangFromLedgeIfPlayerIsNotMoving || playerControllerManager.isPlayerMoving (0))) {
//check the ledge in front of him
currentRayPosition = hangFromLedgeOnGroundRaycastPosition.position;
currentRayDirection = hangFromLedgeOnGroundRaycastPosition.forward;
if (!Physics.Raycast (currentRayPosition, currentRayDirection, out hit, checkForHangFromLedgeOnGroundRaycastDistance, layerMaskToCheck)) {
//if not surface is found, then
if (showGizmo) {
Debug.DrawRay (currentRayPosition, checkForHangFromLedgeOnGroundRaycastDistance * currentRayDirection, Color.green);
}
//search for the closest point surface of that ledge, by lowering the raycast position until a surface is found
bool surfaceToLedgeDetected = false;
RaycastHit newHit = new RaycastHit ();
int numberOfLoops = 0;
while (!surfaceToLedgeDetected && numberOfLoops < 100) {
if (showGizmo) {
Debug.DrawRay (currentRayPosition, checkForHangFromLedgeOnGroundRaycastDistance * currentRayDirection, Color.blue, 4);
}
if (Physics.Raycast (currentRayPosition, currentRayDirection, out newHit, climbLedgeRayDownDistance, layerMaskToCheck)) {
hit = newHit;
surfaceToLedgeDetected = true;
holdLedgeZone.position = hit.point + 0.04f * playerTransform.up;
} else {
holdLedgeZone.position = hit.point + 0.04f * playerTransform.up;
currentRayPosition -= 0.05f * hangFromLedgeOnGroundRaycastPosition.up;
}
numberOfLoops++;
}
//once the ledge is found, then
if (surfaceToLedgeDetected) {
if (checkLedgeZoneDetectedByRaycast) {
currentSurfaceFoundBelow = hit.collider.gameObject;
if (currentSurfaceFoundBelow != previousSurfaceFoundBelow) {
previousSurfaceFoundBelow = currentSurfaceFoundBelow;
ledgeZoneSystem currentLedgeZoneSystem = currentSurfaceFoundBelow.GetComponent<ledgeZoneSystem> ();
if (currentLedgeZoneSystem != null) {
stopHangFromLedgeAction = currentLedgeZoneSystem.avoidPlayerGrabLedge;
canClimbCurrentLedgeZone = currentLedgeZoneSystem.ledgeZoneCanBeClimbed;
canCheckForHangFromLedgeOnGround = currentLedgeZoneSystem.canCheckForHangFromLedgeOnGround;
} else {
stopHangFromLedgeAction = false;
canClimbCurrentLedgeZone = true;
canCheckForHangFromLedgeOnGround = true;
}
}
if (!canCheckForHangFromLedgeOnGround) {
return;
}
//the current surface can't be grabbed, so stop the rest of checkings until a new surface is found
if (stopHangFromLedgeAction) {
return;
}
}
//launch a raycast to get the position of the wall below the ledge, to get also the normal of that surface, so the player can move and rotate toward that direction
currentRayPosition = holdLedgeZone.position + 1.4f * playerTransform.forward - playerTransform.up;
currentRayDirection = -playerTransform.forward;
if (Physics.Raycast (currentRayPosition, currentRayDirection, out hit, checkForHangFromLedgeOnGroundRaycastDistance, layerMaskToCheck)) {
//once that surface is found, check that there is enough room in the ledge for the player to hang there, for example an space of an unit is not enough, using the
//regular height of the player, 2 units, as measure
currentRayPosition = holdLedgeZone.position + 0.5f * hit.normal;
currentRayDirection = -playerTransform.up;
RaycastHit newAirHit = new RaycastHit ();
if (!Physics.Raycast (currentRayPosition, currentRayDirection, out newAirHit, 2, layerMaskToCheck)) {
surfaceToHangNormal = hit.normal;
holdLedgeZone.rotation = Quaternion.LookRotation (-surfaceToHangNormal, playerTransform.up);
lastTimeLedgeToHangFound = Time.time;
surfaceToHangOnGroundFound = true;
holdLedgeZone.position += 0.5f * hit.normal;
}
}
}
} else {
if (showGizmo) {
Debug.DrawRay (currentRayPosition, hit.distance * currentRayDirection, Color.white);
}
}
}
} else {
//once a possible ledge to hang is found, then
if (!movingTowardSurfaceToHang) {
//if the player needs to being close to the ledge to detect it and then press the movement input again to activate the auto hang, then
if (onlyHangFromLedgeIfPlayerIsNotMoving) {
//use a delay to avoid different input signal
if (Time.time > lastTimeLedgeToHangFound + 0.5f) {
if (!hasToLookAtLedgePositionOnFirstPerson || (!firstPersonActive || ledgeZoneCloseEnoughToScreenCenter)) {
//check the direction of the input and check how much time is pressed, using only a quick press
Vector3 movementDirection = playerControllerManager.getMoveInputDirection ();
if (Mathf.Abs (movementDirection.magnitude) > 0) {
if (!movementInputPressed) {
lastTimeVerticalInputPressed = Time.time;
movementInputPressed = true;
}
lastMovementDirection = movementDirection;
} else {
if (movementInputPressed) {
movementInputReleased = true;
}
}
//once the input is quickly pressed
if (movementInputPressed && movementInputReleased) {
//if the amount of time to press the input is correct
if (Time.time < lastTimeVerticalInputPressed + 0.4f) {
//check the direction of the input according to camera direction and player forward direction
Vector3 ledgeDirection = holdLedgeZone.position - playerTransform.position;
directionAngle = Vector3.SignedAngle (lastMovementDirection, ledgeDirection, playerTransform.up);
if (Mathf.Abs (directionAngle) < 90) {
//orientation is correct, proceed to auto hang
lastTimeMovingTowardSurfaceToHang = Time.time;
movingTowardSurfaceToHang = true;
previouslyMovingTowardSurfaceToHang = true;
playerControllerManager.overrideMainCameraTransformDirection (holdLedgeZone.transform, true);
if (useHangFromLedgeIcon) {
enableOrDisableHangFromLedgeIcon (false);
}
} else {
stopHangFromLedge ();
}
} else {
//else, stop the auto hang
stopHangFromLedge ();
}
}
}
}
} else {
//else, the player only needs to get close to the ledge and the system will automatically activate the auto hang
lastTimeMovingTowardSurfaceToHang = Time.time;
movingTowardSurfaceToHang = true;
previouslyMovingTowardSurfaceToHang = true;
playerControllerManager.overrideMainCameraTransformDirection (holdLedgeZone.transform, true);
}
}
}
}
return;
}
//check if the player only grabs a ledge when he is pressing the movement input
if (onlyGrabLedgeIfMovingForward && !previouslyMovingTowardSurfaceToHang) {
if (playerControllerManager.isPlayerMoving (0)) {
return;
}
}
//check for a surface to climb in front of the player when he is on the air
if (!climbingLedge && Time.time > lastTimeLedgeChecked + 0.6f) {
currentRayPosition = climbLedgeRayPosition.position;
currentRayDirection = climbLedgeRayPosition.forward;
//check surfaces in front of him
if (Physics.Raycast (currentRayPosition, currentRayDirection, out hit, climbLedgeRayForwardDistance, layerMaskToCheck)) {
if (hit.rigidbody != null) {
return;
}
//check if there is a ledge zone system attached in the current surface in front of him, to get the values configured on it, so maybe that surface can be grabbed or climbed
if (checkLedgeZoneDetectedByRaycast) {
currentSurfaceFound = hit.collider.gameObject;
if (currentSurfaceFound != previousSurfaceFound) {
previousSurfaceFound = currentSurfaceFound;
ledgeZoneSystem currentLedgeZoneSystem = currentSurfaceFound.GetComponent<ledgeZoneSystem> ();
if (currentLedgeZoneSystem != null) {
stopGrabLedge = currentLedgeZoneSystem.avoidPlayerGrabLedge;
canClimbCurrentLedgeZone = currentLedgeZoneSystem.ledgeZoneCanBeClimbed;
canCheckForHangFromLedgeOnGround = currentLedgeZoneSystem.canCheckForHangFromLedgeOnGround;
} else {
stopHangFromLedgeAction = false;
canClimbCurrentLedgeZone = true;
canCheckForHangFromLedgeOnGround = true;
}
}
//the current surface can't be grabbed, so stop the rest of checkings until a new surface is found
if (stopGrabLedge) {
return;
}
}
if (showGizmo) {
Debug.DrawRay (currentRayPosition, climbLedgeRayForwardDistance * currentRayDirection, Color.blue);
}
surfaceFoundNormal = hit.normal;
//if a surface is located in front of the player, checks that there is enough space above it, by checking that a second raycast doesn't found a surface
currentRayPosition = climbLedgeRayPosition.position + 0.3f * climbLedgeRayPosition.up;
currentRayDirection = climbLedgeRayPosition.forward;
if (!Physics.Raycast (currentRayPosition, currentRayDirection, out hit, climbLedgeRayForwardDistance, layerMaskToCheck)) {
if (showGizmo) {
Debug.DrawRay (currentRayPosition, climbLedgeRayForwardDistance * currentRayDirection, Color.blue);
}
//check then the ledge surface
currentRayPosition = climbLedgeRayDownPosition.position;
currentRayDirection = -climbLedgeRayDownPosition.up;
if (ledgeZoneFound) {
currentRayPosition +=
checkDownRaycastOffset.x * playerTransform.right +
checkDownRaycastOffset.y * playerTransform.up +
checkDownRaycastOffset.z * playerTransform.forward;
}
if (Physics.Raycast (currentRayPosition, currentRayDirection, out hit, climbLedgeRayDownDistance, layerMaskToCheck)) {
if (showGizmo) {
Debug.DrawRay (currentRayPosition, climbLedgeRayDownDistance * currentRayDirection, Color.yellow);
}
activateGrabLedge ();
} else {
if (showGizmo) {
Debug.DrawRay (currentRayPosition, climbLedgeRayDownDistance * currentRayDirection, Color.white);
}
}
} else {
if (showGizmo) {
Debug.DrawRay (currentRayPosition, climbLedgeRayForwardDistance * currentRayDirection, Color.red);
}
}
} else {
if (showGizmo) {
Debug.DrawRay (currentRayPosition, climbLedgeRayForwardDistance * currentRayDirection, Color.red);
}
if (checkLedgeZoneDetectedByRaycast) {
currentSurfaceFound = null;
previousSurfaceFound = null;
stopGrabLedge = false;
}
}
}
}
public void enableOrDisableHangFromLedgeIcon (bool state)
{
if (hangFromLedgeIcon.activeSelf != state) {
hangFromLedgeIcon.SetActive (state);
}
}
//once the player climbs a ledge or lose it or jumps above it, resume player components and state
public void resumePlayer ()
{
stopAdjustHoldOnLedgeCoroutine ();
stopClimbLedgeOnFirstPersonMovementCoroutine ();
if (currentParentAssignedSystem != null) {
if (previousParent != null) {
playerControllerManager.setPlayerAndCameraParent (previousParent);
} else {
playerControllerManager.setPlayerAndCameraParent (null);
}
holdLedgeZone.SetParent (null);
climbLedgZone.SetParent (null);
currentParentAssignedSystem = null;
}
surfaceBelowPlayerDetected = false;
lastTimeLedgeChecked = Time.time;
climbingLedge = false;
activateClimbAction = false;
climbLedgeActionActivated = false;
loseLedgeActionActivated = false;
previouslyMovingTowardSurfaceToHang = false;
playerControllerManager.setLastTimeFalling ();
playerControllerManager.changeScriptState (true);
playerControllerManager.setHeadTrackCanBeUsedState (true);
playerControllerManager.setApplyRootMotionAlwaysActiveState (false);
playerControllerManager.setActionActiveState (false);
playerControllerManager.setGravityForcePuase (false);
playerControllerManager.setCheckOnGroungPausedState (false);
playerControllerManager.setFootStepManagerState (false);
MainCollider.isTrigger = false;
mainRigidbody.isKinematic = false;
if (!firstPersonActive) {
animator.SetInteger (actionIDAnimatorID, 0);
animator.SetBool (actionActiveAnimatorID, false);
}
headBobManager.pauseHeadBodWithDelay (0.5f);
headBobManager.playOrPauseHeadBob (true);
if (carryingWeaponsPreviously && drawWeaponsAfterClimbLedgeIfPreviouslyCarried) {
weaponsManager.checkIfDrawSingleOrDualWeapon ();
}
checkEventOnLedgeClimbed ();
grabbingSurface = false;
mainPlayerCamera.setOriginalMoveCameraPositionWithMouseWheelActiveState ();
if (showDebugPrint) {
print ("resume player");
}
playerControllerManager.setUpdate2_5dClampedPositionPausedState (false);
if (setPivotCameraTransformParentCurrentTransformToFollowPreviouslyActive) {
mainPlayerCamera.setPivotCameraTransformOriginalParent ();
mainPlayerCamera.resetPivotCameraTransformLocalRotation ();
mainPlayerCamera.setPlayerCameraTransformFollowsPlayerTransformRotationOnFBAState (false);
}
setPivotCameraTransformParentCurrentTransformToFollowPreviouslyActive = false;
playerControllerManager.setGrabbingToSurfaceActiveState (false);
playerControllerManager.setCurrentCustomActionCategoryID (0);
playerControllerManager.setAdjustingToSurfaceToGrabInProcessState (false);
}
//function called to climb a ledge
public void startClimbLedgeAction ()
{
if (!canClimbCurrentLedgeZone) {
return;
}
playerControllerManager.setApplyRootMotionAlwaysActiveState (true);
stopAdjustHoldOnLedgeCoroutine ();
playerControllerManager.setAdjustingToSurfaceToGrabInProcessState (true);
playerControllerManager.overrideOnGroundAnimatorValue (timeToClimbLedgeThirdPerson + 0.5f);
playerControllerManager.setFootStepManagerState (false);
lastTimeClimbLedge = Time.time;
activateClimbAction = true;
if (!firstPersonActive) {
animator.SetInteger (actionIDAnimatorID, climbLedgeActionID);
}
canStartToClimbLedge = true;
if (firstPersonActive) {
climbLedgeOnFirstPersonMovement ();
} else {
mainRigidbody.isKinematic = false;
if (playerControllerManager.getCurrentNormal () != Vector3.up) {
climbLedgZone.position -= 0.5f * playerTransform.forward - 0.15f * playerTransform.up;
activatingManualMatchTargetPosition = true;
climbLedgeOnFirstPersonMovement ();
}
}
checkEventOnStartLedgeClimb ();
}
bool activatingManualMatchTargetPosition;
//function called to lose a ledge
public void startLoseLedgeAction ()
{
if (!climbingLedge) {
return;
}
activateClimbAction = true;
playerControllerManager.currentVelocity = 10 * (-playerTransform.up);
resumePlayer ();
checkEventOnLedgeLose ();
}
public void loseAnyGrabbedSurface ()
{
if (grabbingSurface) {
resumePlayer ();
}
}
//match target animation function
public void MatchTarget (Vector3 matchPosition, Quaternion matchRotation, AvatarTarget target, MatchTargetWeightMask weightMask, float normalisedStartTime, float normalisedEndTime)
{
if (animator.isMatchingTarget) {
return;
}
if (animator.IsInTransition (baseLayerIndex)) {
return;
}
float normalizeTime = Mathf.Repeat (animator.GetCurrentAnimatorStateInfo (baseLayerIndex).normalizedTime, 1f);
if (normalizeTime > normalisedEndTime) {
return;
}
animator.MatchTarget (matchPosition, matchRotation, target, weightMask, normalisedStartTime, normalisedEndTime);
}
//adjust player to the current surface found in that edge, for position and rotation, using a corutine
public void adjustHoldOnLedge ()
{
stopAdjustHoldOnLedgeCoroutine ();
holdOnLedgeCoroutine = StartCoroutine (adjustHoldOnLedgeCoroutine ());
}
public void stopAdjustHoldOnLedgeCoroutine ()
{
if (holdOnLedgeCoroutine != null) {
StopCoroutine (holdOnLedgeCoroutine);
}
playerControllerManager.setAdjustingToSurfaceToGrabInProcessState (false);
}
IEnumerator adjustHoldOnLedgeCoroutine ()
{
// holdLedgeZone.rotation = Quaternion.LookRotation (-surfaceFoundNormal, playerTransform.up);
// float angleWithPlayer = Vector3.SignedAngle (playerTransform.right, holdLedgeZone.right, playerTransform.up);
//
// print (angleWithPlayer);
// holdLedgeZone.Rotate (angleWithPlayer, 0, 0);
playerControllerManager.setAdjustingToSurfaceToGrabInProcessState (true);
Vector3 currentPlayerRotation = playerTransform.eulerAngles;
float angleWithSurface = Vector3.SignedAngle (-surfaceFoundNormal, playerTransform.forward, playerTransform.up);
Vector3 currentTargetRotation = playerTransform.eulerAngles - angleWithSurface * playerTransform.up;
holdLedgeZone.eulerAngles = currentTargetRotation;
Vector3 currentHoldOnLedgeOffset = holdOnLedgeOffset;
if (mainPlayerCamera.isFullBodyAwarenessActive ()) {
currentHoldOnLedgeOffset = holdOnLedgeOffsetFBA;
}
holdLedgeZone.position +=
currentHoldOnLedgeOffset.x * holdLedgeZone.right +
currentHoldOnLedgeOffset.y * playerTransform.up +
currentHoldOnLedgeOffset.z * holdLedgeZone.forward;
// Quaternion lookRotationTarget = Quaternion.LookRotation (-surfaceFoundNormal, playerTransform.up);
if (showDebugPrint) {
print (angleWithSurface + " " + (-surfaceFoundNormal) + " " + currentTargetRotation);
}
playerTargetRotation = currentTargetRotation;
float dist = GKC_Utils.distance (playerTransform.position, holdLedgeZone.position);
float duration = dist / adjustToHoldOnLedgePositionSpeed;
float translateTimer = 0;
float rotateTimer = 0;
float teleportTimer = 0;
float normalAngle = 0;
float positionDifference = 0;
bool targetReached = false;
while (!targetReached) {
translateTimer += Time.deltaTime / duration;
playerTransform.position = Vector3.Lerp (playerTransform.position, holdLedgeZone.position, translateTimer);
rotateTimer += Time.deltaTime * adjustToHoldOnLedgeRotationSpeed;
playerTransform.eulerAngles = Vector3.Lerp (currentPlayerRotation, playerTargetRotation, rotateTimer);
teleportTimer += Time.deltaTime;
normalAngle = Mathf.Abs (Vector3.SignedAngle (playerTransform.forward, -surfaceFoundNormal, playerTransform.up));
positionDifference = GKC_Utils.distance (playerTransform.position, holdLedgeZone.position);
if ((positionDifference < 0.01f && normalAngle < 1) || teleportTimer > (duration + 1)) {
targetReached = true;
}
yield return null;
}
playerControllerManager.setAdjustingToSurfaceToGrabInProcessState (false);
}
//climg ledge in first person is not driven by root motion and animation, so the movement is managed by translation using a coroutine
public void climbLedgeOnFirstPersonMovement ()
{
stopClimbLedgeOnFirstPersonMovementCoroutine ();
climbLedgeOnFirstPersonCoroutine = StartCoroutine (climbLedgeOnFirstPersonMovementCoroutine ());
}
public void stopClimbLedgeOnFirstPersonMovementCoroutine ()
{
if (climbLedgeOnFirstPersonCoroutine != null) {
StopCoroutine (climbLedgeOnFirstPersonCoroutine);
}
}
IEnumerator climbLedgeOnFirstPersonMovementCoroutine ()
{
float dist = GKC_Utils.distance (playerTransform.position, climbLedgZone.position);
float duration = dist / climbLedgeSpeedFirstPerson;
if (activatingManualMatchTargetPosition) {
duration = dist / (climbLedgeSpeedFirstPerson / 4);
activatingManualMatchTargetPosition = false;
}
float translateTimer = 0;
float teleportTimer = 0;
bool targetReached = false;
float positionDifference = 0;
while (!targetReached) {
translateTimer += Time.deltaTime / duration;
playerTransform.position = Vector3.Lerp (playerTransform.position, climbLedgZone.position, translateTimer);
teleportTimer += Time.deltaTime;
positionDifference = GKC_Utils.distance (playerTransform.position, climbLedgZone.position);
if ((positionDifference < 0.03f) || teleportTimer > (duration + 1)) {
targetReached = true;
}
yield return null;
}
}
//when the option to auto hang is active and the player is on first person, the player rotation is adjusted by the camera rotation, looking in the same direction, so the rotation to face the ledge
//can't be done by input in the player, so instead, the camera is rotated with a coroutine to face the wall of the ledge
public void rotateTowardLedgeOnFirstPerson ()
{
stopClimbLedgeOnFirstPersonMovementCoroutine ();
rotateToLedgeOnFirstPersonCoroutine = StartCoroutine (rotateTowardLedgeOnFirstPersonCoroutine ());
}
public void stopRotateTowardLedgeOnFirstPersonCoroutine ()
{
if (rotateToLedgeOnFirstPersonCoroutine != null) {
StopCoroutine (rotateToLedgeOnFirstPersonCoroutine);
}
}
IEnumerator rotateTowardLedgeOnFirstPersonCoroutine ()
{
rotationToLedgeOnFirstPersonActive = true;
playerRotatedToLedgeOnFirstPerson = true;
Vector3 currentPlayerRotation = playerTransform.eulerAngles;
Quaternion targetRotation = Quaternion.LookRotation (-surfaceToHangNormal, playerTransform.up);
playerTargetRotation = targetRotation.eulerAngles;
float rotateTimer = 0;
float normalAngle = 0;
bool targetReached = false;
while (!targetReached) {
rotateTimer += Time.deltaTime * adjustToHoldOnLedgeRotationSpeed;
mainPlayerCamera.transform.eulerAngles = Vector3.Lerp (currentPlayerRotation, playerTargetRotation, rotateTimer);
normalAngle = Vector3.SignedAngle (mainPlayerCamera.transform.forward, -surfaceToHangNormal, playerTransform.up);
if (Mathf.Abs (normalAngle) < 1) {
targetReached = true;
rotationToLedgeOnFirstPersonActive = false;
}
yield return null;
}
}
//set if a ledge zone system has been detected by trigger
public void setLedgeZoneFoundState (bool state)
{
if (!checkForLedgeZonesActive) {
return;
}
ledgeZoneFound = state;
}
//configure new raycast distance for the current ledge zones, usually to make these raycast shorter, for more little ledges in a wall instead of the top part
public void setNewRaycastDistance (float forwardDistance, float downDistance, Vector3 newCheckDownRaycastOffset)
{
if (!checkForLedgeZonesActive) {
return;
}
climbLedgeRayForwardDistance = forwardDistance;
climbLedgeRayDownDistance = downDistance;
checkDownRaycastOffset = newCheckDownRaycastOffset;
}
//set original distance on raycast
public void setOriginalRaycastDistance ()
{
if (!checkForLedgeZonesActive) {
return;
}
setNewRaycastDistance (originalClimbLedgeRayForwardDistance, originalClimbLedgeRayDownDistance, Vector3.zero);
}
//configure if the system can climb a surface right now from a trigger detection on a ledge zone system configured in the level
public void setCanClimbCurrentLedgeZoneState (bool state)
{
canClimbCurrentLedgeZone = state;
}
public void setCanGrabAnySurfaceOnAirActiveState (bool state)
{
canGrabAnySurfaceOnAirActive = state;
}
public void setCanGrabAnySurfaceOnAirState (bool state)
{
canGrabAnySurfaceOnAir = state;
}
//function called on jump action input by event, so if the player is hanging from a ledge, he can jump from it
public void inputJumpOnHoldLedge ()
{
if (climbLedgeActive && canJumpWhenHoldLedge && (climbingLedge || grabbingSurface) && !activateClimbAction) {
resumePlayer ();
bool jumpUpResult = true;
if (checkMoveInputDirectionOnJump) {
rawMovementAxis = playerInput.getPlayerRawMovementAxis ();
if (rawMovementAxis != Vector2.zero) {
Vector3 movementDirection = rawMovementAxis.y * playerControllerManager.getCurrentForwardDirection () + rawMovementAxis.x * playerControllerManager.getCurrentRightDirection ();
directionAngle = Vector3.SignedAngle (movementDirection, playerTransform.forward, playerTransform.up);
if (Mathf.Abs (directionAngle) > 90) {
if (showDebugPrint) {
print ("pressing in opposite direction to grabbed surface, jumping in opposite direction");
}
jumpUpResult = false;
}
}
if (!jumpUpResult) {
currentDirectionToRotatePlayer = -playerTransform.forward;
Vector3 jumpDirection = playerTransform.forward;
bool isFirstPersonActive = mainPlayerCamera.isFirstPersonActive ();
if (isFirstPersonActive) {
float angleWithCamera = Vector3.SignedAngle (playerTransform.forward, mainPlayerCamera.transform.forward, playerTransform.up);
if (Mathf.Abs (angleWithCamera) > 45) {
currentDirectionToRotatePlayer = mainPlayerCamera.transform.forward;
}
}
Vector3 totalJumpForce = jumpForceWhenHoldLedgeOnOppositeDirection * (currentDirectionToRotatePlayer + 0.5f * playerTransform.up);
playerControllerManager.useJumpPlatform (totalJumpForce, jumpForceMode);
rotateCharacterOnJump ();
}
}
if (jumpUpResult) {
playerControllerManager.useJumpPlatform (jumpForceWhenHoldLedge * playerTransform.up, jumpForceMode);
}
checkEventOnLedgeJump ();
}
}
public void inputGrabSurface ()
{
if (!canGrabToSurfaceOnAirInputEnabled) {
return;
}
activateGrabToSurfaceAction ();
}
public void activateGrabToSurfaceAction ()
{
if (canGrabToSurface ()) {
if (grabToSurfaceInputPaused) {
return;
}
if (!canGrabAnySurfaceOnAirActive) {
return;
}
if (climbLedgeActionPaused) {
return;
}
if (otherClimbActionActive) {
return;
}
if (usingDevicesManager.hasDeviceToUse ()) {
return;
}
enableOrDisableGrabToSurfaceState ();
}
}
public void activateGrabToSurfaceActionExternally (bool state, Transform newLedgeTransform)
{
if (state) {
if (grabbingSurface) {
if (newLedgeTransform != null && newLedgeTransform == newLedgeTransformToGrabAutomatically) {
if (showDebugPrint) {
print ("trying to grab to already current surface, ignoring");
}
return;
}
} else {
if (Time.time < lastTimeLedgeChecked + 0.2f) {
if (showDebugPrint) {
print ("trying to grab before proper wait time, ignoring");
}
return;
}
}
}
callingActivateGrabToSurfaceActionExternally = state;
if (showDebugPrint) {
print ("callingActivateGrabToSurfaceActionExternally " + state);
}
newLedgeTransformToGrabAutomatically = newLedgeTransform;
if (callingActivateGrabToSurfaceActionExternally) {
if (playerControllerManager.isActionActive ()) {
playerControllerManager.stopAllActionsOnActionSystem ();
}
activateGrabToSurfaceAction ();
callingActivateGrabToSurfaceActionExternally = false;
}
}
public bool canGrabToSurface ()
{
if (!canGrabAnySurfaceOnAir) {
return false;
}
if (playerOnGround) {
return false;
}
if (climbingLedge) {
return false;
}
if (pauseAllPlayerDownForces) {
return false;
}
if (wallRunningActive) {
return false;
}
if (freeClimbActive) {
return false;
}
if (swimModeActive) {
return false;
}
if (flyModeActive) {
return false;
}
if (waitingForMinTimeToCheckForLedge) {
return false;
}
return true;
}
public void enableOrDisableGrabToSurfaceState ()
{
if (grabbingSurface) {
resumePlayer ();
return;
} else {
if (callingActivateGrabToSurfaceActionExternally) {
if (playerControllerManager.isPlayerOnGround ()) {
callingActivateGrabToSurfaceActionExternally = false;
return;
}
holdLedgeZone.position = newLedgeTransformToGrabAutomatically.position;
holdLedgeZone.rotation = newLedgeTransformToGrabAutomatically.rotation;
//check surfaces in front of him
surfaceFoundNormal = -newLedgeTransformToGrabAutomatically.forward;
grabbingSurface = true;
climbingLedge = false;
//ground above the ledge is found, so pause the necessary components in the player and activate the grab ledge action
stopHangFromLedge ();
setPlayerPauseState ();
playerControllerManager.setGrabbingToSurfaceActiveState (true);
callingActivateGrabToSurfaceActionExternally = false;
} else {
if (Time.time < playerControllerManager.getLastTimeFalling () + 0.35f) {
return;
}
if (useGrabSurfaceAmountLimit) {
currentGrabSurfaceAmount++;
if (currentGrabSurfaceAmount > grabSurfaceAmountLimit) {
return;
}
}
currentRayPosition = climbLedgeRayPosition.position;
currentRayDirection = climbLedgeRayPosition.forward;
//check surfaces in front of him
if (Physics.Raycast (currentRayPosition, currentRayDirection, out hit, climbLedgeRayForwardDistance, layerMaskToCheck)) {
if (hit.collider.GetComponent<Rigidbody> ()) {
return;
} else {
surfaceFoundNormal = hit.normal;
}
} else {
return;
}
if (useSurfacesToCheckTagsOnGrabSurfaceByInput) {
if (!surfacesToCheckTagsGrabSurfaceByInputList.Contains (hit.collider.tag)) {
return;
}
}
grabbingSurface = true;
climbingLedge = false;
//ground above the ledge is found, so pause the necessary components in the player and activate the grab ledge action
stopHangFromLedge ();
setPlayerPauseState ();
playerControllerManager.setGrabbingToSurfaceActiveState (true);
//lower the raycast to get the exact edge of the ledge in front of the player
if (Physics.Raycast (currentRayPosition, currentRayDirection, out hit, climbLedgeRayDownDistance * 2, layerMaskToCheck)) {
holdLedgeZone.position = hit.point + 0.08f * playerTransform.up;
if (showGizmo) {
Debug.DrawRay (holdLedgeZone.position, climbLedgeRayDownDistance * 2 * hit.normal, Color.black, 10);
}
}
}
//adjust the player rotation and position to the ledge found
adjustHoldOnLedge ();
canStartToClimbLedge = false;
lastTimeHoldOnEdge = Time.time;
//keep player weapons if he was
if (keepWeaponsOnLedgeDetected) {
carryingWeaponsPreviously = weaponsManager.isPlayerCarringWeapon ();
if (carryingWeaponsPreviously) {
weaponsManager.checkIfDisableCurrentWeapon ();
}
}
if (powersManager.isAimingPower ()) {
powersManager.useAimMode ();
}
//call ledge grabbed event
checkEventOnLedgeGrabbed ();
}
}
public void setPlayerPauseState ()
{
playerControllerManager.changeScriptState (false);
playerControllerManager.setHeadTrackCanBeUsedState (false);
playerControllerManager.setActionActiveState (true);
playerControllerManager.setGravityForcePuase (true);
playerControllerManager.setPlayerOnGroundAnimatorStateOnOverrideOnGround (true);
playerControllerManager.setCheckOnGroungPausedState (true);
playerControllerManager.setPlayerVelocityToZero ();
playerControllerManager.setFootStepManagerState (true);
mainRigidbody.isKinematic = true;
playerControllerManager.currentVelocity = Vector3.zero;
headBobManager.stopAllHeadbobMovements ();
headBobManager.stopBobTransform ();
headBobManager.playOrPauseHeadBob (false);
MainCollider.isTrigger = true;
mainPlayerCamera.stopShakeCamera ();
firstPersonActive = playerControllerManager.isPlayerOnFirstPerson ();
if (!firstPersonActive) {
animator.SetBool (actionActiveAnimatorID, true);
animator.CrossFadeInFixedTime (holdOnLedgeActionName, 0.1f);
}
currentRayPosition = climbLedgeRayPosition.position;
currentRayDirection = climbLedgeRayPosition.forward;
//if a ledge zone is found, check if maybe is a moving platform, to make the player a child of that object while he is grabbed to it
if (ledgeZoneFound) {
currentParentAssignedSystem = hit.collider.GetComponent<parentAssignedSystem> ();
if (currentParentAssignedSystem != null) {
if (playerControllerManager.isPlayerSetAsChildOfParent ()) {
previousParent = playerTransform.parent;
} else {
previousParent = null;
}
Transform newParent = currentParentAssignedSystem.getAssignedParent ().transform;
playerControllerManager.setPlayerAndCameraParent (newParent);
holdLedgeZone.SetParent (newParent);
climbLedgZone.SetParent (newParent);
}
}
mainPlayerCamera.setMoveCameraPositionWithMouseWheelActiveState (false);
playerControllerManager.setUpdate2_5dClampedPositionPausedState (true);
playerControllerManager.setJumpsAmountValue (0);
setPivotCameraTransformParentCurrentTransformToFollowPreviouslyActive = false;
bool isFullBodyAwarenessActive = mainPlayerCamera.isFullBodyAwarenessActive ();
if (isFullBodyAwarenessActive) {
mainPlayerCamera.setPivotCameraTransformParentCurrentTransformToFollow ();
mainPlayerCamera.setPlayerCameraTransformFollowsPlayerTransformRotationOnFBAState (true);
setPivotCameraTransformParentCurrentTransformToFollowPreviouslyActive = true;
}
playerControllerManager.setCurrentCustomActionCategoryID (10700);
}
bool setPivotCameraTransformParentCurrentTransformToFollowPreviouslyActive;
public void activateGrabLedge ()
{
climbingLedge = true;
//ground above the ledge is found, so pause the necessary components in the player and activate the grab ledge action
stopHangFromLedge ();
setPlayerPauseState ();
bool surfaceRotatedForward = false;
bool checkProperSurface = false;
currentRayPosition = climbLedgeRayPosition.position + climbLedgeRayPosition.up + 1.2f * climbLedgeRayPosition.forward;
currentRayDirection = -climbLedgeRayPosition.up;
if (Physics.Raycast (currentRayPosition, currentRayDirection, out hit, climbLedgeRayDownDistance + 0.5f, layerMaskToCheck)) {
float angleWithSurface = Vector3.SignedAngle (hit.normal, playerTransform.up, playerTransform.right);
if (Mathf.Abs (angleWithSurface) > 4) {
if (angleWithSurface < 0) {
surfaceRotatedForward = true;
}
if (showDebugPrint) {
print ("Angle on top " + angleWithSurface + " " + hit.normal);
print ("surfaceRotatedForward " + surfaceRotatedForward);
}
checkProperSurface = true;
}
}
if (checkProperSurface) {
if (surfaceRotatedForward) {
currentRayPosition = climbLedgeRayPosition.position + climbLedgeRayPosition.up + 1.2f * climbLedgeRayPosition.forward;
currentRayDirection = -climbLedgeRayPosition.forward - 0.1f * climbLedgeRayPosition.up;
} else {
currentRayPosition = climbLedgeRayPosition.position - climbLedgeRayPosition.forward + 0.7f * climbLedgeRayPosition.up;
currentRayDirection = -climbLedgeRayDownPosition.up;
}
} else {
currentRayPosition = climbLedgeRayPosition.position - climbLedgeRayPosition.up * 0.65f;
currentRayDirection = climbLedgeRayPosition.forward + 0.05f * climbLedgeRayPosition.up;
}
//lower the raycast to get the exact edge of the ledge in front of the player
bool surfaceToLedgeDetected = false;
int numberOfLoops = 0;
bool firstSurfaceContactDetected = false;
while (!surfaceToLedgeDetected) {
if (checkProperSurface) {
if (surfaceRotatedForward) {
if (Physics.Raycast (currentRayPosition, currentRayDirection, out hit, climbLedgeRayDownDistance, layerMaskToCheck)) {
holdLedgeZone.position = hit.point + 0.08f * playerTransform.up;
if (showGizmo) {
Debug.DrawLine (currentRayPosition, hit.point, Color.black, 5);
}
surfaceToLedgeDetected = true;
} else {
currentRayPosition -= 0.05f * playerTransform.up;
if (showGizmo) {
Debug.DrawLine (currentRayPosition, currentRayPosition + 3 * currentRayDirection, Color.white, 6);
}
}
} else {
if (Physics.Raycast (currentRayPosition, currentRayDirection, out hit, climbLedgeRayDownDistance, layerMaskToCheck)) {
holdLedgeZone.position = hit.point + 0.08f * playerTransform.up;
if (showGizmo) {
Debug.DrawLine (currentRayPosition, hit.point, Color.black, 5);
}
surfaceToLedgeDetected = true;
} else {
currentRayPosition += 0.05f * playerTransform.forward;
if (showGizmo) {
Debug.DrawLine (currentRayPosition, currentRayPosition + 3 * currentRayDirection, Color.white, 6);
}
}
}
} else {
if (Physics.Raycast (currentRayPosition, currentRayDirection, out hit, climbLedgeRayDownDistance, layerMaskToCheck)) {
holdLedgeZone.position = hit.point + 0.08f * playerTransform.up;
currentRayPosition += 0.05f * playerTransform.up;
if (showGizmo) {
Debug.DrawLine (currentRayPosition, hit.point, Color.black, 5);
}
firstSurfaceContactDetected = true;
} else {
if (firstSurfaceContactDetected) {
surfaceToLedgeDetected = true;
} else {
currentRayPosition += 0.05f * playerTransform.up;
if (showDebugPrint) {
print ("no surface found on first check raycast, waiting for first contact");
}
}
if (showGizmo) {
Debug.DrawLine (currentRayPosition, currentRayPosition + 3 * currentRayDirection, Color.white, 6);
}
}
}
numberOfLoops++;
if (numberOfLoops > 100) {
if (!surfaceToLedgeDetected) {
print ("WARNING; NO HOLD LEDGE ZONE FOUND, USING DEFAULT ONE TO ADJUST PLAYER");
}
surfaceToLedgeDetected = true;
}
}
if (checkProperSurface) {
currentRayPosition = playerTransform.position + 0.5f * playerTransform.up;
currentRayDirection = climbLedgeRayPosition.forward;
if (Physics.Raycast (currentRayPosition, currentRayDirection, out hit, climbLedgeRayDownDistance + 0.5f, layerMaskToCheck)) {
surfaceFoundNormal = hit.normal;
}
}
//lower the raycast to get the exact edge of the ledge in the surface above that ledge
currentRayPosition = climbLedgeRayPosition.position + 0.4f * playerTransform.up;
currentRayDirection = -climbLedgeRayDownPosition.up;
surfaceToLedgeDetected = false;
numberOfLoops = 0;
while (!surfaceToLedgeDetected) {
if (Physics.Raycast (currentRayPosition, currentRayDirection, out hit, climbLedgeRayDownDistance, layerMaskToCheck)) {
climbLedgZone.position = hit.point;
Vector3 currentOffset = climbLedgeTargetPositionOffsetThirdPerson;
if (firstPersonActive) {
currentOffset = climdLedgeTargetPositionOffsetFirstPerson;
} else {
if (mainPlayerCamera.isFullBodyAwarenessActive ()) {
currentOffset = climdLedgeTargetPositionOffsetFirstFBA;
}
}
climbLedgZone.position += currentOffset.x * playerTransform.right +
currentOffset.y * playerTransform.up +
currentOffset.z * playerTransform.forward;
surfaceToLedgeDetected = true;
} else {
currentRayPosition += 0.05f * playerTransform.forward;
}
numberOfLoops++;
if (numberOfLoops > 100) {
surfaceToLedgeDetected = true;
}
}
//adjust the player rotation and position to the ledge found
adjustHoldOnLedge ();
canStartToClimbLedge = false;
lastTimeHoldOnEdge = Time.time;
//keep player weapons if he was
if (keepWeaponsOnLedgeDetected) {
carryingWeaponsPreviously = weaponsManager.isUsingWeapons ();
if (carryingWeaponsPreviously) {
weaponsManager.checkIfDisableCurrentWeapon ();
}
}
if (powersManager.isAimingPower ()) {
powersManager.useAimMode ();
}
//call ledge grabbed event
checkEventOnLedgeGrabbed ();
}
public void setClimbLedgeActiveState (bool state)
{
climbLedgeActive = state;
}
public void setClimbLedgeActiveStateFromEditor (bool state)
{
setClimbLedgeActiveState (state);
updateComponent ();
}
//events for grab, climb and lose ledge
public void checkEventOnLedgeGrabbed ()
{
if (useEvents) {
eventOnLedgeGrabbed.Invoke ();
}
}
public void checkEventOnLedgeLose ()
{
if (useEvents) {
eventOnLedgeLose.Invoke ();
}
}
public void checkEventOnLedgeClimbed ()
{
if (useEvents) {
eventOnLedgeClimbed.Invoke ();
}
}
public void checkEventOnStartLedgeClimb ()
{
if (useEvents) {
eventOnStartLedgeClimb.Invoke ();
}
}
public void checkEventOnLedgeJump ()
{
if (useEvents) {
eventOnLedgeJump.Invoke ();
}
}
//a ledge zone system configured in a trigger is configured to avoid the player to grab a ledge
public void setAvoidPlayerGrabLedgeValue (bool state)
{
avoidPlayerGrabLedge = state;
}
//stop the auto hang from ledge action
public void stopHangFromLedge ()
{
playerInput.overrideInputValues (Vector2.zero, false);
playerControllerManager.overrideMainCameraTransformDirection (null, false);
movingTowardSurfaceToHang = false;
surfaceToHangOnGroundFound = false;
lastTimeVerticalInputPressed = 0;
movementInputPressed = false;
movementInputReleased = false;
onAirWhileSearchingLedgeToHang = false;
rotationToLedgeOnFirstPersonActive = false;
playerRotatedToLedgeOnFirstPerson = false;
if (useHangFromLedgeIcon) {
enableOrDisableHangFromLedgeIcon (false);
}
}
public void setCanCheckForHangFromLedgeOnGroundState (bool state)
{
canCheckForHangFromLedgeOnGround = state;
}
public void setOnlyHangFromLedgeIfPlayerIsNotMovingValue (bool state)
{
onlyHangFromLedgeIfPlayerIsNotMoving = state;
}
public void setOnlyHangFromLedgeIfPlayerIsNotMovingOriginalValue ()
{
onlyHangFromLedgeIfPlayerIsNotMoving = originalOnlyHangFromLedgeIfPlayerIsNotMovingValue;
}
public void setClimbLedgeActionPausedState (bool state)
{
climbLedgeActionPaused = state;
}
public void setOtherClimbActionActiveState (bool state)
{
otherClimbActionActive = state;
}
public void setGrabToSurfaceInputPausedState (bool state)
{
grabToSurfaceInputPaused = state;
}
public void stopSetClimbLedgeActionPausedStateWithDurationCoroutine ()
{
if (climbLedgeActionPausedCoroutine != null) {
StopCoroutine (climbLedgeActionPausedCoroutine);
}
}
public void setClimbLedgeActionPausedStateWithDuration (float pauseDuration)
{
stopSetClimbLedgeActionPausedStateWithDurationCoroutine ();
climbLedgeActionPausedCoroutine = StartCoroutine (setClimbLedgeActionPausedStateWithDurationCoroutine (pauseDuration));
}
IEnumerator setClimbLedgeActionPausedStateWithDurationCoroutine (float pauseDuration)
{
setClimbLedgeActionPausedState (true);
avoidPlayerGrabLedge = true;
yield return new WaitForSeconds (pauseDuration);
setClimbLedgeActionPausedState (false);
avoidPlayerGrabLedge = false;
}
Coroutine jumpCoroutine;
public void rotateCharacterOnJump ()
{
stopRotateCharacterOnJumpCoroutine ();
jumpCoroutine = StartCoroutine (rotateCharacterOnJumpCoroutine ());
}
void stopRotateCharacterOnJumpCoroutine ()
{
if (jumpCoroutine != null) {
StopCoroutine (jumpCoroutine);
}
}
public IEnumerator rotateCharacterOnJumpCoroutine ()
{
bool targetReached = false;
float movementTimer = 0;
float t = 0;
float duration = 0;
bool isFirstPersonActive = mainPlayerCamera.isFirstPersonActive ();
if (isFirstPersonActive) {
duration = 0.5f / jumpRotationSpeedFirstPerson;
} else {
duration = 0.5f / jumpRotationSpeedThirdPerson;
}
float angleDifference = 0;
bool isFullBodyAwarenessActive = mainPlayerCamera.isFullBodyAwarenessActive ();
Transform objectToRotate = playerTransform;
if (isFirstPersonActive || isFullBodyAwarenessActive) {
objectToRotate = mainPlayerCamera.transform;
}
Quaternion targetRotation = Quaternion.LookRotation (currentDirectionToRotatePlayer, objectToRotate.up);
while (!targetReached) {
t += Time.deltaTime / duration;
objectToRotate.rotation = Quaternion.Slerp (objectToRotate.rotation, targetRotation, t);
angleDifference = Quaternion.Angle (objectToRotate.rotation, targetRotation);
movementTimer += Time.deltaTime;
if (angleDifference < 0.2f || movementTimer > (duration + 1)) {
targetReached = true;
}
yield return null;
}
}
void updateComponent ()
{
GKC_Utils.updateComponent (this);
GKC_Utils.updateDirtyScene ("Update Climb Ledge System", gameObject);
}
//gizmo settings
#if UNITY_EDITOR
void OnDrawGizmos ()
{
if (!showGizmo) {
return;
}
if (GKC_Utils.isCurrentSelectionActiveGameObject (gameObject)) {
DrawGizmos ();
}
}
void OnDrawGizmosSelected ()
{
DrawGizmos ();
}
void DrawGizmos ()
{
if (showGizmo) {
Gizmos.color = gizmoColor;
Gizmos.DrawSphere (holdLedgeZone.position, gizmoRadius);
}
}
#endif
}