void Start() { grapplingHook = transform.Find("GrapplingHook"); hookCord = grapplingHook.Find("HookCord"); actualHookTransform = grapplingHook.Find("ActualHook"); actualHook = actualHookTransform.gameObject.GetComponent <ActualHook>(); hookLaunchPoint = transform.Find("HookLaunchPoint"); playerTransform = transform.parent; currState = GrapplingHookState.Inactive; }
/// <summary> /// Launches the player with slingshot velocity /// </summary> virtual protected void DoSlingShot() { // Launch (slingshot) direction = ((Vector2)grapple.transform.position - HandOffset) - (Vector2)character.Transform.position; direction.Normalize(); // Overwrite velocity character.SetVelocityX(direction.x * slingshotLaunchVelocity.x); character.SetVelocityY(direction.y * slingshotLaunchVelocity.y); state = GrapplingHookState.NONE; }
/// <summary> /// Handle inputs during the grapple /// </summary> virtual protected void HandleInputs() { // Early out if no state if (state == GrapplingHookState.NONE) { return; } // Early out if no slingshot (further input has no effect) if (state == GrapplingHookState.SLINGSHOT) { return; } // Check if button released if (GetGrappleButtonState() == ButtonState.NONE || GetGrappleButtonState() == ButtonState.UP) { buttonReleased = true; } // Check if button pressed again if (buttonReleased && GetGrappleButtonState() == ButtonState.DOWN) { buttonHoldStarted = true; buttonHeldTimer = 0; } // Launching we can't do anything more if (state == GrapplingHookState.LAUNCHING) { return; } // Check for button release if (buttonHoldStarted) { // Check if button held if (buttonHeldTimer > buttonHeldTime) { travelledDistance = 0; slingshotLaunchDistance = Mathf.Min(MaxRopeSlingshotDistance, ropeDistance / 2.0f); state = GrapplingHookState.SLINGSHOT; } // Else Cut hook else if (GetGrappleButtonState() == ButtonState.UP) { // Apply velovity boosts character.SetVelocityX(launchVelocityMultiplier.x * character.Velocity.x); character.SetVelocityY(launchVelocityMultiplier.y * character.Velocity.y); character.AddVelocity(launchVelocityBoost.x * character.FacingDirection, launchVelocityBoost.y, true); CancelGrapple(); } } }
/// <summary> /// Start the special mvoe /// </summary> override public void DoSpecialMove() { if (state == GrapplingHookState.NONE) { swingTime = 0.0f; buttonReleased = false; buttonHoldStarted = false; state = GrapplingHookState.LAUNCHING; FireGrapple(); } }
/// <summary> /// Cancels the grapple. /// </summary> virtual public void CancelGrapple() { state = GrapplingHookState.NONE; if (ropeGo != null) { ropeGo.SetActive(false); } if (grapple != null) { grapple.Hide(); } }
/// <summary> /// Called when the movement loses control. /// </summary> override public void LosingControl() { buttonReleased = false; buttonHoldStarted = false; state = GrapplingHookState.NONE; if (grapple != null) { grapple.Hide(); } if (ropeGo != null) { ropeGo.SetActive(false); } CancelGrapple(); }
/// <summary> /// Called by the hook when it latches on to something. /// </summary> virtual public void Latch() { ropeDistance = Vector2.Distance(((Vector2)grapple.transform.position - HandOffset), (Vector2)character.Transform.position); if (maxRopeDistance < ropeDistance) { maxRopeDistance = Vector2.Distance(((Vector2)grapple.transform.position - HandOffset), (Vector2)character.Transform.position); } // TODO Make a constant if (ropeDistance < 0.25f) { CancelGrapple(); } else { state = GrapplingHookState.CLINGING; } }
/** * Retracts the grappling hook */ public IEnumerator Retract() { anchorPoint = Vector3.zero; State = GrapplingHookState.Retracting; if (dontBring) { actualHook.DetachInteractable(); dontBring = false; } Vector3 currScale = hookCord.transform.localScale; float length = currScale.y * 2; //since scale for a cylinder corresponds to # of units from centre to either side while (length > 0) { //Adjust scale currScale.y -= retractSpeed * Time.deltaTime; length = currScale.y * 2; hookCord.transform.localScale = currScale; //Adjust position to stay "anchored" to anchor point hookCord.transform.position = hookLaunchPoint.position + (hookLaunchPoint.forward * currScale.y); //Ensure actual hook stays on far end of hook cord AdjustActualHookPos(currScale.y); yield return(null); } //Prevent scale from going negative currScale.y = 0; hookCord.transform.localScale = currScale; //Interactable will now be controlled by mech hand rather than the grappling hook actualHook.SwitchToMechHand(); //Grappling hook is left disabled unless in use //to avoid problems with collisions grapplingHook.gameObject.SetActive(false); actualHook.gameObject.SetActive(false); //actual hook is disabled to not interfere with mech hand grabbing State = GrapplingHookState.Inactive; }
/** * Extends the grappling hook */ public IEnumerator Extend() { //Shoot a raycast out in the direction the grappling hook would fire //if the raycast happens to hit something, the grappling hook will hit it too //store this position as the anchor point //this is to ensure that the destination is on the surface of the object //as sometimes the grappling hook goes inside objects RaycastHit hit; if (Physics.Raycast(hookLaunchPoint.position, hookLaunchPoint.forward, out hit, maxLength)) { if (hit.collider.gameObject.GetComponent <Hookable>()) { anchorPoint = hit.point; } } else { anchorPoint = Vector3.zero; } grapplingHook.gameObject.SetActive(true); actualHook.gameObject.SetActive(true); //actual hook is disabled to not interfere with mech hand grabbing State = GrapplingHookState.Extending; Vector3 currScale = hookCord.transform.localScale; Vector3 currPos = hookCord.transform.position; float length = currScale.y * 2; //since scale for a cylinder corresponds to # of units from centre to either side while (length < maxLength) { //Adjust scale currScale.y += extendSpeed * Time.deltaTime; length = currScale.y * 2; hookCord.transform.localScale = currScale; //Adjust position to stay "anchored" to mech hand hookCord.transform.position = hookLaunchPoint.position + (hookLaunchPoint.forward * currScale.y); //Ensure actual hook stays on far end of hook cord AdjustActualHookPos(currScale.y); //ActualHook has hooked something, so stop extending if (actualHook.Hooked) { State = GrapplingHookState.Hooked; //move actual hook back out to the anchored position which is on surface actualHookTransform.position = anchorPoint; /* Change length of hook cord to make sure it doesnt overshoot the actual hook */ length = Vector3.Distance(anchorPoint, hookLaunchPoint.position); //Adjust scale currScale.y = length / 2; hookCord.transform.localScale = currScale; //Adjust position to stay "anchored" to mech hand hookCord.transform.position = hookLaunchPoint.position + (hookLaunchPoint.forward * currScale.y); //If the Hookable is an Interactable if (actualHook.target) { State = GrapplingHookState.Held; actualHook.AttachInteractable(); } yield break; } if (actualHook.obstructed) { StartCoroutine(Retract()); yield break; } yield return(null); } //Start retract process after reaching maximum length StartCoroutine(Retract()); }
/** * Pull player towards hooked location */ public IEnumerator Pull() { State = GrapplingHookState.Pulling; Vector3 currScale = hookCord.transform.localScale; float length = currScale.y * 2; //since scale for a cylinder corresponds to # of units from centre to either side //Direction from player to the hooked point, the direction we will be pulled towards Vector3 dirToMove = (anchorPoint - playerTransform.position).normalized; //This is to make sure we dont move downwards if we aim lower (or higher!) dirToMove.y = 0; while (length > 0) { float oldScale = currScale.y; //Adjust scale currScale.y -= retractSpeed * Time.deltaTime; length = currScale.y * 2; hookCord.transform.localScale = currScale; /**1. We move the coord to the mech hand, just like in Extend()/Retract() **/ //Adjust position to stay "anchored" to anchor point hookCord.transform.position = hookLaunchPoint.position + (hookLaunchPoint.forward * currScale.y); //Ensure actual hook stays on far end of hook cord AdjustActualHookPos(currScale.y); /** 2. We move the mech hand (and as a result, the hook cord attached to the mech hand) * in the hook cord's up direction (the direction it moves in) by the amount * the hook cord has shrunk multiplied by 2. Mulitiplication because the hook cord * is no longer in the center * (below is pic demonstrating what this horrible explanation is trying to say) * * A) , = center of hook cord * currently, scale of 4 * [M.Hand]----,----[HookedObj] * * B) hook cord scaled down, now scale of 3 * 4 - 3 = 1 (deltaScale), distance from HookedObj * [M.Hand] ---,--- [HookedObj] * * C) hook cord moved to mech hand, (which is also deltaScale units away) * increasing the distance b/w hook cord and HookedObj to be 2 * deltaScale * [M.Hand]---,--- [HookedObj] * * D) translate mech hand by 2 * deltaScale and the hook cord is shorter and the mech * hand has moved * [M.Hand]---,---[HookedObj] * * **/ float deltaScale = oldScale - currScale.y; playerTransform.Translate(dirToMove * deltaScale * 2, Space.World); yield return(null); } //Prevent scale from going negative currScale.y = 0; hookCord.transform.localScale = currScale; //Grappling hook is left disabled unless in use //to avoid problems with collisions grapplingHook.gameObject.SetActive(false); State = GrapplingHookState.Inactive; }
/// <summary> /// Moves the character. /// </summary> override public void DoMove() { if (grapple == null) { state = GrapplingHookState.NONE; return; } // Check for hitting head if (character.WouldHitHeadThisFrame || WouldHitSides()) { CancelGrapple(); return; } // Cancel swing if we hit ground again if (hasLeftGround && character.Grounded) { CancelGrapple(); return; } // have we left the ground? if (!hasLeftGround && !character.Grounded) { hasLeftGround = true; } // Hnadle input HandleInputs(); switch (state) { case GrapplingHookState.LAUNCHING: if (!character.Grounded) { // Apply acceleration character.AddVelocity(0, TimeManager.FrameTime * character.Gravity, false); // Limit to terminal velocity if (character.Velocity.y < character.terminalVelocity) { character.SetVelocityY(character.terminalVelocity); } // Keep moving according to residual velocity character.Translate(character.Velocity.x * TimeManager.FrameTime, character.Velocity.y * TimeManager.FrameTime, true); } // Update rope distance ropeDistance = Vector2.Distance(((Vector2)grapple.transform.position - HandOffset), (Vector2)character.Transform.position); if (maxDistance < ropeDistance) { maxDistance = ropeDistance; } break; case GrapplingHookState.CLINGING: if (controlType == GrapplingHookControlType.AUTO_RETRACT) { state = GrapplingHookState.AUTO_RETRACTING; } DoSwing(); if (controlType != GrapplingHookControlType.AUTO_RETRACT && character.Input.VerticalAxisDigital == 1) { DoRetract(1); } if (controlType == GrapplingHookControlType.UP_AND_DOWN && character.Input.VerticalAxisDigital == -1 && !character.Grounded) { DoRetract(-1); } break; case GrapplingHookState.AUTO_RETRACTING: DoSwing(); DoRetract(1); break; case GrapplingHookState.SLINGSHOT: // DoSwing(); DoRetract(1); break; } // Draw Rope DrawRope(); }