Untitled
unknown
csharp
3 years ago
23 kB
15
Indexable
using System.Collections.Generic;
//using System.Diagnostics;
using System.Linq;
using Unity.Burst.CompilerServices;
using Unity.VisualScripting;
//using System.Numerics;
using UnityEngine;
using UnityEngine.SocialPlatforms;
using UnityEngine.UIElements;
using static UnityEngine.UIElements.UxmlAttributeDescription;
public class Cast_v2 : MonoBehaviour
{
public float PullForce = 5f;
public int Shots = 2;
public float ClimbSpeed = 1f; //how fast you can move toward grapple point
public float SlideSpeed = 2f; // how fast you can rappel away from grapple point
public float Range = 12f; //grapple range
public float shotspeed = 10f; //cast speed
public float retractspeed = 5f; //retract speed
public float WiggleForce = 1f; //how much you can move while hanging
private float tshot; //timer for shot
private float tret; //timer for retraction
public int ShotsLeft;
public float ropelength = 0;
private Vector2 MousePosonClick;
private LineRenderer lr;
private Vector2 CharPos;
public Rigidbody2D rb;
private Rigidbody2D GrappleBody;
private EdgeCollider2D GrappleObjCollider;
public Movement mvmt;
public DistanceJoint2D Grapple;
public bool WillGrapple;
public Vector2 RopeGrapplePoint;
private Vector2[] GrappleColliderPoints;
private Vector2 GrabPointinWorld;
private RaycastHit2D Updatehit;
private RaycastHit2D hit;
//public LayerMask IgnoreLayer = 7;
private bool clicked = false;
private bool clicking = false;
private bool rclicked = false; //want to eventually make it so rclick is an item grabber
private bool rclicking = false;
private bool ClickedCheck;
private bool ClickingCheck;
private float angle_storage;
public int RangeMode = 0; //0 -- normal range; 1 -- extended range
private Vector2 GrabPointinLocal;
private Dictionary<int, Vector2> WrappingPoints = new();
private Dictionary<int, Rigidbody2D> WrappingBodies = new();
private Dictionary<int, int> WrappingAngleLookup = new();
public bool ReadytoRetract;
Vector2 dir = Vector2.zero;
private bool prevmotorstate;
private bool firstmotorstate;
//private bool FreshlyWrapped = false;
// Start is called before the first frame update
void Start()
{
Grapple = GetComponent<DistanceJoint2D>();
Grapple.enabled = false;
ShotsLeft = Shots;
lr = GetComponent<LineRenderer>();
rb = GetComponent<Rigidbody2D>();
mvmt = GetComponent<Movement>();
}
// Update is called once per frame
void Update()
{
if (rb.velocity.magnitude >= 25f && RangeMode == 0) //increase range if moving fast
{
Range *= 1.1f;
RangeMode = 1;
}
if (rb.velocity.magnitude < 25f && RangeMode == 1) // return to normal range once slowed down
{
Range /= 1.1f;
RangeMode = 0;
}
if (clicked)
{
return;
}
else
{
clicked = Input.GetMouseButtonDown(0);
}
if (rclicked)
{
return;
}
else
{
rclicked = Input.GetMouseButtonDown(1);
}
clicking = Input.GetMouseButton(0);
rclicking = Input.GetMouseButton(1);
}
void FixedUpdate()
{
Vector2 aim = Input.mousePosition;
Vector2 target = Camera.main.ScreenToWorldPoint(aim);
CharPos = rb.position;
dir = -(CharPos - target).normalized; //direction from player to target
//Check and initiate grapple
if (clicked || rclicked)
{
MousePosonClick = target;
if (clicked)
{
IntitialCastCheck();
clicked = false;
if (hit.rigidbody.GetComponent<SliderJoint2D>() != null)// shut off elevator motor so player can pull it down
{
var slider = hit.rigidbody.GetComponent<SliderJoint2D>();
if (slider.limitState == JointLimitState2D.UpperLimit)
{
prevmotorstate = slider.useMotor;
firstmotorstate = prevmotorstate;
slider.useMotor = false;
//???????????????????????????? wanna make it so elevators can lift us while grappling to them, but can still be pulled down by grappling
// maybe use a spring joint
}
}
}
else if (rclicked)
{
tshot = 0;
tret = 0;
rclicked = false;
}
}
//if not going to grapple, still run the shooting animation
if (!WillGrapple && ReadytoRetract)
{
lr.SetPosition(1, CharPos);
var shottime = Range / shotspeed;
Vector3 End = CharPos - Range * (CharPos - MousePosonClick).normalized;
if (tshot < shottime)
{
Vector3 newpos;
newpos = Vector3.Lerp(CharPos, End, tshot / shottime);
lr.SetPosition(0, newpos);
tshot += Time.deltaTime;
}
if (tshot >= shottime)
{
Retract(End);
}
}
//update linerenderer, movement/swinging, wrapping
if (clicking && WillGrapple && WrappingBodies.Count > 0)
{
if (hit.rigidbody.GetComponent<TriggerLift>() != null) //deal with elevator trigger thats controller rotating thing
{
var slider = hit.rigidbody.GetComponent<SliderJoint2D>();
var trigged = hit.rigidbody.GetComponent<TriggerLift>().isTrigged;
if (trigged)
{
prevmotorstate = false;
}
else
{
prevmotorstate = firstmotorstate;
}
}
GrabPointinLocal = WrappingPoints[WrappingPoints.Count - 1]; //grapple point in local coords -- this remains constant as object moves
GrabPointinWorld = WrappingBodies[WrappingBodies.Count - 1].transform.TransformPoint(GrabPointinLocal); //get new grapple point in world coords -- this changes each frame
ropelength = 0;
RopeUpdater();
GrappleMove();
}
//un-hook and retract rope
if (!clicking && WillGrapple && ReadytoRetract)
{
if (hit.rigidbody.GetComponent<SliderJoint2D>() != null)//return elevator to its initial stae
{
var slider = hit.rigidbody.GetComponent<SliderJoint2D>();
slider.useMotor = prevmotorstate;
}
Retract(WrappingBodies[WrappingBodies.Count - 1].transform.TransformPoint(WrappingPoints[WrappingPoints.Count - 1]));
}
}
void OnCollisionExit2D(Collision2D collision) //mark as un-grounded upon leaving ground
{
if (collision.gameObject.CompareTag("Ground"))
{
ShotsLeft = Shots; //reset shots
}
}
void InitialGrabCheck()
{
//will need to use slider joints instead of the distance joint
tshot = 0;
tret = 0;
var mask = ~(1 << 7 | 1 << 2);
hit = Physics2D.CircleCast(CharPos, 0.1f, dir, Range, mask); //raycast from character to target
lr.positionCount = 2;
lr.SetPosition(1, CharPos); //will stay at characer
lr.SetPosition(0, CharPos);// will eventually go to grapple point
if (hit.collider != null) //if the raycast hits, initiate grapple
{
GrappleObjCollider = hit.collider as EdgeCollider2D; //get the target's collider
//GrappleObjCollider = hit.collider as hit.collider.GetType(); //get the target's collider
WrappingBodies.Add(0, hit.rigidbody); //get targets rigidbody
GrabPointinLocal = WrappingBodies[0].transform.InverseTransformPoint(hit.point); //Get the hit-point in the grapple body's ref frame. This will make
//it easy to get connect to moving bodies
//1) move to bodies frame
//2) body moves in world frame, doesnt move in its own frame
//3) move back to world frame to get the new location of the hit point
// on the body (for linerenderer)
//(with GrappleBody, anchor point is in terms of local coords)
WrappingPoints.Add(0, GrabPointinLocal);
WrappingAngleLookup.Add(0, 0);
Grapple.connectedBody = WrappingBodies[0]; //set connected body to grapple rigid body
Grapple.connectedAnchor = WrappingPoints[0]; //set grapple point to hit point
Grapple.distance = hit.distance; //set grapple distance
WillGrapple = true;
Grapple.enabled = true;
}
else if (hit.collider == null)// if raycast doesnt hit anything, dont anchor
{
WillGrapple = false;
}
ReadytoRetract = true;
}
void IntitialCastCheck()
{
tshot = 0;
tret = 0;
if (ShotsLeft > 0)
{
var mask = ~(1 << 7 | 1 << 2); //dont grapple on NoRope and Ignore Raycast layers
hit = Physics2D.CircleCast(CharPos,0.75f,dir,Range, mask); //raycast from character to target
lr.positionCount = 2;
lr.SetPosition(1, CharPos); //will stay at characer
lr.SetPosition(0, CharPos);// will eventually go to grapple point
if (hit.collider != null) //if the raycast hits, initiate grapple
{
GrappleObjCollider = hit.collider as EdgeCollider2D; //get the target's collider
//GrappleObjCollider = hit.collider as hit.collider.GetType(); //get the target's collider
WrappingBodies.Add(0, hit.rigidbody); //get targets rigidbody
GrabPointinLocal = WrappingBodies[0].transform.InverseTransformPoint(hit.point); //Get the hit-point in the grapple body's ref frame. This will make
//it easy to get connect to moving bodies
//1) move to bodies frame
//2) body moves in world frame, doesnt move in its own frame
//3) move back to world frame to get the new location of the hit point
// on the body (for linerenderer)
//(with GrappleBody, anchor point is in terms of local coords)
WrappingPoints.Add(0, GrabPointinLocal); //store intitial grapple point
WrappingAngleLookup.Add(0, 0); //store angle (for unwrapping)
Grapple.connectedBody = WrappingBodies[0]; //set connected body to grapple rigid body
Grapple.connectedAnchor = WrappingPoints[0]; //set grapple point to hit point
Grapple.distance = hit.distance; //set grapple distance
WillGrapple = true;
Grapple.enabled = true; //enable the distance joint
ShotsLeft--; //decrement shots remaining
}
else if (hit.collider == null)// if raycast doesnt hit anything, dont anchor
{
WillGrapple = false;
}
ReadytoRetract = true;
}
}
void GrappleMove() //how movement works while grappled (ie. swinging)
{
float xMove = Input.GetAxisRaw("Horizontal"); //A,D down
float yMove = Input.GetAxisRaw("Vertical"); //W, S down
Vector2 GrappleAnchor = GrabPointinWorld;
Vector2 PlayertoAnchor = -(CharPos - GrappleAnchor).normalized;
List<float> DPs = new() //DPs[0] = DP with up, DPs[1] = DP with right
{
Mathf.Abs(Vector2.Dot(PlayertoAnchor, Vector2.up)),
Mathf.Abs(Vector2.Dot(PlayertoAnchor, Vector2.right))
};
int DirIndex = DPs.IndexOf(DPs.Max());
if (DirIndex == 0) //if grapple point is above or below player
{
var DPVert = Vector2.Dot(PlayertoAnchor, Vector2.up);
rb.AddForce(new Vector2(xMove * WiggleForce, 0), ForceMode2D.Impulse); //x-movement is unchanged
if (DPVert > 0 && ropelength < Range)//if under the anchor point
{
rb.velocity += yMove * ClimbSpeed * PlayertoAnchor; //up is climb, down is slide
Grapple.distance -= yMove * ClimbSpeed;
}
if (DPVert < 0 && ropelength < Range) // if above the anchor
{
rb.velocity += yMove * ClimbSpeed * PlayertoAnchor; // down is climb, up is slide
Grapple.distance += yMove * ClimbSpeed;
}
}
if (DirIndex == 1) //if grapple is to the left or right of player
{
var DPHoriz = Vector2.Dot(PlayertoAnchor, Vector2.right);
rb.AddForce(new Vector2(0, yMove * WiggleForce), ForceMode2D.Impulse);
if (DPHoriz > 0 && ropelength < Range)//if to the left of the anchor
{
rb.velocity += xMove * ClimbSpeed * PlayertoAnchor; // right is climb, left is slide
Grapple.distance -= xMove * ClimbSpeed;
}
if (DPHoriz < 0 && ropelength < Range) // if to the right of the anchor
{
rb.velocity += xMove * ClimbSpeed * PlayertoAnchor; //left is climb, right is slide
Grapple.distance += xMove * ClimbSpeed;
}
}
if (ropelength >= Range) // keep rope from going over its max length
{
Grapple.distance -= (ropelength - Range);
}
}
void RopeUpdater()
{
//Grapple.connectedAnchor = GrabPointinLocal;
//Grapple.connectedBody = WrappingBodies[WrappingPoints.Count - 1];
//initial rope cast
Vector2 Grappoint0inWorld = WrappingBodies[0].transform.TransformPoint(WrappingPoints[0]); // the initial grapple point
var shottime = (CharPos - Grappoint0inWorld).magnitude / shotspeed; //time to reach grapple position
if (tshot < shottime) //cast rope to grapple point animation
{
Vector3 newpos;
newpos = Vector3.Lerp(CharPos, Grappoint0inWorld, tshot / shottime);
lr.SetPosition(0, newpos);
tshot += Time.deltaTime;
}
else if (tshot > shottime) // keep rope at grapple point as it moves
{
//tshot = shottime;
lr.SetPosition(0, Grappoint0inWorld);
RopeGrapplePoint = lr.GetPosition(0);
}
for (int i = 0; i < lr.positionCount - 1; i++)
{
lr.SetPosition(i, WrappingBodies[i].transform.TransformPoint(WrappingPoints[i]));// move rope points along with moving objects
ropelength += (lr.GetPosition(i) - lr.GetPosition(i + 1)).magnitude; // measure rope length
}
lr.SetPosition(lr.positionCount - 1, CharPos); // keep the last rope position on charachter
//Wrapping handler
var dir2 = (GrabPointinWorld - CharPos).normalized; //from character to the current grab point
Updatehit = Physics2D.Raycast(CharPos, dir2, (CharPos - GrabPointinWorld).magnitude - 0.5f); //a new raycast to see if we should wrap the rope
//wrapper
if (Updatehit.collider != null)
{
var WrappingGrappleObj = Updatehit.collider as EdgeCollider2D;
//get (possibly new) grapple object
GrappleColliderPoints = WrappingGrappleObj.points; //get the points of the collider of the grapple object
GrappleBody = Updatehit.rigidbody;
Debug.DrawLine(Updatehit.point, Updatehit.point + Updatehit.normal, Color.red, 10f);
var OrderedDistanceDict = GrappleColliderPoints.ToDictionary<Vector2, float, Vector2> // dictionary that finds the collider point thats closest to the raycast hit point
(position => Vector2.Distance(Updatehit.point, GrappleBody.transform.TransformPoint(position)),
position => GrappleBody.transform.TransformPoint(position)).OrderBy(e => e.Key);
GrabPointinWorld = OrderedDistanceDict.First().Value; //get closest point
GrabPointinLocal = GrappleBody.transform.InverseTransformPoint(GrabPointinWorld); //make closest point the new grapple point
WrappingPoints.Add(WrappingPoints.Count, GrabPointinLocal); //add the new point and rigid body to wrapping dictionaries
WrappingBodies.Add(WrappingBodies.Count, GrappleBody);
WrappingAngleLookup.Add(WrappingAngleLookup.Count, 0);
lr.positionCount++; //add a line render position
Grapple.connectedBody = GrappleBody; // anchor to new object at the new point
Grapple.connectedAnchor = GrabPointinLocal;
Grapple.distance = (CharPos - GrabPointinWorld).magnitude;
lr.SetPosition(lr.positionCount - 1, lr.GetPosition(lr.positionCount - 2)); //put character position as the final linerender point
lr.SetPosition(lr.positionCount - 2, GrabPointinWorld); //2nd last LR point is the new grapple point
}
if (WrappingPoints.Count > 1) //unwrapper
{
/*
Vector2 HingetoChar = (lr.GetPosition(lr.positionCount - 1) - lr.GetPosition(lr.positionCount - 2)); //vector stuff to find the unwrapping angle
Vector2 NextHingetoCurrentHinge = (lr.GetPosition(lr.positionCount - 2) - lr.GetPosition(lr.positionCount - 3));
var interhingeAngle = Vector2.Angle(lr.GetPosition(lr.positionCount - 3), NextHingetoCurrentHinge);
Vector2 NextHingetoChar = (lr.GetPosition(lr.positionCount - 1) - lr.GetPosition(lr.positionCount - 3));
var HingetoCharangle = Vector2.Angle(lr.GetPosition(lr.positionCount - 3), NextHingetoChar);
*/
// Maybe use angles in local coordinates? -- does seem to work better
var lr_1 = GrappleBody.transform.InverseTransformPoint(lr.GetPosition(lr.positionCount - 1));
var lr_2 = GrappleBody.transform.InverseTransformPoint(lr.GetPosition(lr.positionCount - 2));
var lr_3 = GrappleBody.transform.InverseTransformPoint(lr.GetPosition(lr.positionCount - 3));
Vector2 HingetoChar = (lr_1 - lr_2);
Vector2 NextHingetoCurrentHinge = (lr_2 - lr_3);
var interhingeAngle = Vector2.Angle(lr_3, NextHingetoCurrentHinge);
Vector2 NextHingetoChar = (lr_1 - lr_3);
var HingetoCharangle = Vector2.Angle(lr_3, NextHingetoChar);
var ang = HingetoCharangle - interhingeAngle;
Debug.Log(ang);
if (ang < 0)
{
if (WrappingAngleLookup[WrappingAngleLookup.Count - 1] == 1) //if initial angle was positive, unwrap
{
UnwrapHandler();
return;
}
WrappingAngleLookup[WrappingAngleLookup.Count - 1] = -1; //set the wrapping angle indicator to -1 to indicate that the initial angle is negative
}
else
{
if (WrappingAngleLookup[WrappingAngleLookup.Count - 1] == -1) //if initial angle was negative, unwrap
{
UnwrapHandler();
return;
}
WrappingAngleLookup[WrappingAngleLookup.Count - 1] = 1;//set the wrapping angle indicator to +1 to indicate that the initial angle is positive
}
}
}
void UnwrapHandler()
{
WrappingPoints.Remove(WrappingPoints.Count - 1); //remove the current grapple point from the dictionaries
WrappingBodies.Remove(WrappingBodies.Count - 1);
WrappingAngleLookup.Remove(WrappingAngleLookup.Count - 1);
GrappleBody = WrappingBodies[WrappingBodies.Count - 1]; //set new grapple point to the previous grapple point
GrabPointinLocal = WrappingPoints[WrappingPoints.Count - 1];
GrabPointinWorld = GrappleBody.transform.TransformPoint(GrabPointinLocal);
Grapple.connectedBody = GrappleBody;
Grapple.connectedAnchor = GrabPointinLocal; //anchor to new point
Grapple.distance = (GrabPointinWorld - CharPos).magnitude;
lr.SetPosition(lr.positionCount - 2, CharPos);
lr.positionCount--;
Debug.Log("unwrapped");
}
void Retract(Vector2 RetractFrom)
{
Grapple.connectedBody = null; //disconnect joint
Grapple.enabled = false;
var retracttime = (RetractFrom - CharPos).magnitude / retractspeed;
if (tret < retracttime) //run retraction animation
{
lr.SetPosition(lr.positionCount - 1, CharPos);
Vector3 newposret;
newposret = Vector3.Lerp(RetractFrom, CharPos, tret / retracttime);
lr.SetPosition(0, newposret);
tret += Time.deltaTime;
}
else if (tret >= retracttime)
{
WrappingPoints.Clear();
WrappingBodies.Clear();
WrappingAngleLookup.Clear();
lr.positionCount = 0;
ReadytoRetract = false;
}
}
}
Editor is loading...