OpenXR开发实战项目之VR Interaction Fram
2022-05-03 本文已影响0人
TonyWan_AR
一、框架视图
二、关键代码
GrabAction
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
namespace BNG {
public class GrabAction : GrabbableEvents {
public GrabberEvent OnGrabEvent;
Grabbable g;
float lastGrabTime = 0;
float minTimeBetweenGrabs = 0.2f; // In Seconds
public override void OnGrab(Grabber grabber) {
if(g == null) {
g = GetComponent<Grabbable>();
}
g.DropItem(grabber, false, false);
if(grabber.RemoteGrabbingItem || grabber.HoldingItem) {
return;
}
if (OnGrabEvent != null) {
if(Time.time - lastGrabTime >= minTimeBetweenGrabs) {
OnGrabEvent.Invoke(grabber);
lastGrabTime = Time.time;
}
}
}
}
}
GrabbableHighlight
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace BNG {
public class GrabbableHighlight : GrabbableEvents {
public bool HighlightOnGrabbable = true;
public bool HighlightOnRemoteGrabbable = true;
public override void OnGrab(Grabber grabber) {
UnhighlightItem();
}
public override void OnBecomesClosestGrabbable(ControllerHand touchingHand) {
if (HighlightOnGrabbable) {
HighlightItem();
}
}
public override void OnNoLongerClosestGrabbable(ControllerHand touchingHand) {
if (HighlightOnGrabbable) {
UnhighlightItem();
}
}
public override void OnBecomesClosestRemoteGrabbable(ControllerHand touchingHand) {
if (HighlightOnRemoteGrabbable) {
HighlightItem();
}
}
public override void OnNoLongerClosestRemoteGrabbable(ControllerHand touchingHand) {
if (HighlightOnRemoteGrabbable) {
UnhighlightItem();
}
}
public void HighlightItem() {
}
public void UnhighlightItem() {
}
}
}
UIPointer
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
namespace BNG {
public class UIPointer : MonoBehaviour {
[Tooltip("The controller side this pointer is on")]
public ControllerHand ControllerSide = ControllerHand.Right;
[Tooltip("If true this object will update the VRUISystem's Left or Right Transform property")]
public bool AutoUpdateUITransforms = true;
public GameObject cursor;
private GameObject _cursor;
[Tooltip("If true the cursor and LineRenderer will be Hidden. Otherwise it will still be show at a fixed length")]
public bool HidePointerIfNoObjectsFound = true;
[Tooltip("How long the line / cursor should extend if no objects are found to point at")]
public float FixedPointerLength = 0.5f;
[Tooltip("If true the cursor object will scale based on how far away the pointer is from the origin. A cursor far away will have a larger cusor than one up close.")]
public bool CursorScaling = true;
[Tooltip("Minimum scale of the Cursor object if CursorScaling is enabled")]
public float CursorMinScale = 0.6f;
public float CursorMaxScale = 6.0f;
private Vector3 _cursorInitialLocalScale;
[Tooltip("Example : 0.5 = Line Goes Half Way. 1 = Line reaches end.")]
public float LineDistanceModifier = 0.8f;
VRUISystem uiSystem;
PointerEvents selectedPointerEvents;
PointerEventData data;
[Tooltip("LineRenderer to use when showing a valid UI Canvas. Leave null to attempt a GetComponent<> on this object.")]
public LineRenderer lineRenderer;
void Awake() {
if(cursor) {
_cursor = GameObject.Instantiate(cursor);
_cursor.transform.SetParent(transform);
_cursorInitialLocalScale = transform.localScale;
}
if (lineRenderer == null) {
lineRenderer = GetComponent<LineRenderer>();
}
uiSystem = VRUISystem.Instance;
}
void OnEnable() {
if (AutoUpdateUITransforms && ControllerSide == ControllerHand.Left) {
uiSystem.LeftPointerTransform = this.transform;
}
else if (AutoUpdateUITransforms && ControllerSide == ControllerHand.Right) {
uiSystem.RightPointerTransform = this.transform;
}
uiSystem.UpdateControllerHand(ControllerSide);
}
public void Update() {
data = uiSystem.EventData;
// Can bail early if not looking at anything
if (data == null || data.pointerCurrentRaycast.gameObject == null) {
HidePointer();
return;
}
if (_cursor != null ) {
bool lookingAtUI = data.pointerCurrentRaycast.module.GetType() == typeof(GraphicRaycaster);
selectedPointerEvents = data.pointerCurrentRaycast.gameObject.GetComponent<PointerEvents>();
bool lookingAtPhysicalObject = selectedPointerEvents != null;
if(lookingAtPhysicalObject) {
if (data.pointerCurrentRaycast.distance > selectedPointerEvents.MaxDistance) {
HidePointer();
return;
}
}
if(!lookingAtUI && !lookingAtPhysicalObject) {
HidePointer();
return;
}
float distance = Vector3.Distance(transform.position, data.pointerCurrentRaycast.worldPosition);
_cursor.transform.localPosition = new Vector3(0, 0, distance - 0.0001f);
_cursor.transform.rotation = Quaternion.FromToRotation(Vector3.forward, data.pointerCurrentRaycast.worldNormal);
float cameraDist = Vector3.Distance(Camera.main.transform.position, _cursor.transform.position);
_cursor.transform.localScale = _cursorInitialLocalScale * Mathf.Clamp(cameraDist, CursorMinScale, CursorMaxScale);
_cursor.SetActive(data.pointerCurrentRaycast.distance > 0);
}
if (lineRenderer) {
lineRenderer.useWorldSpace = false;
lineRenderer.SetPosition(0, Vector3.zero);
lineRenderer.SetPosition(1, new Vector3(0, 0 , Vector3.Distance(transform.position, data.pointerCurrentRaycast.worldPosition) * LineDistanceModifier));
lineRenderer.enabled = data.pointerCurrentRaycast.distance > 0;
}
}
public virtual void HidePointer() {
if(HidePointerIfNoObjectsFound) {
_cursor.SetActive(false);
lineRenderer.enabled = false;
}
else {
if (_cursor) {
_cursor.SetActive(false);
}
if (lineRenderer) {
lineRenderer.useWorldSpace = false;
lineRenderer.SetPosition(0, Vector3.zero);
lineRenderer.SetPosition(1, new Vector3(0, 0, FixedPointerLength * LineDistanceModifier));
lineRenderer.enabled = true;
}
}
}
}
}
GrabbablesInTrigger
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace BNG {
public class GrabbablesInTrigger : MonoBehaviour {
public Dictionary<Collider, Grabbable> NearbyGrabbables;
public Dictionary<Collider, Grabbable> ValidGrabbables;
public Grabbable ClosestGrabbable;
public Dictionary<Collider, Grabbable> ValidRemoteGrabbables;
public Grabbable ClosestRemoteGrabbable;
public bool FireGrabbableEvents = true;
private Grabbable _closest;
private float _lastDistance;
private float _thisDistance;
private Dictionary<Collider, Grabbable> _valids;
private Dictionary<Collider, Grabbable> _filtered;
void Start() {
NearbyGrabbables = new Dictionary<Collider, Grabbable>();
ValidGrabbables = new Dictionary<Collider, Grabbable>();
ValidRemoteGrabbables = new Dictionary<Collider, Grabbable>();
}
void Update() {
updateClosestGrabbable();
updateClosestRemoteGrabbables();
}
void updateClosestGrabbable() {
NearbyGrabbables = SanitizeGrabbables(NearbyGrabbables);
ValidGrabbables = GetValidGrabbables(NearbyGrabbables);
ClosestGrabbable = GetClosestGrabbable(ValidGrabbables);
}
void updateClosestRemoteGrabbables() {
ClosestRemoteGrabbable = GetClosestGrabbable(ValidRemoteGrabbables, true);
if (ClosestGrabbable != null) {
ClosestRemoteGrabbable = null;
}
}
public virtual Grabbable GetClosestGrabbable(Dictionary<Collider, Grabbable> grabbables, bool remoteOnly = false) {
_closest = null;
_lastDistance = 9999f;
if(grabbables == null) {
return null;
}
foreach (var kvp in grabbables) {
if (kvp.Value == null || !kvp.Value.IsGrabbable()) {
continue;
}
_thisDistance = Vector3.Distance(kvp.Value.transform.position, transform.position);
if (_thisDistance < _lastDistance && kvp.Value.isActiveAndEnabled) {
if (remoteOnly && !kvp.Value.RemoteGrabbable) {
continue;
}
if (remoteOnly && _thisDistance > kvp.Value.RemoteGrabDistance) {
continue;
}
// This is now our closest grabbable
_lastDistance = _thisDistance;
_closest = kvp.Value;
}
}
return _closest;
}
public Dictionary<Collider, Grabbable> GetValidGrabbables(Dictionary<Collider, Grabbable> grabs) {
_valids = new Dictionary<Collider, Grabbable>();
if (grabs == null) {
return _valids;
}
foreach (var kvp in grabs) {
if (isValidGrabbale(kvp.Key, kvp.Value) && !_valids.ContainsKey(kvp.Key)) {
_valids.Add(kvp.Key, kvp.Value);
}
}
return _valids;
}
protected virtual bool isValidGrabbale(Collider col, Grabbable grab) {
if (col == null || grab == null || !grab.isActiveAndEnabled || !col.enabled) {
return false;
}
else if (!grab.IsGrabbable()) {
return false;
}
else if(grab.GetComponent<SnapZone>() != null && grab.GetComponent<SnapZone>().HeldItem == null) {
return false;
}
else if (grab == ClosestGrabbable) {
if (grab.BreakDistance > 0 && Vector3.Distance(grab.transform.position, transform.position) > grab.BreakDistance) {
return false;
}
}
return true;
}
public virtual Dictionary<Collider, Grabbable> SanitizeGrabbables(Dictionary<Collider, Grabbable> grabs) {
_filtered = new Dictionary<Collider, Grabbable>();
if (grabs == null) {
return _filtered;
}
foreach (var g in grabs) {
if (g.Key != null && g.Key.enabled && g.Value.isActiveAndEnabled) {
if (g.Value.BreakDistance > 0 && Vector3.Distance(g.Key.transform.position, transform.position) > g.Value.BreakDistance) {
continue;
}
_filtered.Add(g.Key, g.Value);
}
}
return _filtered;
}
public virtual void AddNearbyGrabbable(Collider col, Grabbable grabObject) {
if(NearbyGrabbables == null) {
NearbyGrabbables = new Dictionary<Collider, Grabbable>();
}
if (grabObject != null && !NearbyGrabbables.ContainsKey(col)) {
NearbyGrabbables.Add(col, grabObject);
}
}
public virtual void RemoveNearbyGrabbable(Collider col, Grabbable grabObject) {
if (grabObject != null && NearbyGrabbables != null && NearbyGrabbables.ContainsKey(col)) {
NearbyGrabbables.Remove(col);
}
}
public virtual void RemoveNearbyGrabbable(Grabbable grabObject) {
if (grabObject != null) {
foreach (var x in NearbyGrabbables) {
if (x.Value == grabObject) {
NearbyGrabbables.Remove(x.Key);
break;
}
}
}
}
public virtual void AddValidRemoteGrabbable(Collider col, Grabbable grabObject) {
if(col == null || grabObject == null) {
return;
}
if (ValidRemoteGrabbables == null) {
ValidRemoteGrabbables = new Dictionary<Collider, Grabbable>();
}
try {
if (grabObject != null && grabObject.RemoteGrabbable && col != null && !ValidRemoteGrabbables.ContainsKey(col)) {
ValidRemoteGrabbables.Add(col, grabObject);
}
}
catch(System.Exception e) {
Debug.Log("Could not add Collider " + col.transform.name + " " + e.Message);
}
}
public virtual void RemoveValidRemoteGrabbable(Collider col, Grabbable grabObject) {
if (grabObject != null && ValidRemoteGrabbables != null && ValidRemoteGrabbables.ContainsKey(col)) {
ValidRemoteGrabbables.Remove(col);
}
}
void OnTriggerEnter(Collider other) {
Grabbable g = other.GetComponent<Grabbable>();
if (g != null) {
AddNearbyGrabbable(other, g);
return;
}
GrabbableChild gc = other.GetComponent<GrabbableChild>();
if (gc != null && gc.ParentGrabbable != null) {
AddNearbyGrabbable(other, gc.ParentGrabbable);
return;
}
}
void OnTriggerExit(Collider other) {
Grabbable g = other.GetComponent<Grabbable>();
if (g != null) {
RemoveNearbyGrabbable(other, g);
return;
}
GrabbableChild gc = other.GetComponent<GrabbableChild>();
if (gc != null) {
RemoveNearbyGrabbable(other, gc.ParentGrabbable);
return;
}
}
}
}
CharacterIK
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace BNG {
public class CharacterIK : MonoBehaviour {
public Transform FollowLeftController;
public Transform FollowRightController;
public Transform FollowLeftFoot;
public Transform FollowRightFoot;
public Transform FollowHead;
public float FootYPosition = 0;
public bool IKActive = true;
public bool IKFeetActive = true;
public bool HideHead = true;
public bool HideLeftArm = false;
public bool HideRightArm = false;
public bool HideLeftHand = false;
public bool HideRightHand = false;
public bool HideLegs = false;
public Transform HipsJoint;
public CharacterController FollowPlayer;
Transform headBone;
Transform leftShoulderJoint;
Transform rightShoulderJoint;
Transform leftHandJoint;
Transform rightHandJoint;
Animator animator;
public float HipOffset = 0;
void Start() {
animator = GetComponent<Animator>();
headBone = animator.GetBoneTransform(HumanBodyBones.Head);
leftHandJoint = animator.GetBoneTransform(HumanBodyBones.LeftHand);
rightHandJoint = animator.GetBoneTransform(HumanBodyBones.RightHand);
leftShoulderJoint = animator.GetBoneTransform(HumanBodyBones.LeftShoulder);
rightShoulderJoint = animator.GetBoneTransform(HumanBodyBones.RightShoulder);
}
public Vector3 hideBoneScale = new Vector3(0.0001f, 0.0001f, 0.0001f);
void Update() {
if (headBone != null) {
headBone.localScale = HideHead ? Vector3.zero : Vector3.one;
}
if (leftShoulderJoint != null) {
leftShoulderJoint.localScale = HideLeftArm ? hideBoneScale : Vector3.one;
}
if (rightShoulderJoint != null) {
rightShoulderJoint.localScale = HideRightArm ? hideBoneScale : Vector3.one;
}
if (leftHandJoint != null) {
leftHandJoint.localScale = HideLeftHand ? Vector3.zero : Vector3.one;
}
if (rightHandJoint != null) {
rightHandJoint.localScale = HideRightHand ? Vector3.zero : Vector3.one;
}
if(HipsJoint) {
HipsJoint.localScale = HideLegs ? Vector3.zero : Vector3.one;
}
Transform hipJoint = animator.GetBoneTransform(HumanBodyBones.RightShoulder);
}
void OnAnimatorIK() {
if (animator) {
if (IKActive) {
if (FollowHead != null) {
animator.SetLookAtWeight(1);
animator.SetLookAtPosition(FollowHead.position);
}
if (FollowLeftController != null) {
animator.SetIKPositionWeight(AvatarIKGoal.LeftHand, 1);
animator.SetIKRotationWeight(AvatarIKGoal.LeftHand, 1);
animator.SetIKPosition(AvatarIKGoal.LeftHand, FollowLeftController.position);
animator.SetIKRotation(AvatarIKGoal.LeftHand, FollowLeftController.rotation);
}
if (FollowRightController != null) {
animator.SetIKPositionWeight(AvatarIKGoal.RightHand, 1);
animator.SetIKRotationWeight(AvatarIKGoal.RightHand, 1);
animator.SetIKPosition(AvatarIKGoal.RightHand, FollowRightController.position);
animator.SetIKRotation(AvatarIKGoal.RightHand, FollowRightController.rotation);
}
if(IKFeetActive) {
if (FollowLeftFoot != null) {
animator.SetIKPositionWeight(AvatarIKGoal.LeftFoot, 1);
animator.SetIKPosition(AvatarIKGoal.LeftFoot, new Vector3(FollowLeftFoot.position.x, FootYPosition, FollowLeftFoot.position.z));
}
if (FollowRightFoot != null) {
animator.SetIKPositionWeight(AvatarIKGoal.RightFoot, 1);
animator.SetIKPosition(AvatarIKGoal.RightFoot, new Vector3(FollowRightFoot.position.x, FootYPosition, FollowRightFoot.position.z));
}
}
else {
if (FollowLeftFoot != null) {
animator.SetIKPositionWeight(AvatarIKGoal.LeftFoot, 0);
}
if (FollowRightFoot != null) {
animator.SetIKPositionWeight(AvatarIKGoal.RightFoot, 0);
}
}
}
else {
animator.SetIKPositionWeight(AvatarIKGoal.LeftHand, 0);
animator.SetIKRotationWeight(AvatarIKGoal.LeftHand, 0);
animator.SetIKPositionWeight(AvatarIKGoal.RightHand, 0);
animator.SetIKRotationWeight(AvatarIKGoal.RightHand, 0);
animator.SetLookAtWeight(0);
}
}
}
}
}
SteeringWheel
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
namespace BNG {
public class SteeringWheel : GrabbableEvents {
[Header("Rotation Limits")]
public float MinAngle = -360f;
public float MaxAngle = 360f;
[Header("Rotation Object")]
public Transform RotatorObject;
[Header("Rotation Speed")]
public float RotationSpeed = 0f;
[Header("Two-Handed Option")]
public bool AllowTwoHanded = true;
[Header("Return to Center")]
public bool ReturnToCenter = false;
public float ReturnToCenterSpeed = 45;
[Header("Debug Options")]
public Text DebugText;
[Header("Events")]
public FloatEvent onAngleChange;
public FloatEvent onValueChange;
[Header("Editor Option")]
public bool ShowEditorGizmos = true;
public float Angle {
get {
return Mathf.Clamp(smoothedAngle, MinAngle, MaxAngle);
}
}
public float RawAngle {
get {
return targetAngle;
}
}
public float ScaleValue {
get {
return GetScaledValue(Angle, MinAngle, MaxAngle);
}
}
public float ScaleValueInverted {
get {
return ScaleValue * -1;
}
}
public float AngleInverted {
get {
return Angle * -1;
}
}
public Grabber PrimaryGrabber {
get {
return GetPrimaryGrabber();
}
}
public Grabber SecondaryGrabber {
get {
return GetSecondaryGrabber();
}
}
protected Vector3 rotatePosition;
protected Vector3 previousPrimaryPosition;
protected Vector3 previousSecondaryPosition;
protected float targetAngle;
protected float previousTargetAngle;
protected float smoothedAngle;
void Update() {
if (grab.BeingHeld) {
UpdateAngleCalculations();
}
else if (ReturnToCenter) {
ReturnToCenterAngle();
}
ApplyAngleToSteeringWheel(Angle);
CallEvents();
UpdatePreviewText();
UpdatePreviousAngle(targetAngle);
}
public virtual void UpdateAngleCalculations() {
float angleAdjustment = 0f;
if (PrimaryGrabber) {
rotatePosition = transform.InverseTransformPoint(PrimaryGrabber.transform.position);
rotatePosition = new Vector3(rotatePosition.x, rotatePosition.y, 0);
angleAdjustment += GetRelativeAngle(rotatePosition, previousPrimaryPosition);
previousPrimaryPosition = rotatePosition;
}
if (AllowTwoHanded && SecondaryGrabber != null) {
rotatePosition = transform.InverseTransformPoint(SecondaryGrabber.transform.position);
rotatePosition = new Vector3(rotatePosition.x, rotatePosition.y, 0);
angleAdjustment += GetRelativeAngle(rotatePosition, previousSecondaryPosition);
previousSecondaryPosition = rotatePosition;
}
if(PrimaryGrabber != null && SecondaryGrabber != null) {
angleAdjustment *= 0.5f;
}
targetAngle -= angleAdjustment;
if(RotationSpeed == 0) {
smoothedAngle = targetAngle;
}
else {
smoothedAngle = Mathf.Lerp(smoothedAngle, targetAngle, Time.deltaTime * RotationSpeed);
}
if (MinAngle != 0 && MaxAngle != 0) {
targetAngle = Mathf.Clamp(targetAngle, MinAngle, MaxAngle);
smoothedAngle = Mathf.Clamp(smoothedAngle, MinAngle, MaxAngle);
}
}
public float GetRelativeAngle(Vector3 position1, Vector3 position2) {
if (Vector3.Cross(position1, position2).z < 0) {
return -Vector3.Angle(position1, position2);
}
return Vector3.Angle(position1, position2);
}
public virtual void ApplyAngleToSteeringWheel(float angle) {
RotatorObject.localEulerAngles = new Vector3(0, 0, angle);
}
public virtual void UpdatePreviewText() {
if (DebugText) {
DebugText.text = String.Format("{0}\n{1}", (int)AngleInverted, (ScaleValueInverted).ToString("F2"));
}
}
public virtual void CallEvents() {
if (targetAngle != previousTargetAngle) {
onAngleChange.Invoke(targetAngle);
}
onValueChange.Invoke(ScaleValue);
}
public override void OnGrab(Grabber grabber) {
if(grabber == SecondaryGrabber) {
previousSecondaryPosition = transform.InverseTransformPoint(SecondaryGrabber.transform.position);
previousSecondaryPosition = new Vector3(previousSecondaryPosition.x, previousSecondaryPosition.y, 0);
}
else {
previousPrimaryPosition = transform.InverseTransformPoint(PrimaryGrabber.transform.position);
previousPrimaryPosition = new Vector3(previousPrimaryPosition.x, previousPrimaryPosition.y, 0);
}
}
public virtual void ReturnToCenterAngle() {
bool wasUnderZero = smoothedAngle < 0;
if (smoothedAngle > 0) {
smoothedAngle -= Time.deltaTime * ReturnToCenterSpeed;
}
else if (smoothedAngle < 0) {
smoothedAngle += Time.deltaTime * ReturnToCenterSpeed;
}
if (wasUnderZero && smoothedAngle > 0) {
smoothedAngle = 0;
}
else if (!wasUnderZero && smoothedAngle < 0) {
smoothedAngle = 0;
}
if (smoothedAngle < 0.02f && smoothedAngle > -0.02f) {
smoothedAngle = 0;
}
targetAngle = smoothedAngle;
}
public Grabber GetPrimaryGrabber() {
if (grab.HeldByGrabbers != null) {
for (int x = 0; x < grab.HeldByGrabbers.Count; x++) {
Grabber g = grab.HeldByGrabbers[x];
if (g.HandSide == ControllerHand.Right) {
return g;
}
}
}
return null;
}
public Grabber GetSecondaryGrabber() {
if (grab.HeldByGrabbers != null) {
for (int x = 0; x < grab.HeldByGrabbers.Count; x++) {
Grabber g = grab.HeldByGrabbers[x];
if (g.HandSide == ControllerHand.Left) {
return g;
}
}
}
return null;
}
public virtual void UpdatePreviousAngle(float angle) {
previousTargetAngle = angle;
}
public virtual float GetScaledValue(float value, float min, float max) {
float range = (max - min) / 2f;
float returnValue = ((value - min) / range) - 1;
return returnValue;
}
#if UNITY_EDITOR
public void OnDrawGizmosSelected() {
if (ShowEditorGizmos && !Application.isPlaying) {
Vector3 origin = transform.position;
float rotationDifference = MaxAngle - MinAngle;
float lineLength = 0.1f;
float arcLength = 0.1f;
UnityEditor.Handles.color = Color.cyan;
Vector3 minPosition = origin + Quaternion.AngleAxis(MinAngle, transform.forward) * transform.up * lineLength;
Vector3 maxPosition = origin + Quaternion.AngleAxis(MaxAngle, transform.forward) * transform.up * lineLength;
UnityEditor.Handles.DrawLine(origin, minPosition);
UnityEditor.Handles.DrawLine(origin, maxPosition);
Debug.DrawLine(transform.position, origin + Quaternion.AngleAxis(0, transform.up) * transform.up * lineLength, Color.magenta);
if (rotationDifference == 180) {
minPosition = origin + Quaternion.AngleAxis(MinAngle + 0.01f, transform.up) * transform.up * lineLength;
}
Vector3 cross = Vector3.Cross(minPosition - origin, maxPosition - origin);
if (rotationDifference > 180) {
cross = Vector3.Cross(maxPosition - origin, minPosition - origin);
}
UnityEditor.Handles.color = new Color(0, 255, 255, 0.1f);
UnityEditor.Handles.DrawSolidArc(origin, cross, minPosition - origin, rotationDifference, arcLength);
}
}
#endif
}
}
HandController
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace BNG {
public class HandController : MonoBehaviour {
public Transform HandAnchor;
public bool ResetHandAnchorPosition = true;
public Animator HandAnimator;
public HandPoser handPoser;
public AutoPoser autoPoser;
public bool AutoPoseWhenNoGrabbable = false;
public float HandAnimationSpeed = 20f;
public Grabber grabber;
public float GripAmount;
private float _prevGrip;
public float PointAmount;
private float _prevPoint;
public float ThumbAmount;
private float _prevThumb;
public int PoseId;
ControllerOffsetHelper offset;
InputBridge input;
Rigidbody rigid;
Transform offsetTransform;
Vector3 offsetPosition {
get {
if(offset) {
return offset.OffsetPosition;
}
return Vector3.zero;
}
}
Vector3 offsetRotation {
get {
if (offset) {
return offset.OffsetRotation;
}
return Vector3.zero;
}
}
void Start() {
rigid = GetComponent<Rigidbody>();
offset = GetComponent<ControllerOffsetHelper>();
offsetTransform = new GameObject("OffsetHelper").transform;
offsetTransform.parent = transform;
if (HandAnchor) {
transform.parent = HandAnchor;
offsetTransform.parent = HandAnchor;
if (ResetHandAnchorPosition) {
transform.localPosition = offsetPosition;
transform.localEulerAngles = offsetRotation;
}
}
if(grabber == null) {
grabber = GetComponentInChildren<Grabber>();
}
if(grabber != null) {
grabber.onAfterGrabEvent.AddListener(OnGrabberGrabbed);
grabber.onReleaseEvent.AddListener(OnGrabberReleased);
}
SetHandAnimator();
input = InputBridge.Instance;
}
public void Update() {
CheckForGrabChange();
UpdateFromInputs();
UpdateAnimimationStates();
UpdateHandPoser();
}
public GameObject PreviousHeldObject;
public virtual void CheckForGrabChange() {
if(grabber != null) {
if(grabber.HeldGrabbable == null && PreviousHeldObject != null) {
OnGrabDrop();
}
else if(grabber.HeldGrabbable != null && !GameObject.ReferenceEquals(grabber.HeldGrabbable.gameObject, PreviousHeldObject)) {
OnGrabChange(grabber.HeldGrabbable.gameObject);
}
}
}
public virtual void OnGrabChange(GameObject newlyHeldObject) {
if(grabber != null && grabber.HeldGrabbable != null) {
if (grabber.HeldGrabbable.handPoseType == HandPoseType.AnimatorID) {
EnableHandAnimator();
}
else if (grabber.HeldGrabbable.handPoseType == HandPoseType.AutoPoseOnce) {
EnableAutoPoser(false);
}
else if (grabber.HeldGrabbable.handPoseType == HandPoseType.AutoPoseContinuous) {
EnableAutoPoser(true);
}
else if (grabber.HeldGrabbable.handPoseType == HandPoseType.HandPose) {
if (grabber.HeldGrabbable.SelectedHandPose != null) {
EnableHandPoser();
}
else {
EnableHandAnimator();
}
}
}
PreviousHeldObject = newlyHeldObject;
}
public virtual void OnGrabDrop() {
if(AutoPoseWhenNoGrabbable) {
EnableAutoPoser(true);
}
else {
EnableHandAnimator();
DisableAutoPoser();
}
PreviousHeldObject = null;
}
public virtual void SetHandAnimator() {
if (HandAnimator == null || !HandAnimator.gameObject.activeInHierarchy) {
HandAnimator = GetComponentInChildren<Animator>();
}
}
public virtual void UpdateFromInputs() {
if (grabber == null || !grabber.isActiveAndEnabled) {
grabber = GetComponentInChildren<Grabber>();
GripAmount = 0;
PointAmount = 0;
ThumbAmount = 0;
return;
}
if (grabber.HandSide == ControllerHand.Left) {
GripAmount = input.LeftGrip;
PointAmount = 1 - input.LeftTrigger;
PointAmount *= InputBridge.Instance.InputSource == XRInputSource.SteamVR ? 0.25F : 0.5F;
if (input.SupportsIndexTouch && input.LeftTriggerNear == false && PointAmount != 0) {
PointAmount = 1f;
}
else if (!input.SupportsIndexTouch && input.LeftTrigger == 0) {
PointAmount = 1;
}
ThumbAmount = input.LeftThumbNear ? 0 : 1;
}
else if (grabber.HandSide == ControllerHand.Right) {
GripAmount = input.RightGrip;
PointAmount = 1 - input.RightTrigger;
PointAmount *= InputBridge.Instance.InputSource == XRInputSource.SteamVR ? 0.25F : 0.5F;
if (input.SupportsIndexTouch && input.RightTriggerNear == false && PointAmount != 0) {
PointAmount = 1f;
}
else if (!input.SupportsIndexTouch && input.RightTrigger == 0) {
PointAmount = 1;
}
ThumbAmount = input.RightThumbNear ? 0 : 1;
}
}
public bool DoUpdateAnimationStates = true;
public bool DoUpdateHandPoser = true;
public virtual void UpdateAnimimationStates()
{
if(DoUpdateAnimationStates == false) {
return;
}
if(IsAnimatorGrabbable() && !HandAnimator.isActiveAndEnabled) {
EnableHandAnimator();
}
if (HandAnimator != null && HandAnimator.isActiveAndEnabled && HandAnimator.runtimeAnimatorController != null) {
_prevGrip = Mathf.Lerp(_prevGrip, GripAmount, Time.deltaTime * HandAnimationSpeed);
_prevThumb = Mathf.Lerp(_prevThumb, ThumbAmount, Time.deltaTime * HandAnimationSpeed);
_prevPoint = Mathf.Lerp(_prevPoint, PointAmount, Time.deltaTime * HandAnimationSpeed);
HandAnimator.SetFloat("Flex", _prevGrip);
HandAnimator.SetLayerWeight(1, _prevThumb);
HandAnimator.SetLayerWeight(2, _prevPoint);
if (grabber != null && grabber.HeldGrabbable != null) {
HandAnimator.SetLayerWeight(0, 0);
HandAnimator.SetLayerWeight(1, 0);
HandAnimator.SetLayerWeight(2, 0);
PoseId = (int)grabber.HeldGrabbable.CustomHandPose;
if (grabber.HeldGrabbable.ActiveGrabPoint != null) {
HandAnimator.SetLayerWeight(0, 1);
HandAnimator.SetFloat("Flex", 1);
setAnimatorBlend(grabber.HeldGrabbable.ActiveGrabPoint.IndexBlendMin, grabber.HeldGrabbable.ActiveGrabPoint.IndexBlendMax, PointAmount, 2);
setAnimatorBlend(grabber.HeldGrabbable.ActiveGrabPoint.ThumbBlendMin, grabber.HeldGrabbable.ActiveGrabPoint.ThumbBlendMax, ThumbAmount, 1);
}
else {
if (grabber.HoldingItem) {
GripAmount = 1;
PointAmount = 0;
ThumbAmount = 0;
}
}
HandAnimator.SetInteger("Pose", PoseId);
}
else {
HandAnimator.SetInteger("Pose", 0);
}
}
}
void setAnimatorBlend(float min, float max, float input, int animationLayer) {
HandAnimator.SetLayerWeight(animationLayer, min + (input) * max - min);
}
public virtual bool IsAnimatorGrabbable() {
return HandAnimator != null && grabber != null && grabber.HeldGrabbable != null && grabber.HeldGrabbable.handPoseType == HandPoseType.AnimatorID;
}
public virtual void UpdateHandPoser() {
if (DoUpdateHandPoser == false) {
return;
}
if (handPoser == null || !handPoser.isActiveAndEnabled) {
handPoser = GetComponentInChildren<HandPoser>();
}
if(handPoser == null || grabber == null || grabber.HeldGrabbable == null || grabber.HeldGrabbable.handPoseType != HandPoseType.HandPose) {
return;
}
}
public virtual void EnableHandPoser() {
if(handPoser != null) {
DisableHandAnimator();
}
}
public virtual void EnableAutoPoser(bool continuous) {
if (autoPoser == null || !autoPoser.gameObject.activeInHierarchy) {
if(handPoser != null) {
autoPoser = handPoser.GetComponent<AutoPoser>();
}
else {
autoPoser = GetComponentInChildren<AutoPoser>(false);
}
}
if (autoPoser != null) {
autoPoser.UpdateContinuously = continuous;
if(!continuous) {
autoPoser.UpdateAutoPoseOnce();
}
DisableHandAnimator();
}
}
public virtual void DisableAutoPoser() {
if (autoPoser != null) {
autoPoser.UpdateContinuously = false;
}
}
public virtual void EnableHandAnimator() {
if (HandAnimator != null && HandAnimator.enabled == false) {
HandAnimator.enabled = true;
}
}
public virtual void DisableHandAnimator() {
if (HandAnimator != null && HandAnimator.enabled) {
HandAnimator.enabled = false;
}
}
public virtual void OnGrabberGrabbed(Grabbable grabbed) {
if (grabbed.SelectedHandPose != null && handPoser != null) {
handPoser.CurrentPose = grabber.HeldGrabbable.SelectedHandPose;
handPoser.OnPoseChanged();
}
}
public virtual void OnGrabberReleased(Grabbable released) {
OnGrabDrop();
}
}
}
Door
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace BNG {
public class Door : MonoBehaviour {
public AudioClip DoorOpenSound;
public AudioClip DoorCloseSound;
public bool RequireHandleTurnToOpen = false;
public Transform HandleFollower;
public float DegreesTurned;
public float DegreesTurnToOpen = 10f;
public Transform DoorLockTransform;
float initialLockPosition;
HingeJoint hinge;
Rigidbody rigid;
bool playedOpenSound = false;
bool readyToPlayCloseSound = false;
public float AngularVelocitySnapDoor = 0.2f;
void Start() {
hinge = GetComponent<HingeJoint>();
rigid = GetComponent<Rigidbody>();
if(DoorLockTransform) {
initialLockPosition = DoorLockTransform.transform.localPosition.x;
}
}
public float angle;
public float AngularVelocity = 0.2f;
bool doorLocked = false;
public float lockPos;
Vector3 currentRotation;
float moveLockAmount, rotateAngles, ratio;
void Update() {
AngularVelocity = rigid.angularVelocity.magnitude;
currentRotation = transform.localEulerAngles;
angle = Mathf.Floor(currentRotation.y);
if(angle >= 180) {
angle -= 180;
}
else {
angle = 180 - angle;
}
if (angle > 10) {
if(!playedOpenSound) {
VRUtils.Instance.PlaySpatialClipAt(DoorOpenSound, transform.position, 1f, 1f);
playedOpenSound = true;
}
}
if(angle > 30) {
readyToPlayCloseSound = true;
}
if(angle < 2 && playedOpenSound) {
playedOpenSound = false;
}
if (angle < 1 && AngularVelocity <= AngularVelocitySnapDoor) {
rigid.angularVelocity = Vector3.zero;
}
if (readyToPlayCloseSound && angle < 2) {
VRUtils.Instance.PlaySpatialClipAt(DoorCloseSound, transform.position, 1f, 1f);
readyToPlayCloseSound = false;
}
if (HandleFollower) {
DegreesTurned = Mathf.Abs(HandleFollower.localEulerAngles.y - 270);
}
if(DoorLockTransform) {
moveLockAmount = 0.025f;
rotateAngles = 55;
ratio = rotateAngles / (rotateAngles - Mathf.Clamp(DegreesTurned, 0, rotateAngles));
lockPos = initialLockPosition - (ratio * moveLockAmount) + moveLockAmount;
lockPos = Mathf.Clamp(lockPos, initialLockPosition - moveLockAmount, initialLockPosition);
DoorLockTransform.transform.localPosition = new Vector3(lockPos, DoorLockTransform.transform.localPosition.y, DoorLockTransform.transform.localPosition.z);
}
if(RequireHandleTurnToOpen) {
doorLocked = DegreesTurned < DegreesTurnToOpen;
}
if(angle < 0.02f && doorLocked) {
if (rigid.collisionDetectionMode == CollisionDetectionMode.Continuous || rigid.collisionDetectionMode == CollisionDetectionMode.ContinuousDynamic) {
rigid.collisionDetectionMode = CollisionDetectionMode.ContinuousSpeculative;
}
rigid.isKinematic = true;
}
else {
if (rigid.collisionDetectionMode == CollisionDetectionMode.ContinuousSpeculative) {
rigid.collisionDetectionMode = CollisionDetectionMode.ContinuousDynamic;
}
rigid.isKinematic = false;
}
}
}
}
PlayerClimbing
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace BNG {
public class PlayerClimbing : MonoBehaviour {
public Transform LeftControllerTransform;
public Transform RightControllerTransform;
public float ClimbingCapsuleHeight = 0.5f;
public float ClimbingCapsuleCenter = -0.25f;
public bool ApplyHapticsOnGrab = true;
public float VibrateFrequency = 0.3f;
public float VibrateAmplitude = 0.1f;
public float VibrateDuration = 0.1f;
List<Grabber> climbers;
bool wasGrippingClimbable;
CharacterController characterController;
SmoothLocomotion smoothLocomotion;
PlayerGravity playerGravity;
[Header("Shown for Debug : ")]
public bool GrippingClimbable = false;
private Vector3 moveDirection = Vector3.zero;
Vector3 previousLeftControllerPosition;
Vector3 previousRightControllerPosition;
Vector3 controllerMoveAmount;
public void Start() {
climbers = new List<Grabber>();
characterController = GetComponentInChildren<CharacterController>();
smoothLocomotion = GetComponentInChildren<SmoothLocomotion>();
playerGravity = GetComponentInChildren<PlayerGravity>();
}
public void LateUpdate() {
checkClimbing();
if (LeftControllerTransform != null) {
previousLeftControllerPosition = LeftControllerTransform.position;
}
if (RightControllerTransform != null) {
previousRightControllerPosition = RightControllerTransform.position;
}
}
public virtual void AddClimber(Climbable climbable, Grabber grab) {
if (climbers == null) {
climbers = new List<Grabber>();
}
if (!climbers.Contains(grab)) {
if (grab.DummyTransform == null) {
GameObject go = new GameObject();
go.transform.name = "DummyTransform";
go.transform.parent = grab.transform;
go.transform.position = grab.transform.position;
go.transform.localEulerAngles = Vector3.zero;
grab.DummyTransform = go.transform;
}
grab.DummyTransform.parent = climbable.transform;
grab.PreviousPosition = grab.DummyTransform.position;
if(ApplyHapticsOnGrab) {
InputBridge.Instance.VibrateController(VibrateFrequency, VibrateAmplitude, VibrateDuration, grab.HandSide);
}
climbers.Add(grab);
}
}
public virtual void RemoveClimber(Grabber grab) {
if (climbers.Contains(grab)) {
grab.DummyTransform.parent = grab.transform;
grab.DummyTransform.localPosition = Vector3.zero;
climbers.Remove(grab);
}
}
public virtual bool GrippingAtLeastOneClimbable() {
if (climbers != null && climbers.Count > 0) {
for (int x = 0; x < climbers.Count; x++) {
if (climbers[x] != null && climbers[x].HoldingItem) {
return true;
}
}
climbers = new List<Grabber>();
}
return false;
}
protected virtual void checkClimbing() {
GrippingClimbable = GrippingAtLeastOneClimbable();
if (GrippingClimbable && !wasGrippingClimbable) {
onGrabbedClimbable();
}
if (wasGrippingClimbable && !GrippingClimbable) {
onReleasedClimbable();
}
if (GrippingClimbable) {
moveDirection = Vector3.zero;
int count = 0;
float length = climbers.Count;
for (int i = 0; i < length; i++) {
Grabber climber = climbers[i];
if (climber != null && climber.HoldingItem) {
if (climber.HandSide == ControllerHand.Left) {
controllerMoveAmount = previousLeftControllerPosition - LeftControllerTransform.position;
}
else {
controllerMoveAmount = previousRightControllerPosition - RightControllerTransform.position;
}
if (count == length - 1) {
moveDirection = controllerMoveAmount;
moveDirection -= climber.PreviousPosition - climber.DummyTransform.position; ;
}
count++;
}
}
if (smoothLocomotion) {
if(smoothLocomotion.ControllerType == PlayerControllerType.CharacterController) {
smoothLocomotion.MoveCharacter(moveDirection);
}
else if(smoothLocomotion.ControllerType == PlayerControllerType.Rigidbody) {
smoothLocomotion.MoveRigidCharacter(moveDirection);
// Rigidbody rigid = smoothLocomotion.GetComponent<Rigidbody>();
// rigid.velocity = Vector3.MoveTowards(rigid.velocity, (moveDirection * 5000f) * Time.fixedDeltaTime, 1f);
}
}
else if(characterController) {
characterController.Move(moveDirection);
}
}
for (int x = 0; x < climbers.Count; x++) {
Grabber climber = climbers[x];
if (climber != null && climber.HoldingItem) {
if (climber.DummyTransform != null) {
climber.PreviousPosition = climber.DummyTransform.position;
}
else {
climber.PreviousPosition = climber.transform.position;
}
}
}
wasGrippingClimbable = GrippingClimbable;
}
void onGrabbedClimbable() {
if (smoothLocomotion) {
smoothLocomotion.DisableMovement();
}
if (playerGravity) {
playerGravity.ToggleGravity(false);
}
}
void onReleasedClimbable() {
if (smoothLocomotion) {
smoothLocomotion.EnableMovement();
}
if (playerGravity) {
playerGravity.ToggleGravity(true);
}
}
}
}
GunShot
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace BNG {
public class GrappleShot : GrabbableEvents {
public float MaxRange = 100f;
public float GrappleReelForce = 0.5f;
public float MinReelDistance = 0.25f;
public LayerMask GrappleLayers;
public Transform MuzzleTransform;
public Transform HitTargetPrefab;
public LineRenderer GrappleLine;
public LineRenderer HelperLine;
public AudioClip GrappleShotSound;
bool grappling = false;
bool wasGrappling = false;
CharacterController characterController;
SmoothLocomotion smoothLocomotion;
PlayerGravity playerGravity;
PlayerClimbing playerClimbing;
AudioSource audioSource;
public float currentGrappleDistance = 0;
bool validTargetFound = false;
bool isDynamic = false;
Rigidbody grappleTargetRigid;
Collider grappleTargetCollider;
Transform grappleTargetParent;
bool requireRelease = false;
bool climbing = false;
public Climbable ClimbHelper;
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>();
}
else {
Debug.Log("No player object found.");
}
audioSource = GetComponent<AudioSource>();
}
private void LateUpdate() {
if (!grappling && grab.BeingHeld && !requireRelease) {
drawGrappleHelper();
}
else {
hideGrappleHelper();
}
if (grappling && validTargetFound && grab.BeingHeld) {
drawGrappleLine();
}
else {
hideGrappleLine();
}
}
public override void OnTrigger(float triggerValue) {
updateGrappleDistance();
if (triggerValue >= 0.25f) {
if (grappling) {
reelInGrapple(triggerValue);
}
else {
shootGrapple();
}
}
else {
grappling = false;
requireRelease = false;
}
if(!grappling && wasGrappling) {
onReleaseGrapple();
}
base.OnTrigger(triggerValue);
}
void updateGrappleDistance() {
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();
}
void onReleaseGrapple() {
changeGravity(true);
if(grappleTargetRigid && isDynamic) {
grappleTargetRigid.useGravity = true;
grappleTargetRigid.isKinematic = false;
grappleTargetRigid.transform.parent = grappleTargetParent;
if(grappleTargetRigid.GetComponent<Grabbable>()) {
grappleTargetRigid.GetComponent<Grabbable>().ResetParent();
}
}
ClimbHelper.transform.localPosition = Vector3.zero;
playerClimbing.RemoveClimber(thisGrabber);
climbing = false;
grappling = false;
validTargetFound = false;
isDynamic = false;
wasGrappling = false;
}
void drawGrappleHelper() {
if (HitTargetPrefab) {
RaycastHit hit;
if (Physics.Raycast(MuzzleTransform.position, MuzzleTransform.forward, out hit, MaxRange, GrappleLayers, QueryTriggerInteraction.Ignore)) {
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;
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) {
if(validTargetFound && grappleTargetCollider != null && !grappleTargetCollider.enabled) {
dropGrapple();
return;
}
if(validTargetFound && currentGrappleDistance > MinReelDistance) {
if(isDynamic) {
grappleTargetRigid.isKinematic = false;
grappleTargetRigid.transform.parent = grappleTargetParent;
grappleTargetRigid.useGravity = false;
grappleTargetRigid.AddForce((MuzzleTransform.position - grappleTargetRigid.transform.position) * 0.1f, ForceMode.VelocityChange);
}
else {
Vector3 moveDirection = (HitTargetPrefab.position - MuzzleTransform.position) * GrappleReelForce;
changeGravity(false);
if(smoothLocomotion) {
smoothLocomotion.MoveCharacter(moveDirection * Time.deltaTime * triggerValue);
}
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) {
ClimbHelper.transform.localPosition = Vector3.zero;
playerClimbing.AddClimber(ClimbHelper, thisGrabber);
climbing = true;
}
}
}
void shootGrapple() {
if(validTargetFound) {
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);
}
}
}
}