From 1c9b3221a13e6971044e33f2cec389e3ae2e0050 Mon Sep 17 00:00:00 2001 From: Pampattitude Date: Mon, 5 Oct 2015 19:40:45 +0200 Subject: [PATCH] added NinjaHook, example Player Controller and made NinjaRope customizable Added a NinjaHook script to have a dynamic hook. Included is also an example, "NinjaRopePlayerController" (note that the "ExamplePlayerController" has been renamed to "PermanentRopePlayerController"). Also, made the NinjaRope be able to have an unlimited size. --- NinjaHook.cs | 88 +++++++++++++++++ NinjaRope.cs | 39 ++++++-- NinjaRopePlayerController.cs | 96 +++++++++++++++++++ ...ler.cs => PermanentRopePlayerController.cs | 2 +- 4 files changed, 217 insertions(+), 8 deletions(-) create mode 100644 NinjaHook.cs create mode 100644 NinjaRopePlayerController.cs rename ExamplePlayerController.cs => PermanentRopePlayerController.cs (95%) diff --git a/NinjaHook.cs b/NinjaHook.cs new file mode 100644 index 0000000..d321f52 --- /dev/null +++ b/NinjaHook.cs @@ -0,0 +1,88 @@ +using UnityEngine; +using System.Collections; + +public class NinjaHook: MonoBehaviour { + public Vector3 direction; + public float speed; + + private bool isHooked_ = false; + public delegate void HookAction(); + public event HookAction onHooked; + + private Rigidbody2D rigidbody_; + + protected void Start() { + this.initRigidbody_(); + + // Normalize direction and adapt hook angle + this.direction.Normalize(); + gameObject.transform.eulerAngles = new Vector3(0f, 0f, NinjaHook.pointsToAngle(Vector3.zero, this.direction)); + } + + protected void FixedUpdate() { + // When hooked, the hook stops moving + if (this.isHooked_) + return ; + + // Make sure the rotation is right (because of the parent)... + gameObject.transform.eulerAngles = new Vector3(0f, 0f, NinjaHook.pointsToAngle(Vector3.zero, this.direction)); + // ... and also make sure the velocity hasn't changed + this.rigidbody_.velocity = this.direction * this.speed; + } + + + // Initialization + private void initRigidbody_() { + this.rigidbody_ = gameObject.GetComponent(); + if (!this.rigidbody_) { + this.rigidbody_ = gameObject.AddComponent(); + + // No mass, no gravity. The entity is entirely managed by script + this.rigidbody_.mass = 0f; + this.rigidbody_.gravityScale = 0f; + } + + this.rigidbody_.interpolation = RigidbodyInterpolation2D.Interpolate; + this.rigidbody_.collisionDetectionMode = CollisionDetectionMode2D.Continuous; + this.rigidbody_.sleepMode = RigidbodySleepMode2D.StartAwake; + } + // !Initialization + + + // Listeners + protected void OnCollisionEnter2D(Collision2D c) { + // Make kinematic to ensure the hook won't move anymore + this.rigidbody_.isKinematic = true; + + // Stick it to the collided obstacle + gameObject.transform.position = Physics2D.Raycast(gameObject.transform.position, this.direction).point; + gameObject.transform.eulerAngles = new Vector3(0f, 0f, NinjaHook.pointsToAngle(Vector3.zero, this.direction)); + + this.isHooked_ = true; + if (null != this.onHooked) + this.onHooked(); + } + protected void OnTriggerEnter2D(Collider2D c) { + // Make kinematic to ensure the hook won't move anymore + this.rigidbody_.isKinematic = true; + + // Stick it to the collided obstacle + gameObject.transform.position = Physics2D.Raycast(gameObject.transform.position, this.direction).point; + gameObject.transform.eulerAngles = new Vector3(0f, 0f, NinjaHook.pointsToAngle(Vector3.zero, this.direction)); + + this.isHooked_ = true; + if (null != this.onHooked) + this.onHooked(); + } + // !Listeners + + + // Util + public bool isHooked() { return this.isHooked_; } + + private static float pointsToAngle(Vector2 anc1, Vector2 anc2) { + // Returns the angle between both vectors; note that the angle is NOT between -180 and 180 + return (Mathf.Atan2(anc2.y - anc1.y, anc2.x - anc1.x) * Mathf.Rad2Deg - 90f); + } + // !Util +} diff --git a/NinjaRope.cs b/NinjaRope.cs index b11e7e9..bfbce0b 100644 --- a/NinjaRope.cs +++ b/NinjaRope.cs @@ -1,12 +1,7 @@ -using UnityEngine; +using UnityEngine; using System.Collections; using System.Collections.Generic; -/* - * Game object must have: - * - the script (duh) - * - a line renderer - */ [RequireComponent(typeof(LineRenderer))] public class NinjaRope: MonoBehaviour { private Rigidbody2D rigidbody_; @@ -142,8 +137,16 @@ private void updateJoint_() { DistanceJoint2D joint = gameObject.GetComponent(); if (!joint) { joint = gameObject.AddComponent(); - joint.maxDistanceOnly = false; + + if (float.MaxValue - 0.001f > this.ropeLength) + joint.maxDistanceOnly = false; + else + joint.maxDistanceOnly = true; } + if (float.MaxValue - 0.001f > this.ropeLength) + joint.maxDistanceOnly = false; + else + joint.maxDistanceOnly = true; joint.anchor = gameObject.transform.InverseTransformPoint(this.lastAnchor.anchor); joint.connectedBody = this.endEntity.GetComponent(); @@ -231,5 +234,27 @@ private float getActualRopeLength_() { dist += Vector3.Distance(this.anchors_[i].anchor, this.anchors_[i + 1].anchor); return dist; } + // Different from the previous one because it also accounts for the last chunk, ending with the target + public float getRopeLength() { + float dist = 0f; + for (int i = 0 ; this.anchors_.Count > i + 1 ; ++i) + dist += Vector3.Distance(this.anchors_[i].anchor, this.anchors_[i + 1].anchor); + return dist; + } + + public void setLength(float len) { + this.ropeLength = len; + + DistanceJoint2D joint = gameObject.GetComponent(); + if (joint) + joint.maxDistanceOnly = false; + } + public void removeLength() { + this.ropeLength = float.MaxValue; + + DistanceJoint2D joint = gameObject.GetComponent(); + if (joint) + joint.maxDistanceOnly = true; + } // !Utils } diff --git a/NinjaRopePlayerController.cs b/NinjaRopePlayerController.cs new file mode 100644 index 0000000..2f810db --- /dev/null +++ b/NinjaRopePlayerController.cs @@ -0,0 +1,96 @@ +using UnityEngine; +using System.Collections; + +public class NinjaRopePlayerController: MonoBehaviour { + private float speed_ = 120f, ropeSpeed_ = 5f; + private float minRopeLength_ = 0.1f, maxRopeLength_ = 12f; + private float ropeAngle_ = 20f; + + public GameObject hookPrefab; + private GameObject hook_; + private bool isHooking_ = false; + + public GameObject ropePrefab; + private GameObject rope_; + + void Update() { + if (!this.isHooking_) { + // No hook launched / hooked, so can launch it + if (Input.GetKeyDown(KeyCode.A)) + this.launchHook_(Quaternion.Euler(0f, 0f, this.ropeAngle_) * Vector3.up); + if (Input.GetKeyDown(KeyCode.D)) + this.launchHook_(Quaternion.Euler(0f, 0f, -this.ropeAngle_) * Vector3.up); + } + else { + // Hook is either launched or hooked + NinjaRope rope = this.rope_.GetComponent(); + + // No input, remove the hook + if (Input.GetKeyUp(KeyCode.A) || + Input.GetKeyUp(KeyCode.D)) { + this.removeHook_(); + return ; + } + + if (!this.hook_.GetComponent().isHooked()) { + // The hook is launched, but not yet hooked + if (this.maxRopeLength_ < Vector3.Distance(this.hook_.transform.position, gameObject.transform.position)) // Remove hook if it took too long to find a spot to hook on + this.removeHook_(); + } + else { + // The hook is hooked on a surface + Vector3 upVector = (rope.lastAnchor.anchor - gameObject.transform.position).normalized, + rightVector = (Quaternion.Euler(0f, 0f, -90f) * upVector).normalized; + Vector3 posDelta = Vector3.zero; + + // Move the player + if (Input.GetKey(KeyCode.A)) + posDelta -= rightVector * this.speed_ * Time.fixedDeltaTime; + if (Input.GetKey(KeyCode.D)) + posDelta += rightVector * this.speed_ * Time.fixedDeltaTime; + + if (0.0001f < posDelta.magnitude) + gameObject.GetComponent().AddForce(posDelta); + + // Move up or down the rope + if (Input.GetKey(KeyCode.W)) + rope.ropeLength -= this.ropeSpeed_ * Time.fixedDeltaTime; + if (Input.GetKey(KeyCode.S)) + rope.ropeLength += this.ropeSpeed_ * Time.fixedDeltaTime; + + if (this.minRopeLength_ > rope.ropeLength) + rope.ropeLength = this.minRopeLength_; + if (this.maxRopeLength_ < rope.ropeLength) + rope.ropeLength = this.maxRopeLength_; + } + } + } + + private void launchHook_(Vector3 spd) { + // Instantiate hook, and make the player the hook parent to ensure the hook position is affected by the player (TMP: DOES NOT WORK) + this.hook_ = Object.Instantiate(this.hookPrefab, gameObject.transform.position, Quaternion.identity) as GameObject; + this.hook_.GetComponent().direction = spd; + + this.rope_ = Object.Instantiate(this.ropePrefab); + this.rope_.GetComponent().baseEntity = this.hook_; + this.rope_.GetComponent().endEntity = gameObject; + this.rope_.GetComponent().removeLength(); // Remove the max length of the rope + + this.hook_.GetComponent().onHooked += this.onHooked; + + this.isHooking_ = true; + } + + private void removeHook_() { + // Destroy both the hook and the rope (duh!) + Destroy(this.hook_); + Destroy(this.rope_); + + this.isHooking_ = false; + } + + public void onHooked() { + // Reset the max length of the rope + this.rope_.GetComponent().setLength(this.rope_.GetComponent().getRopeLength() + gameObject.GetComponent().velocity.magnitude * Time.deltaTime); + } +} diff --git a/ExamplePlayerController.cs b/PermanentRopePlayerController.cs similarity index 95% rename from ExamplePlayerController.cs rename to PermanentRopePlayerController.cs index 7e035ad..f882ae8 100644 --- a/ExamplePlayerController.cs +++ b/PermanentRopePlayerController.cs @@ -1,7 +1,7 @@ using UnityEngine; using System.Collections; -public class ExamplePlayerController: MonoBehaviour { +public class PermanentRopePlayerController: MonoBehaviour { private float speed_ = 104f, ropeSpeed_ = 5f; private float minRopeLength_ = 1f, maxRopeLength_ = 8f;