Guest User

Untitled

a guest
Dec 13th, 2017
116
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 16.00 KB | None | 0 0
  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. using System.Linq;
  5.  
  6. public class RopeSystem : MonoBehaviour {
  7.  
  8. public DistanceJoint2D ropeJoint;
  9. public Transform crosshair;
  10. public SpriteRenderer crosshairSprite;
  11. public PlayerMovement playerMovement;
  12. private bool ropeAttached;
  13. private Vector2 playerPosition; // Stores player's world coords
  14.  
  15. // RopeHingeAnchor Vars
  16. public GameObject ropeHingeAnchor;
  17. private Rigidbody2D ropeHingeAnchorRb;
  18. private SpriteRenderer ropeHingeAnchorSprite;
  19.  
  20. private Rigidbody2D grappleObjectRb;
  21.  
  22. // RayCast vars
  23. public LineRenderer ropeRenderer;
  24. public LayerMask ropeLayerMask;
  25. private float ropeMaxCastDistance = 20f;
  26. private List<Vector2> ropePositions = new List<Vector2>(); // Contains all rope positions for wrapping around objects
  27.  
  28. private bool distanceSet; // Flag to let the script know that the rope's distance has been set correctly
  29.  
  30. private Dictionary<Vector2, int> wrapPointsLookup = new Dictionary<Vector2, int>(); // Stores all points of polygon collider that hook is curently attached to
  31.  
  32. // Rappeling Vars
  33. public float climbSpeed = 3f;
  34. private bool isColliding;
  35.  
  36. //Vars to brach behaviour for different type of objects hit by hook
  37. private string currentlyAttachedTag;
  38.  
  39. void Awake() {
  40. ropeJoint.enabled = false;
  41. playerPosition = transform.position;
  42. ropeHingeAnchorRb = ropeHingeAnchor.GetComponent<Rigidbody2D>();
  43. ropeHingeAnchorSprite = ropeHingeAnchor.GetComponent<SpriteRenderer>();
  44. }
  45.  
  46. void Update() {
  47.  
  48. float aimAngle = CalculateAimAngle();
  49.  
  50. // Convert angle from player to cursor to degrees. This will be used later for Raycasting from plyer to cursor
  51. Vector2 aimDirection = Quaternion.Euler(0, 0, aimAngle * Mathf.Rad2Deg) * Vector2.right;
  52.  
  53. //keep tracking player world coords
  54. playerPosition = transform.position;
  55.  
  56. //Determine if the rope is attached to an anchor point
  57. if (ropeAttached) {
  58. crosshairSprite.enabled = false;
  59.  
  60. switch (currentlyAttachedTag) {
  61. case ("GrappleTerrain"):
  62. playerMovement.isSwinging = true;
  63. playerMovement.ropeHook = ropePositions.Last();
  64. HandleRopeWrapping();
  65. HandleRopeUnwrapping();
  66. break;
  67. case ("GrappleObject"):
  68. playerMovement.isSwinging = true;
  69. playerMovement.ropeHook = grappleObjectRb.transform.position;
  70. break;
  71. }
  72.  
  73. } else {
  74. playerMovement.isSwinging = false;
  75.  
  76. SetCrosshairPosition(aimAngle);
  77. }
  78.  
  79. UpdateRopePositions();
  80. HandlePlayerInput(aimDirection);
  81. HandleRopeLength();
  82. }
  83.  
  84. #region Rope Wrapping Methods
  85.  
  86. //Wraps Rope around edges of polygoncolliders that touch the rope
  87. private void HandleRopeWrapping() {
  88. //Check if ropePositions list has any positions stored
  89. if (ropePositions.Count > 0) {
  90. // Fire a raycast out from the player's position, in the direction of the player looking at the last rope position in the list
  91. var lastRopePoint = ropePositions.Last();
  92. var playerToCurrentNextHit = Physics2D.Raycast(playerPosition, (lastRopePoint - playerPosition).normalized, Vector2.Distance(playerPosition, lastRopePoint) - 0.1f, ropeLayerMask);
  93.  
  94. // If the raycast hits something, then that hit object's collider is safe cast to a PolygonCollider2D.
  95. // As long as it's a real PolygonCollider2D, then the closest vertex position on that collider is returned as a Vector2,
  96. if (playerToCurrentNextHit) {
  97. var colliderWithVertices = playerToCurrentNextHit.collider as PolygonCollider2D;
  98. if (colliderWithVertices != null) {
  99. var closestPointToHit = GetClosestColliderPointFromRaycastHit(playerToCurrentNextHit, colliderWithVertices);
  100.  
  101. // The wrapPointsLookup is checked to make sure the same position is not being wrapped again.
  102. // If it is, then it'll reset the rope and cut it, dropping the player.
  103. if (wrapPointsLookup.ContainsKey(closestPointToHit)) {
  104. ResetRope();
  105. return;
  106. }
  107.  
  108. // The wrapPointsLookup dictionar and ropePositions list is now updated, adding the position the rope should wrap around
  109. // DistanceSet flag is disabled, so that UpdateRopePositions() method can re-configure the rope's distances to take into account the new rope length and segments.
  110. ropePositions.Add(closestPointToHit);
  111. wrapPointsLookup.Add(closestPointToHit, 0);
  112. distanceSet = false;
  113. }
  114. }
  115. }
  116. }
  117.  
  118. //Unwraps Rope around edges of polygoncolliders that touch the rope
  119. private void HandleRopeUnwrapping() {
  120. //Ignore if rope attached at 1 point only
  121. if (ropePositions.Count <= 1) {
  122. return;
  123. }
  124.  
  125. // Hinge = next point up from the player position
  126. // Anchor = next point up from the Hinge
  127. // Hinge Angle = Angle between anchor and hinge
  128. // Player Angle = Angle between anchor and player
  129.  
  130. // anchorIndex is the index in the ropePositions collection two positions from the end of the collection.
  131. var anchorIndex = ropePositions.Count - 2;
  132. // index in the collection where the current hinge point is stored
  133. var hingeIndex = ropePositions.Count - 1;
  134.  
  135. Vector2 anchorPosition = ropePositions[anchorIndex];
  136. Vector2 hingePosition = ropePositions[hingeIndex];
  137. Vector2 hingeDir = hingePosition - anchorPosition;
  138.  
  139. // Angle between hing & anchor point
  140. float hingeAngle = Vector2.Angle(anchorPosition, hingeDir);
  141. // Vector that points from anchorPosition to the player pos
  142. var playerDir = playerPosition - anchorPosition;
  143. // angle between the anchor point and the player
  144. var playerAngle = Vector2.Angle(anchorPosition, playerDir);
  145.  
  146. if (!wrapPointsLookup.ContainsKey(hingePosition)) {
  147. Debug.LogError("We were not tracking hingePosition (" + hingePosition + ") in the look up dictionary.");
  148. return;
  149. }
  150.  
  151. if (playerAngle < hingeAngle) {
  152. if (wrapPointsLookup[hingePosition] == 1) {
  153. UnwrapRopePosition(anchorIndex, hingeIndex);
  154. return;
  155. }
  156.  
  157. wrapPointsLookup[hingePosition] = -1;
  158. } else {
  159. if (wrapPointsLookup[hingePosition] == -1) {
  160. UnwrapRopePosition(anchorIndex, hingeIndex);
  161. return;
  162. }
  163.  
  164. wrapPointsLookup[hingePosition] = 1;
  165. }
  166. }
  167.  
  168. private void UnwrapRopePosition(int anchorIndex, int hingeIndex) {
  169. // 1
  170. var newAnchorPosition = ropePositions[anchorIndex];
  171. wrapPointsLookup.Remove(ropePositions[hingeIndex]);
  172. ropePositions.RemoveAt(hingeIndex);
  173.  
  174. // 2
  175. ropeHingeAnchorRb.transform.position = newAnchorPosition;
  176. distanceSet = false;
  177.  
  178. // Set new rope distance joint distance for anchor position if not yet set.
  179. if (distanceSet) {
  180. return;
  181. }
  182. ropeJoint.distance = Vector2.Distance(transform.position, newAnchorPosition);
  183. distanceSet = true;
  184. }
  185.  
  186.  
  187. //Update the LineRenderer for Rope Effect
  188. private void UpdateRopePositions() {
  189. //Ignore if no rope
  190. if (!ropeAttached) {
  191. return;
  192. }
  193.  
  194. switch (currentlyAttachedTag) {
  195. case ("GrappleTerrain"):
  196. //Count number of vertexs(including player)
  197. ropeRenderer.positionCount = ropePositions.Count + 1;
  198.  
  199. //Set LineRenderer positions to be same as list of rope positions
  200. for (var i = ropeRenderer.positionCount - 1; i >= 0; i--) {
  201. if (i != ropeRenderer.positionCount - 1) // if not the Last point of line renderer
  202. {
  203. ropeRenderer.SetPosition(i, ropePositions[i]);
  204.  
  205. //Set the rope anchor to the second-to-last rope position where the current hinge/anchor should be
  206. if (i == ropePositions.Count - 1 || ropePositions.Count == 1) {
  207. var ropePosition = ropePositions[ropePositions.Count - 1];
  208. if (ropePositions.Count == 1) {
  209. ropeHingeAnchorRb.transform.position = ropePosition;
  210. if (!distanceSet) {
  211. ropeJoint.distance = Vector2.Distance(transform.position, ropePosition);
  212. distanceSet = true;
  213. }
  214. } else {
  215. ropeHingeAnchorRb.transform.position = ropePosition;
  216. if (!distanceSet) {
  217. ropeJoint.distance = Vector2.Distance(transform.position, ropePosition);
  218. distanceSet = true;
  219. }
  220. }
  221. }
  222. //Rope position being looped over is the second-to-last one
  223. else if (i - 1 == ropePositions.IndexOf(ropePositions.Last())) {
  224. var ropePosition = ropePositions.Last();
  225. ropeHingeAnchorRb.transform.position = ropePosition;
  226. if (!distanceSet) {
  227. ropeJoint.distance = Vector2.Distance(transform.position, ropePosition);
  228. distanceSet = true;
  229. }
  230. }
  231. } else {
  232. //Set the rope's last vertex position to the player's current position.
  233. ropeRenderer.SetPosition(i, transform.position);
  234. }
  235. }
  236. break;
  237. case ("GrappleObject"):
  238. ropeRenderer.SetPosition(0, transform.position);
  239. ropeRenderer.SetPosition(1, grappleObjectRb.transform.position);
  240. break;
  241. }
  242.  
  243.  
  244. }
  245.  
  246. //WARNING: Requires all objects to be hooked to have a polygon collider
  247. private Vector2 GetClosestColliderPointFromRaycastHit(RaycastHit2D hit, PolygonCollider2D polyCollider) {
  248. //Converts the polygon collider's collection of points, into a dictionary of Vector2 positions
  249. //The key of each entry, is set to the distance that this point is to the player's position
  250. var distanceDictionary = polyCollider.points.ToDictionary<Vector2, float, Vector2>(
  251. position => Vector2.Distance(hit.point, polyCollider.transform.TransformPoint(position)),
  252. position => polyCollider.transform.TransformPoint(position));
  253.  
  254. //Dictionary ordered by distance closest to the player's current position, and the closest one is returned
  255. var orderedDictionary = distanceDictionary.OrderBy(e => e.Key);
  256. return orderedDictionary.Any() ? orderedDictionary.First().Value : Vector2.zero;
  257. }
  258.  
  259. # endregion
  260.  
  261. //Returns the angle from the player to the cursor as a radian
  262. private float CalculateAimAngle() {
  263. var worldMousePosition = Camera.main.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, 0f));
  264. var facingDirection = worldMousePosition - transform.position;
  265. var aimAngle = Mathf.Atan2(facingDirection.y, facingDirection.x);
  266. if (aimAngle < 0f) {
  267. aimAngle = Mathf.PI * 2 + aimAngle;
  268. }
  269. return aimAngle;
  270. }
  271.  
  272. //Set the crosshair sprite 1.5 units in between player & cursor
  273. private void SetCrosshairPosition(float aimAngle) {
  274. if (!crosshairSprite.enabled) {
  275. crosshairSprite.enabled = true;
  276. }
  277.  
  278. var x = transform.position.x + 1.5f * Mathf.Cos(aimAngle);
  279. var y = transform.position.y + 1.5f * Mathf.Sin(aimAngle);
  280.  
  281. var crossHairPosition = new Vector3(x, y, 0);
  282. crosshair.transform.position = crossHairPosition;
  283. }
  284.  
  285. //Handle Input from player playing game
  286. private void HandlePlayerInput(Vector2 aimDirection) {
  287. //On Player Left Click
  288. if (Input.GetMouseButton(0)) {
  289. ShootGrapple(aimDirection);
  290. }
  291.  
  292. //On Player Right Click
  293. if (Input.GetMouseButton(1)) {
  294. ResetRope();
  295. }
  296. }
  297.  
  298. //Fire Grapple Hook at player mouse cursor direction
  299. private void ShootGrapple(Vector2 aimDirection) {
  300. // Ignore if rope already attached
  301. if (ropeAttached) return;
  302. ropeRenderer.enabled = true;
  303.  
  304. RaycastHit2D hit = Physics2D.Raycast(playerPosition, aimDirection, ropeMaxCastDistance, ropeLayerMask);
  305.  
  306. // Raycast hit something
  307. if (hit.collider != null) {
  308.  
  309. currentlyAttachedTag = hit.collider.tag;
  310.  
  311. switch (currentlyAttachedTag) {
  312. case ("GrappleTerrain"):
  313. ropeAttached = true;
  314.  
  315. //Check if raycast hit position is new
  316. if (!ropePositions.Contains(hit.point)) {
  317. // Jump slightly to distance the player a little from the ground after grappling to something.
  318. transform.GetComponent<Rigidbody2D>().AddForce(new Vector2(0f, 2f), ForceMode2D.Impulse);
  319. ropePositions.Add(hit.point);
  320. ropeJoint.connectedBody = ropeHingeAnchorRb;
  321. ropeJoint.distance = Vector2.Distance(playerPosition, hit.point);
  322. ropeJoint.enabled = true;
  323. ropeHingeAnchorSprite.enabled = true;
  324. }
  325. break;
  326. case ("GrappleObject"):
  327. ropeAttached = true;
  328.  
  329. ropeHingeAnchor.transform.position = hit.point;
  330. ropeHingeAnchor.transform.parent = hit.collider.transform;
  331. grappleObjectRb = hit.collider.GetComponent<Rigidbody2D>();
  332. ropeJoint.connectedBody = grappleObjectRb;
  333. ropeJoint.distance = Vector2.Distance(playerPosition, hit.collider.transform.position);
  334. ropeJoint.enabled = true;
  335. break;
  336. }
  337.  
  338. }
  339. // Disable Rope Vars & Visuals
  340. else {
  341. ropeRenderer.enabled = false;
  342. ropeAttached = false;
  343. ropeJoint.enabled = false;
  344. }
  345. }
  346.  
  347. //Reset Rope Vars & Visuals
  348. private void ResetRope() {
  349. ropeJoint.enabled = false;
  350. ropeAttached = false;
  351. playerMovement.isSwinging = false;
  352. ropeRenderer.positionCount = 2;
  353. ropeRenderer.SetPosition(0, transform.position);
  354. ropeRenderer.SetPosition(1, transform.position);
  355. ropePositions.Clear();
  356. ropeHingeAnchorSprite.enabled = false;
  357. wrapPointsLookup.Clear();
  358. currentlyAttachedTag = "None";
  359. }
  360.  
  361. //Shortens/Lengthens Rope Length depending on player input
  362. private void HandleRopeLength() {
  363. if (Input.GetAxis("Vertical") >= 1f && ropeAttached && !isColliding) {
  364. ropeJoint.distance -= Time.deltaTime * climbSpeed;
  365. } else if (Input.GetAxis("Vertical") < 0f && ropeAttached) {
  366. ropeJoint.distance += Time.deltaTime * climbSpeed;
  367. }
  368.  
  369. //Detach rope & boost player up if rope distance is too short
  370. if(ropeAttached && ropeJoint.distance <= 0.5f) {
  371. transform.GetComponent<Rigidbody2D>().AddForce(new Vector2(0f, 15f), ForceMode2D.Impulse);
  372. ResetRope();
  373. }
  374. }
  375.  
  376. #region Collision Checking Methods
  377.  
  378. void OnTriggerStay2D(Collider2D colliderStay) {
  379. isColliding = true;
  380. }
  381.  
  382. private void OnTriggerExit2D(Collider2D colliderOnExit) {
  383. isColliding = false;
  384. }
  385.  
  386. #endregion
  387.  
  388.  
  389. }
Add Comment
Please, Sign In to add comment