using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; using UnityEngine.InputSystem; public class GrappleHook : MonoBehaviour { [Header("Grapple settings")] [SerializeField] private float maxGrappleDist = 200; [SerializeField] private float grappleExtensionSpeed = 10; [SerializeField] private float grappleStrength = 4.5f * 5f; [SerializeField] private float grappleDamper = 7f; [SerializeField] private float grappleBoostSpeed = 50f; [SerializeField] private float grappleDrawingSpeed = 20; [SerializeField] private LayerMask grappleableLayer; [SerializeField] private float aimSpeed = 50f; [Header("References")] [SerializeField] private Transform gunPos; [SerializeField] private RectTransform canvasRect; [SerializeField] private RectTransform hitMarkerRect; private PlayerInput playerInput; private Vector3 hitPosLocal, aimDir, aimDirTarget; private SpringJoint joint; private LineRenderer lr; RaycastHit hit; private bool grappled = false, grappling = false, boosted = false, changingLength = false, isStunned = false; float changingLengthDir = 0; private Vector3 currGrappleEndPos; #region private methods void Start() { lr = gameObject.GetComponentInChildren(); lr.enabled = false; hitMarkerRect.gameObject.SetActive(false); playerInput = gameObject.GetComponent(); } void Update() { if(isStunned){ return; } if(grappled){ DrawRope(Time.deltaTime); if(boosted){ joint.maxDistance -= grappleBoostSpeed * Time.deltaTime; } if(changingLength){ ChangeGrappleLength(changingLengthDir * grappleExtensionSpeed * Time.deltaTime); } return; } Aim(); } private void Aim(){ aimDir = Vector3.Lerp(aimDir, aimDirTarget, Time.deltaTime * aimSpeed); if(!hitMarkerRect.gameObject.activeSelf)hitMarkerRect.gameObject.SetActive(true); hitMarkerRect.anchoredPosition = WorldToUI(gameObject.transform.position + aimDir * maxGrappleDist); if(Physics.Raycast(gameObject.transform.position, aimDir, out hit, maxGrappleDist, grappleableLayer)){ hitMarkerRect.anchoredPosition = WorldToUI(hit.point); hitMarkerRect.gameObject.GetComponent().color = Color.green; if(grappling){ StartGrapple(hit); } }else{ hitMarkerRect.gameObject.GetComponent().color = Color.red; } } private void DrawRope(float deltaTime){ // TODO animate a wiggle on the rope if(!grappled)return; lr.SetPosition(0, gunPos.position); Vector3 endPoint; if(hit.rigidbody != null){ endPoint = hit.transform.TransformPoint(hitPosLocal); }else{ endPoint = hit.point; } currGrappleEndPos = Vector3.Lerp(currGrappleEndPos, endPoint, deltaTime * grappleDrawingSpeed); lr.SetPosition(1, currGrappleEndPos); if(!lr.enabled)lr.enabled = true; } private void StartGrapple(RaycastHit hit){ grappled = true; grappling = false; //Display hitMarkerRect.gameObject.SetActive(false); // Rope phys joint = gameObject.AddComponent(); joint.anchor = transform.InverseTransformPoint(gunPos.position); joint.autoConfigureConnectedAnchor = false; joint.maxDistance = hit.distance * 0.9f; if(hit.rigidbody != null){ Rigidbody hitRb = hit.rigidbody; joint.connectedBody = hitRb; joint.connectedAnchor = hit.transform.InverseTransformPoint(hit.point); joint.enableCollision = true; hitPosLocal = hit.transform.InverseTransformPoint(hit.point); }else{ joint.connectedAnchor = hit.point; } joint.spring = grappleStrength; joint.damper = grappleDamper; joint.massScale = 4.5f; currGrappleEndPos = transform.position; } private void EndGrapple(){ grappled = false; grappling = false; lr.enabled = false; boosted = false; Destroy(joint); } private Vector2 WorldToUI(Vector3 point){ Vector2 viewPortPos = Camera.main.WorldToViewportPoint(point); Vector2 pointScreenPos = new Vector2(((viewPortPos.x*canvasRect.sizeDelta.x)-(canvasRect.sizeDelta.x*0.5f)), ((viewPortPos.y*canvasRect.sizeDelta.y)-(canvasRect.sizeDelta.y*0.5f))); return pointScreenPos; } private void ChangeGrappleLength(float value){ if(joint != null){ joint.maxDistance += value; } } private void UnStun(){ isStunned = false; Debug.Log("unstunned"); } #endregion #region public methods public void Stun(float duration){ isStunned = true; EndGrapple(); Debug.Log("Stunned"); Invoke("UnStun", duration); } #endregion #region InputActions public void Grapple(InputAction.CallbackContext context){ if(grappling && grappled)return; if(context.performed){ grappling = true; }else if(context.canceled){ EndGrapple(); } } public void BoostToAnchor(InputAction.CallbackContext context){ if(context.performed && grappled){ boosted = true; } } public void AimInput(InputAction.CallbackContext context){ Vector2 value = context.ReadValue(); if(playerInput.currentControlScheme.Equals("Controller")){ if(value.magnitude == 0)return; aimDirTarget = new Vector3(value.x, value.y, 0).normalized; }else if(playerInput.currentControlScheme.Equals("KbMouse")){ var screenToWoldPoint = Camera.main.ScreenToWorldPoint(new Vector3(value.x, value.y, Mathf.Abs(Camera.main.transform.position.z))); var aimDirTemp = (new Vector3(screenToWoldPoint.x-transform.position.x, screenToWoldPoint.y-transform.position.y, 0)).normalized; if(aimDirTemp.magnitude == 0)return; aimDirTarget = aimDirTemp; } } public void RaiseLowerGrapple(InputAction.CallbackContext context){ if(context.started){ //IsMoving and value should be 1 or -1 changingLength = true; changingLengthDir = -context.ReadValue().y; }else if(context.canceled){ //is not moving and shoud be 0 changingLength = false; changingLengthDir = context.ReadValue().y; } } #endregion }