rabidus-test/Assets/BNG Framework/Scripts/Extras/GrappleShot.cs

366 lines
14 KiB
C#

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace BNG {
public class GrappleShot : GrabbableEvents {
[Header("Range")]
public float MaxRange = 100f;
[Header("CharacterController Grapple Settings")]
[Tooltip("(CharacterController Player Only) How much movement speed to apply to the CharacterController to move towards the grapple")]
public float GrappleReelForce = 0.5f;
[Tooltip("currentGrappleDistance must be greater than this to reel in")]
public float MinReelDistance = 0.25f;
[Header("Rigidbody Grapple Settings")]
[Tooltip("(Rigidbody Player Only) How much force to apply to the player to move towards the grapple")]
public float GrappleForce = 3f;
[Tooltip("(Rigidbody Player Only) Type of ForceMode to use to move the player towards the grapple point. ForceMode. ForceMode.Acceleration will let you preserve momentum and Swing Around. Use lower number for Acceleration (ex : 3). ForceMode.Velocity will immediately alter your player's velocity, resulting in a smooth but linear movement. Use higher numbers (ex : 200).")]
public ForceMode GrappleForceMode = ForceMode.Acceleration;
[Header("Raycast Layers")]
public LayerMask GrappleLayers;
[Header("Component definitions")]
public Transform MuzzleTransform;
public Transform HitTargetPrefab;
public LineRenderer GrappleLine;
public LineRenderer HelperLine;
public AudioClip GrappleShotSound;
// Are we currently grappling towards an object
bool grappling = false;
// Were we grappling last frame
bool wasGrappling = false;
CharacterController characterController;
SmoothLocomotion smoothLocomotion;
PlayerGravity playerGravity;
PlayerClimbing playerClimbing;
Rigidbody playerRigid;
AudioSource audioSource;
// How far away the grapple is in meters
[Header("Shown for Debug :")]
public float currentGrappleDistance = 0;
bool validTargetFound = false;// Is there something valid to grapple on to
bool isDynamic = false; // Can we reel the object in
Rigidbody grappleTargetRigid; // Object we're grappling
Collider grappleTargetCollider; // Collider we're grappling
Transform grappleTargetParent; // Store original parent
bool requireRelease = false; // Require release to be pressed before continuing
bool climbing = false; // Have we transitioned to climbing?
// We will use the climbable as our means of moving and pulling the character around
public Climbable ClimbHelper;
// Start is called before the first frame update
void Start() {
GameObject player = GameObject.FindGameObjectWithTag("Player");
if (player) {
characterController = player.GetComponentInChildren<CharacterController>();
smoothLocomotion = player.GetComponentInChildren<SmoothLocomotion>();
playerGravity = player.GetComponentInChildren<PlayerGravity>();
playerClimbing = player.GetComponentInChildren<PlayerClimbing>();
playerRigid = player.GetComponent<Rigidbody>();
}
else {
Debug.Log("No player object found.");
}
audioSource = GetComponent<AudioSource>();
}
private void LateUpdate() {
// Draw Lines in LateUpdate
// Draw Helper Line
if (!grappling && grab.BeingHeld && !requireRelease) {
drawGrappleHelper();
}
else {
hideGrappleHelper();
}
// Draw Grappling Line
if (grappling && validTargetFound && grab.BeingHeld) {
drawGrappleLine();
}
else {
hideGrappleLine();
}
}
// Try to shoot or grapple
public override void OnTrigger(float triggerValue) {
updateGrappleDistance();
// Fire gun if possible
if (triggerValue >= 0.25f) {
if (grappling) {
reelInGrapple(triggerValue);
}
else {
shootGrapple();
}
}
else {
grappling = false;
requireRelease = false;
}
// User was grappling previous frame, but not now
if(!grappling && wasGrappling) {
onReleaseGrapple();
}
base.OnTrigger(triggerValue);
}
void updateGrappleDistance() {
// Update Distance
if (grappling) {
currentGrappleDistance = Vector3.Distance(MuzzleTransform.position, HitTargetPrefab.position);
}
else {
currentGrappleDistance = 0;
}
}
public override void OnGrab(Grabber grabber) {
base.OnGrab(grabber);
}
public override void OnRelease() {
onReleaseGrapple();
base.OnRelease();
}
// Called when grappling previous frame, but not this one
void onReleaseGrapple() {
// Reset gravity back to normal
changeGravity(true);
if(grappleTargetRigid && isDynamic) {
grappleTargetRigid.useGravity = true;
grappleTargetRigid.isKinematic = false;
grappleTargetRigid.transform.parent = grappleTargetParent;
// More reliable method of resetting parent :
if(grappleTargetRigid.GetComponent<Grabbable>()) {
grappleTargetRigid.GetComponent<Grabbable>().ResetParent();
}
}
// Reset Climbing
ClimbHelper.transform.localPosition = Vector3.zero;
playerClimbing.RemoveClimber(thisGrabber);
climbing = false;
grappling = false;
validTargetFound = false;
isDynamic = false;
wasGrappling = false;
}
// Draw area where Grapple will land
void drawGrappleHelper() {
if (HitTargetPrefab) {
RaycastHit hit;
if (Physics.Raycast(MuzzleTransform.position, MuzzleTransform.forward, out hit, MaxRange, GrappleLayers, QueryTriggerInteraction.Ignore)) {
// Ignore other grapple shots
if (hit.transform.name.StartsWith("Grapple")) {
hideGrappleHelper();
validTargetFound = false;
isDynamic = false;
return;
}
showGrappleHelper(hit.point, Quaternion.FromToRotation(Vector3.forward, hit.normal));
grappleTargetRigid = hit.collider.GetComponent<Rigidbody>();
grappleTargetCollider = hit.collider;
isDynamic = grappleTargetRigid != null && !grappleTargetRigid.isKinematic && grappleTargetRigid.useGravity;
// Parent the helper to the object we hit so it will be moved with it
// Only allow uniform scale so it doesn't warp
bool isUniformVector = hit.collider.transform.localScale.x == hit.collider.transform.localScale.y;
if (isUniformVector || isDynamic) {
HitTargetPrefab.parent = null;
HitTargetPrefab.localScale = Vector3.one;
HitTargetPrefab.transform.parent = hit.collider.transform;
}
else {
HitTargetPrefab.parent = null;
HitTargetPrefab.localScale = Vector3.one;
}
validTargetFound = true;
if(isDynamic) {
grappleTargetParent = grappleTargetRigid.transform.parent;
}
else {
grappleTargetParent = null;
}
}
else {
hideGrappleHelper();
validTargetFound = false;
isDynamic = false;
}
}
}
void drawGrappleLine() {
GrappleLine.gameObject.SetActive(true);
GrappleLine.SetPosition(0, MuzzleTransform.position);
GrappleLine.SetPosition(1, HitTargetPrefab.position);
}
void hideGrappleLine() {
if (GrappleLine && GrappleLine.gameObject.activeSelf) {
GrappleLine.gameObject.SetActive(false);
}
}
void showGrappleHelper(Vector3 pos, Quaternion rot) {
HitTargetPrefab.gameObject.SetActive(true);
HitTargetPrefab.position = pos;
HitTargetPrefab.rotation = rot;
HitTargetPrefab.localScale = Vector3.one;
if (HelperLine) {
HelperLine.gameObject.SetActive(true);
HelperLine.SetPosition(0, MuzzleTransform.position);
HelperLine.SetPosition(1, pos);
}
}
void hideGrappleHelper() {
if(HitTargetPrefab && HitTargetPrefab.gameObject.activeSelf) {
HitTargetPrefab.gameObject.SetActive(false);
}
if (HelperLine && HelperLine.gameObject.activeSelf) {
HelperLine.gameObject.SetActive(false);
}
}
void reelInGrapple(float triggerValue) {
// Has the collider been destroyed or disabled?
if(validTargetFound && grappleTargetCollider != null && !grappleTargetCollider.enabled) {
dropGrapple();
return;
}
if(validTargetFound && currentGrappleDistance > MinReelDistance) {
// Move object towards our hand
if(isDynamic) {
grappleTargetRigid.isKinematic = false;
grappleTargetRigid.transform.parent = grappleTargetParent;
grappleTargetRigid.useGravity = false;
grappleTargetRigid.AddForce((MuzzleTransform.position - grappleTargetRigid.transform.position) * 0.1f, ForceMode.VelocityChange);
//r.MovePosition(MuzzleTransform.position * Time.deltaTime);
}
// Move character towards Hit location
else {
Vector3 moveDirection = (HitTargetPrefab.position - MuzzleTransform.position) * GrappleReelForce;
// Turn off gravity before we move
changeGravity(false);
// Use smooth loco method if available
if(smoothLocomotion) {
if (smoothLocomotion.ControllerType == PlayerControllerType.CharacterController) {
smoothLocomotion.MoveCharacter(moveDirection * Time.deltaTime * triggerValue);
}
else if (smoothLocomotion.ControllerType == PlayerControllerType.Rigidbody) {
if(GrappleForceMode == ForceMode.VelocityChange) {
playerRigid.velocity = Vector3.MoveTowards(playerRigid.velocity, (moveDirection * GrappleForce) * Time.fixedDeltaTime, 1f);
}
else {
playerRigid.AddForce(moveDirection * GrappleForce, GrappleForceMode);
}
}
}
// Fall back to character controller
else if(characterController) {
characterController.Move(moveDirection * Time.deltaTime * triggerValue);
}
}
}
else if(validTargetFound && currentGrappleDistance <= MinReelDistance) {
if (isDynamic) {
//grappleTargetRigid.useGravity = true;
grappleTargetRigid.velocity = Vector3.zero;
grappleTargetRigid.isKinematic = true;
grappleTargetRigid.transform.parent = transform;
}
if(!climbing && !isDynamic) {
// Add climbable / grabber
ClimbHelper.transform.localPosition = Vector3.zero;
playerClimbing.AddClimber(ClimbHelper, thisGrabber);
climbing = true;
}
}
}
// Shoot the valid out if valid target
void shootGrapple() {
if(validTargetFound) {
// Play Grapple Sound
if(GrappleShotSound && audioSource) {
audioSource.clip = GrappleShotSound;
audioSource.pitch = Time.timeScale;
audioSource.Play();
}
grappling = true;
wasGrappling = true;
requireRelease = true;
}
}
void dropGrapple() {
grappling = false;
validTargetFound = false;
isDynamic = false;
wasGrappling = false;
}
void changeGravity(bool gravityOn) {
if(playerGravity) {
playerGravity.ToggleGravity(gravityOn);
}
}
}
}