You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
	
	
		
			1075 lines
		
	
	
		
			44 KiB
		
	
	
	
		
			C#
		
	
			
		
		
	
	
			1075 lines
		
	
	
		
			44 KiB
		
	
	
	
		
			C#
		
	
using System.Collections;
 | 
						|
using System.Collections.Generic;
 | 
						|
using UnityEngine;
 | 
						|
using Autohand.Demo;
 | 
						|
using System;
 | 
						|
using NaughtyAttributes;
 | 
						|
using UnityEngine.Serialization;
 | 
						|
 | 
						|
#if UNITY_EDITOR
 | 
						|
using UnityEditor;
 | 
						|
#endif
 | 
						|
 | 
						|
namespace Autohand {
 | 
						|
    public enum RotationType {
 | 
						|
        snap,
 | 
						|
        smooth
 | 
						|
    }
 | 
						|
 | 
						|
    public delegate void AutoHandPlayerEvent(AutoHandPlayer player);
 | 
						|
 | 
						|
    [RequireComponent(typeof(Rigidbody)), RequireComponent(typeof(CapsuleCollider)), DefaultExecutionOrder(1)]
 | 
						|
    [HelpURL("https://app.gitbook.com/s/5zKO0EvOjzUDeT2aiFk3/auto-hand-3.1/auto-hand-player")]
 | 
						|
    public class AutoHandPlayer : MonoBehaviour {
 | 
						|
 | 
						|
        static bool notFound = false;
 | 
						|
        public static AutoHandPlayer _Instance;
 | 
						|
        public static AutoHandPlayer Instance {
 | 
						|
            get {
 | 
						|
                if(_Instance == null && !notFound)
 | 
						|
                    _Instance = AutoHandExtensions.CanFindObjectOfType<AutoHandPlayer>();
 | 
						|
 | 
						|
                if(_Instance == null)
 | 
						|
                    notFound = true;
 | 
						|
 | 
						|
                return _Instance;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
 | 
						|
 | 
						|
        [AutoHeader("Auto Hand Player")]
 | 
						|
        public bool ignoreMe;
 | 
						|
 | 
						|
 | 
						|
 | 
						|
        [Tooltip("The tracked headCamera object")]
 | 
						|
        public Camera headCamera;
 | 
						|
        [Tooltip("The object that represents the forward direction movement, usually should be set as the camera or a tracked controller")]
 | 
						|
        public Transform forwardFollow;
 | 
						|
        [Tooltip("This should NOT be a child of this body. This should be a GameObject that contains all the tracked objects (head/controllers)")]
 | 
						|
        public Transform trackingContainer;
 | 
						|
        public Hand handRight;
 | 
						|
        public Hand handLeft;
 | 
						|
 | 
						|
 | 
						|
 | 
						|
        [AutoToggleHeader("Movement")]
 | 
						|
        public bool useMovement = true;
 | 
						|
        [EnableIf("useMovement"), FormerlySerializedAs("moveSpeed")]
 | 
						|
        [Tooltip("Movement speed when isGrounded")]
 | 
						|
        public float maxMoveSpeed = 1.5f;
 | 
						|
        [EnableIf("useMovement")]
 | 
						|
        [Tooltip("Movement acceleration when isGrounded")]
 | 
						|
        public float moveAcceleration = 10f;
 | 
						|
        [EnableIf("useMovement")]
 | 
						|
 | 
						|
        [Tooltip("Whether or not to use snap turning or smooth turning"), Min(0)]
 | 
						|
        public RotationType rotationType =  RotationType.snap;
 | 
						|
        [Tooltip("turn speed when not using snap turning - if snap turning, represents angle per snap")]
 | 
						|
        public float snapTurnAngle = 30f;
 | 
						|
        public float smoothTurnSpeed = 10f;
 | 
						|
 | 
						|
 | 
						|
 | 
						|
        [AutoToggleHeader("Height")]
 | 
						|
        public bool showHeight = true;
 | 
						|
        [ShowIf("showHeight"), Tooltip("Smooths camera upward movement when stepping up")]
 | 
						|
        public float heightSmoothSpeed = 20f;
 | 
						|
        [ShowIf("showHeight")]
 | 
						|
        public float heightOffset = 0f;
 | 
						|
        [ShowIf("showHeight")]
 | 
						|
        public bool crouching = false;
 | 
						|
        [ShowIf("showHeight")]
 | 
						|
        public float crouchHeight = 0.6f;
 | 
						|
        [ShowIf("showHeight")]
 | 
						|
        [Tooltip("Whether or not the capsule height should be adjusted to match the headCamera height")]
 | 
						|
        public bool autoAdjustColliderHeight = true;
 | 
						|
        [ShowIf("showHeight")]
 | 
						|
        [Tooltip("Minimum and maximum auto adjusted height, to adjust height without auto adjustment change capsule collider height instead")]
 | 
						|
        public Vector2 minMaxHeight = new Vector2(0.5f, 2.5f);
 | 
						|
        [ShowIf("showHeight")]
 | 
						|
        public bool useHeadCollision = true;
 | 
						|
        [ShowIf("showHeight")]
 | 
						|
        public float headRadius = 0.15f;
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
        [AutoToggleHeader("Use Grounding")]
 | 
						|
        public bool useGrounding = true;
 | 
						|
        [EnableIf("useGrounding"), Tooltip("Maximum height that the body can step up onto"), Min(0)]
 | 
						|
        public float maxStepHeight = 0.3f;
 | 
						|
        [EnableIf("useGrounding"), Tooltip("Maximum angle the player can walk on"), Min(0)]
 | 
						|
        public float maxStepAngle = 30f;
 | 
						|
        [EnableIf("useGrounding"), Tooltip("The layers that count as ground")]
 | 
						|
        public LayerMask groundLayerMask;
 | 
						|
        [EnableIf("useGrounding"), Tooltip("Movement acceleration when isGrounded")]
 | 
						|
        public float groundedDrag = 4f;
 | 
						|
        [Tooltip("Movement acceleration when grounding is disabled")]
 | 
						|
        public float flyingDrag = 4f;
 | 
						|
 | 
						|
 | 
						|
 | 
						|
        [AutoToggleHeader("Enable Climbing")]
 | 
						|
        [Tooltip("Whether or not the player can use Climbable objects  (Objects with the Climbable component)")]
 | 
						|
        public bool allowClimbing = true;
 | 
						|
        [Tooltip("Whether or not the player move while climbing")]
 | 
						|
        [ShowIf("allowClimbing")]
 | 
						|
        public bool allowClimbingMovement = true;
 | 
						|
        [Tooltip("How quickly the player can climb")]
 | 
						|
        [ShowIf("allowClimbing")]
 | 
						|
        public Vector3 climbingStrength = new Vector3(20f, 20f, 20f);
 | 
						|
        public float climbingAcceleration = 30f;
 | 
						|
        public float climbingDrag = 5f;
 | 
						|
        [Tooltip("Inscreases the step height while climbing up to make it easier to step up onto a surface")]
 | 
						|
        public float climbUpStepHeightMultiplier = 1;
 | 
						|
 | 
						|
 | 
						|
 | 
						|
        [AutoToggleHeader("Enable Pushing")]
 | 
						|
        [Tooltip("Whether or not the player can use Pushable objects (Objects with the Pushable component)")]
 | 
						|
        public bool allowBodyPushing = true;
 | 
						|
        [Tooltip("How quickly the player can climb")]
 | 
						|
        [EnableIf("allowBodyPushing")]
 | 
						|
        public Vector3 pushingStrength = new Vector3(10f, 10f, 10f);
 | 
						|
        public float pushingAcceleration = 10f;
 | 
						|
        public float pushingDrag = 3f;
 | 
						|
        [Tooltip("Inscreases the step height while pushing up to make it easier to step up onto a surface")]
 | 
						|
        public float pushUpStepHeightMultiplier = 1;
 | 
						|
 | 
						|
 | 
						|
 | 
						|
        [AutoToggleHeader("Enable Platforming")]
 | 
						|
        [Tooltip("Platforms will move the player with them. A platform is an object with the Transform component on it")]
 | 
						|
        public bool allowPlatforms = true;
 | 
						|
        [EnableIf("useGrounding"), Tooltip("The layers that platforming will be enabled on, will not work with layers that the HandPlayer can't collide with")]
 | 
						|
        public LayerMask platformingLayerMask = ~0;
 | 
						|
 | 
						|
 | 
						|
 | 
						|
        public AutoHandPlayerEvent OnSnapTurn;
 | 
						|
        public AutoHandPlayerEvent OnTeleported;
 | 
						|
 | 
						|
 | 
						|
        [HideInInspector]
 | 
						|
        public float movementDeadzone = 0.1f;
 | 
						|
        [HideInInspector]
 | 
						|
        public float turnDeadzone = 0.4f;
 | 
						|
 | 
						|
 | 
						|
        public const string HandPlayerLayer = "HandPlayer";
 | 
						|
 | 
						|
        public CapsuleCollider bodyCollider { get { return bodyCapsule; } }
 | 
						|
 | 
						|
        public Rigidbody body { get; protected set; }
 | 
						|
 | 
						|
 | 
						|
        protected float turnResetzone = 0.3f;
 | 
						|
        protected float groundedOffset = 0.1f;
 | 
						|
 | 
						|
        protected HeadPhysicsFollower headPhysicsFollower;
 | 
						|
        protected Vector3 moveDirection;
 | 
						|
        protected float turningAxis;
 | 
						|
 | 
						|
        protected Vector3 climbAxis;
 | 
						|
        protected Dictionary<Hand, Climbable> climbing = new Dictionary<Hand, Climbable>();
 | 
						|
        protected Dictionary<Pushable, Hand> pushRight = new Dictionary<Pushable, Hand>();
 | 
						|
        protected Dictionary<Pushable, int> pushRightCount = new Dictionary<Pushable, int>();
 | 
						|
        protected Dictionary<Pushable, Hand> pushLeft = new Dictionary<Pushable, Hand>();
 | 
						|
        protected Dictionary<Pushable, int> pushLeftCount = new Dictionary<Pushable, int>();
 | 
						|
        protected Vector3 pushAxis;
 | 
						|
 | 
						|
        protected CapsuleCollider bodyCapsule;
 | 
						|
        protected Hand lastRightHand;
 | 
						|
        protected Hand lastLeftHand;
 | 
						|
        protected Collider[] colliderNonAlloc = new Collider[50];
 | 
						|
 | 
						|
        bool isGrounded = false;
 | 
						|
        bool axisReset = true;
 | 
						|
        float playerHeight = 0;
 | 
						|
        bool lastCrouching;
 | 
						|
        float lastCrouchingHeight;
 | 
						|
        Vector3 targetTrackedPos;
 | 
						|
        Vector3 lastUpdatePosition;
 | 
						|
        bool tempDisableGrounding = false;
 | 
						|
        bool editorSelected;
 | 
						|
        Vector3 lastPlatformPosition;
 | 
						|
        Quaternion lastPlatformRotation;
 | 
						|
        RaycastHit closestHit;
 | 
						|
        Vector3 targetPosOffset;
 | 
						|
        Vector3 offset;
 | 
						|
        RaycastHit newClosestHit;
 | 
						|
        float highestPoint;
 | 
						|
        int handPlayerMask;
 | 
						|
 | 
						|
 | 
						|
        public virtual void Awake() {
 | 
						|
            if(_Instance == null) {
 | 
						|
                _Instance = this;
 | 
						|
                notFound = false;
 | 
						|
            }
 | 
						|
 | 
						|
            lastUpdatePosition = transform.position;
 | 
						|
 | 
						|
            gameObject.layer = LayerMask.NameToLayer(HandPlayerLayer);
 | 
						|
 | 
						|
            bodyCapsule = GetComponent<CapsuleCollider>();
 | 
						|
            bodyCapsule.material = Resources.Load<PhysicMaterial>("NoFriction");
 | 
						|
 | 
						|
            body = GetComponent<Rigidbody>();
 | 
						|
            body.interpolation = RigidbodyInterpolation.None;
 | 
						|
            body.freezeRotation = true;
 | 
						|
            if(body.collisionDetectionMode == CollisionDetectionMode.Discrete)
 | 
						|
                body.collisionDetectionMode = CollisionDetectionMode.ContinuousDynamic;
 | 
						|
 | 
						|
            if(forwardFollow == null)
 | 
						|
                forwardFollow = headCamera.transform;
 | 
						|
 | 
						|
            targetTrackedPos = trackingContainer.position;
 | 
						|
            if(useHeadCollision)
 | 
						|
                CreateHeadFollower();
 | 
						|
        }
 | 
						|
 | 
						|
 | 
						|
        public virtual void Start() {
 | 
						|
            StartCoroutine(CheckForTrackingStart());
 | 
						|
 | 
						|
            handPlayerMask = AutoHandExtensions.GetPhysicsLayerMask(gameObject.layer);
 | 
						|
#if UNITY_EDITOR
 | 
						|
                if (Selection.activeGameObject == gameObject)
 | 
						|
                {
 | 
						|
                    Selection.activeGameObject = null;
 | 
						|
                    Debug.Log("Auto Hand: highlighting hand component in the inspector can cause lag and quality reduction at runtime in VR. (Automatically deselecting at runtime) Remove this code at any time.", this);
 | 
						|
                    editorSelected = true;
 | 
						|
                }
 | 
						|
 | 
						|
                Application.quitting += () => { if (editorSelected && Selection.activeGameObject == null) Selection.activeGameObject = gameObject; };
 | 
						|
#endif
 | 
						|
        }
 | 
						|
 | 
						|
        protected virtual void OnEnable() {
 | 
						|
            EnableHand(handRight);
 | 
						|
            EnableHand(handLeft);
 | 
						|
        }
 | 
						|
 | 
						|
        protected virtual void OnDisable() {
 | 
						|
            DisableHand(handRight);
 | 
						|
            DisableHand(handLeft);
 | 
						|
        }
 | 
						|
 | 
						|
        bool trackingStarted = false;
 | 
						|
        Vector3 lastHeadPos;
 | 
						|
        IEnumerator CheckForTrackingStart() {
 | 
						|
            yield return new WaitForEndOfFrame();
 | 
						|
            yield return new WaitForFixedUpdate();
 | 
						|
            lastHeadPos = headCamera.transform.position;
 | 
						|
            while(!trackingStarted) {
 | 
						|
                if(headCamera.transform.position != lastHeadPos) {
 | 
						|
                    //OnHeadTrackingStarted();
 | 
						|
                    trackingStarted = true;
 | 
						|
                }
 | 
						|
                lastHeadPos = headCamera.transform.position;
 | 
						|
                yield return new WaitForEndOfFrame();
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        protected virtual void OnHeadTrackingStarted() {
 | 
						|
            SetPosition(transform.position);
 | 
						|
        }
 | 
						|
 | 
						|
        void CreateHeadFollower() {
 | 
						|
            if(headPhysicsFollower == null) {
 | 
						|
                var headFollower = new GameObject().transform;
 | 
						|
                headFollower.transform.position = headCamera.transform.position;
 | 
						|
                headFollower.name = "Head Follower";
 | 
						|
                headFollower.parent = transform.parent;
 | 
						|
 | 
						|
                var col = headFollower.gameObject.AddComponent<SphereCollider>();
 | 
						|
                col.material = bodyCapsule.material;
 | 
						|
                col.radius = bodyCapsule.radius;
 | 
						|
 | 
						|
                var headBody = headFollower.gameObject.AddComponent<Rigidbody>();
 | 
						|
                headBody.drag = 5;
 | 
						|
                headBody.angularDrag = 5;
 | 
						|
                headBody.freezeRotation = false;
 | 
						|
                headBody.mass = body.mass / 3f;
 | 
						|
 | 
						|
                headPhysicsFollower = headFollower.gameObject.AddComponent<HeadPhysicsFollower>();
 | 
						|
                headPhysicsFollower.headCamera = headCamera;
 | 
						|
                headPhysicsFollower.followBody = transform;
 | 
						|
                headPhysicsFollower.trackingContainer = trackingContainer;
 | 
						|
                headPhysicsFollower.Init();
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
 | 
						|
 | 
						|
        void CheckHands() {
 | 
						|
            if(lastLeftHand != handLeft) {
 | 
						|
                EnableHand(handLeft);
 | 
						|
                lastLeftHand = handLeft;
 | 
						|
            }
 | 
						|
 | 
						|
            if(lastRightHand != handRight) {
 | 
						|
                EnableHand(handRight);
 | 
						|
                lastRightHand = handRight;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
 | 
						|
        void EnableHand(Hand hand) {
 | 
						|
            hand.OnGrabbed += OnHandGrab;
 | 
						|
            hand.OnReleased += OnHandRelease;
 | 
						|
 | 
						|
 | 
						|
            if(allowClimbing) {
 | 
						|
                hand.OnGrabbed += StartClimb;
 | 
						|
                hand.OnReleased += EndClimb;
 | 
						|
            }
 | 
						|
 | 
						|
            if(allowBodyPushing) {
 | 
						|
                hand.OnGrabbed += StartGrabPush;
 | 
						|
                hand.OnReleased += EndGrabPush;
 | 
						|
                hand.OnHandCollisionStart += StartPush;
 | 
						|
                hand.OnHandCollisionStop += StopPush;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        void DisableHand(Hand hand) {
 | 
						|
            hand.OnGrabbed -= OnHandGrab;
 | 
						|
            hand.OnReleased -= OnHandRelease;
 | 
						|
 | 
						|
            if(allowClimbing) {
 | 
						|
                hand.OnGrabbed -= StartClimb;
 | 
						|
                hand.OnReleased -= EndClimb;
 | 
						|
                if(climbing.ContainsKey(hand))
 | 
						|
                    climbing.Remove(hand);
 | 
						|
            }
 | 
						|
 | 
						|
            if(allowBodyPushing) {
 | 
						|
                hand.OnGrabbed -= StartGrabPush;
 | 
						|
                hand.OnReleased -= EndGrabPush;
 | 
						|
                hand.OnHandCollisionStart -= StartPush;
 | 
						|
                hand.OnHandCollisionStop -= StopPush;
 | 
						|
                if(hand.left) {
 | 
						|
                    pushLeft.Clear();
 | 
						|
                    pushLeftCount.Clear();
 | 
						|
                }
 | 
						|
                else {
 | 
						|
                    pushRight.Clear();
 | 
						|
                    pushRightCount.Clear();
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        void OnHandGrab(Hand hand, Grabbable grab) {
 | 
						|
            grab.IgnoreColliders(bodyCapsule);
 | 
						|
            if(headPhysicsFollower != null)
 | 
						|
                grab?.IgnoreColliders(headPhysicsFollower.headCollider);
 | 
						|
        }
 | 
						|
 | 
						|
        void OnHandRelease(Hand hand, Grabbable grab) {
 | 
						|
            if(grab != null && grab.HeldCount() == 0) {
 | 
						|
                grab?.IgnoreColliders(bodyCapsule, false);
 | 
						|
                if(headPhysicsFollower != null)
 | 
						|
                    grab?.IgnoreColliders(headPhysicsFollower.headCollider, false);
 | 
						|
 | 
						|
                if(grab && grab.parentOnGrab && grab.body != null && !grab.body.isKinematic)
 | 
						|
                    grab.body.velocity += body.velocity / 2f;
 | 
						|
            } 
 | 
						|
        }
 | 
						|
 | 
						|
        public void IgnoreCollider(Collider col, bool ignore) {
 | 
						|
            Physics.IgnoreCollision(bodyCapsule, col, ignore);
 | 
						|
            Physics.IgnoreCollision(headPhysicsFollower.headCollider, col, ignore);
 | 
						|
        }
 | 
						|
 | 
						|
 | 
						|
        /// <summary>Sets move direction for this fixedupdate</summary>
 | 
						|
        public virtual void Move(Vector2 axis, bool useDeadzone = true, bool useRelativeDirection = false) {
 | 
						|
            moveDirection.x = (!useDeadzone || Mathf.Abs(axis.x) > movementDeadzone) ? axis.x : 0;
 | 
						|
            moveDirection.z = (!useDeadzone || Mathf.Abs(axis.y) > movementDeadzone) ? axis.y : 0;
 | 
						|
            if(useRelativeDirection)
 | 
						|
                moveDirection = transform.rotation * moveDirection;
 | 
						|
        }
 | 
						|
 | 
						|
        public virtual void Turn(float turnAxis) {
 | 
						|
            turnAxis = (Mathf.Abs(turnAxis) > turnDeadzone) ? turnAxis : 0;
 | 
						|
            turningAxis = turnAxis;
 | 
						|
        }
 | 
						|
 | 
						|
        protected virtual void LateUpdate() {
 | 
						|
            if(useMovement) {
 | 
						|
                UpdateTrackedObjects();
 | 
						|
                UpdateTurn(Time.deltaTime);
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        protected virtual void FixedUpdate() {
 | 
						|
            CheckHands();
 | 
						|
            UpdatePlayerHeight();
 | 
						|
 | 
						|
            if(useMovement) {
 | 
						|
                ApplyPushingForce();
 | 
						|
                ApplyClimbingForce();
 | 
						|
                UpdateRigidbody();
 | 
						|
                UpdatePlatform();
 | 
						|
                Ground();
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
 | 
						|
        protected virtual void UpdateRigidbody() {
 | 
						|
            var move = AlterDirection(moveDirection);
 | 
						|
            var yVel = body.velocity.y;
 | 
						|
 | 
						|
            //1. Moves velocity towards desired push direction
 | 
						|
            if (pushAxis != Vector3.zero) {
 | 
						|
                body.velocity = Vector3.MoveTowards(body.velocity, pushAxis, pushingAcceleration * Time.fixedDeltaTime);
 | 
						|
                body.velocity *= Mathf.Clamp01(1 - pushingDrag * Time.fixedDeltaTime);
 | 
						|
            }
 | 
						|
 | 
						|
            //2. Moves velocity towards desired climb direction
 | 
						|
            if(climbAxis != Vector3.zero) {
 | 
						|
                body.velocity = Vector3.MoveTowards(body.velocity, climbAxis, climbingAcceleration * Time.fixedDeltaTime);
 | 
						|
                body.velocity *= Mathf.Clamp01(1 - climbingDrag * Time.fixedDeltaTime);
 | 
						|
            }
 | 
						|
 | 
						|
            //3. Moves velocity towards desired movement direction
 | 
						|
            if(move != Vector3.zero && CanInputMove()) {
 | 
						|
 | 
						|
                var newVel = Vector3.MoveTowards(body.velocity, move * maxMoveSpeed, moveAcceleration * Time.fixedDeltaTime);
 | 
						|
                if(newVel.magnitude > maxMoveSpeed)
 | 
						|
                    newVel = newVel.normalized * maxMoveSpeed;
 | 
						|
                body.velocity = newVel;
 | 
						|
            }
 | 
						|
 | 
						|
            //5. Checks if gravity should be turned off
 | 
						|
            if (IsClimbing() || pushAxis.y > 0)
 | 
						|
                body.useGravity = false;
 | 
						|
 | 
						|
 | 
						|
            //4. This creates extra drag when grounded to simulate foot strength, or if flying greats drag in every direction when not moving
 | 
						|
            if (move.magnitude <= movementDeadzone && isGrounded)
 | 
						|
                body.velocity *= (Mathf.Clamp01(1 - groundedDrag * Time.fixedDeltaTime));
 | 
						|
            else if(!useGrounding)
 | 
						|
                body.velocity *= (Mathf.Clamp01(1 - flyingDrag * Time.fixedDeltaTime));
 | 
						|
 | 
						|
            //6. This will keep velocity if consistent when moving while falling
 | 
						|
            if(body.useGravity)
 | 
						|
                body.velocity = new Vector3(body.velocity.x, yVel, body.velocity.z);
 | 
						|
 | 
						|
            SyncBodyHead();
 | 
						|
 | 
						|
        }
 | 
						|
 | 
						|
 | 
						|
        protected virtual void UpdateTrackedObjects() {
 | 
						|
 | 
						|
            var startRightHandPos = handRight.transform.position;
 | 
						|
            var startLeftHandPos = handLeft.transform.position;
 | 
						|
 | 
						|
            //Moves the tracked objects based on the physics bodys delta movement
 | 
						|
            targetTrackedPos += (transform.position - lastUpdatePosition);
 | 
						|
            var flatPos = new Vector3(targetTrackedPos.x, trackingContainer.position.y, targetTrackedPos.z);
 | 
						|
            trackingContainer.position = flatPos;
 | 
						|
 | 
						|
 | 
						|
            //This slow moves the head + controllers on the Y-axis so it doesn't jump when stepping up
 | 
						|
            if(isGrounded)
 | 
						|
                trackingContainer.position = Vector3.MoveTowards(trackingContainer.position, targetTrackedPos + Vector3.up * heightOffset, (Mathf.Abs(trackingContainer.position.y - targetTrackedPos.y) + 0.1f) * Time.deltaTime * heightSmoothSpeed);
 | 
						|
            else
 | 
						|
                trackingContainer.position = targetTrackedPos + Vector3.up * heightOffset;
 | 
						|
 | 
						|
 | 
						|
            //This code will move the tracking objects to match the body collider position when moving
 | 
						|
            var targetPos = transform.position - headCamera.transform.position; targetPos.y = 0;
 | 
						|
            targetPosOffset = Vector3.MoveTowards(targetPosOffset, targetPos, body.velocity.magnitude * Time.deltaTime);
 | 
						|
            trackingContainer.position += targetPosOffset;
 | 
						|
 | 
						|
 | 
						|
            if(headPhysicsFollower != null && isGrounded) {
 | 
						|
                //Keeps the head down when colliding something above it and manages bouncing back up when not
 | 
						|
                if(Vector3.Distance(headCamera.transform.position, headPhysicsFollower.transform.position) > headPhysicsFollower.headCollider.radius / 1.5f) {
 | 
						|
                    var idealPos = headPhysicsFollower.transform.position + (headCamera.transform.position - headPhysicsFollower.transform.position).normalized * headPhysicsFollower.headCollider.radius / 1.5f;
 | 
						|
                    var offsetPos = headCamera.transform.position - idealPos;
 | 
						|
 | 
						|
                    trackingContainer.position -= offsetPos;
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            //This helps prevent the hands from clipping
 | 
						|
            var deltaHandPos = handRight.transform.position - startRightHandPos;
 | 
						|
            if(pushRight.Count > 0)
 | 
						|
                handRight.transform.position -= deltaHandPos;
 | 
						|
            else 
 | 
						|
               PreventHandClipping(handRight, startRightHandPos);
 | 
						|
            
 | 
						|
            
 | 
						|
            deltaHandPos = handLeft.transform.position - startLeftHandPos;
 | 
						|
            if(pushLeft.Count > 0)
 | 
						|
                handLeft.transform.position -= deltaHandPos;
 | 
						|
            else 
 | 
						|
                PreventHandClipping(handLeft, startLeftHandPos);
 | 
						|
            
 | 
						|
 | 
						|
            lastUpdatePosition = transform.position;
 | 
						|
 | 
						|
        }
 | 
						|
 | 
						|
        void PreventHandClipping(Hand hand, Vector3 startPosition) {
 | 
						|
            var deltaHandPos = hand.transform.position - startPosition;
 | 
						|
            if (deltaHandPos.magnitude < Physics.defaultContactOffset)
 | 
						|
                return;
 | 
						|
 | 
						|
            var center = hand.handEncapsulationBox.transform.TransformPoint(hand.handEncapsulationBox.center) - deltaHandPos;
 | 
						|
            var halfExtents = hand.handEncapsulationBox.transform.TransformVector(hand.handEncapsulationBox.size) / 2f;
 | 
						|
            var hits = Physics.BoxCastAll(center, halfExtents, deltaHandPos, hand.handEncapsulationBox.transform.rotation, deltaHandPos.magnitude*1.5f, handPlayerMask);
 | 
						|
            for(int i = 0; i < hits.Length; i++) {
 | 
						|
                var hit = hits[i];
 | 
						|
                if(hit.collider.isTrigger)
 | 
						|
                    continue;
 | 
						|
 | 
						|
                if(hand.holdingObj == null || hit.collider.attachedRigidbody == null || (hit.collider.attachedRigidbody != hand.holdingObj.body && !hand.holdingObj.jointedBodies.Contains(hit.collider.attachedRigidbody))) {
 | 
						|
                    var deltaHitPos = hit.point - hand.transform.position;
 | 
						|
                    hand.transform.position = Vector3.MoveTowards(hand.transform.position, startPosition, deltaHitPos.magnitude);
 | 
						|
                    break;
 | 
						|
                }
 | 
						|
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
 | 
						|
        void SyncBodyHead() {
 | 
						|
            var delta = 50f * Time.fixedDeltaTime;
 | 
						|
            float scale = transform.lossyScale.x > transform.lossyScale.z ? transform.lossyScale.x : transform.lossyScale.z;
 | 
						|
            var flatHeadPos = headCamera.transform.position; flatHeadPos.y = 0;
 | 
						|
            var flatBodyPos = transform.position; flatBodyPos.y = 0;
 | 
						|
 | 
						|
            if(Vector3.Distance(flatHeadPos, flatBodyPos) > 0.05f* delta) {
 | 
						|
                var direction = headCamera.transform.position - transform.position; direction.y = 0;
 | 
						|
                if(!Physics.CheckCapsule(
 | 
						|
                direction * 0.1f * delta + scale * transform.position + Vector3.up * scale * bodyCapsule.radius,
 | 
						|
                direction * 0.1f * delta + transform.position - scale * Vector3.up * bodyCapsule.radius + scale * Vector3.up * bodyCapsule.height + Vector3.up * maxStepHeight/2f,
 | 
						|
                scale * bodyCapsule.radius, handPlayerMask, QueryTriggerInteraction.Ignore)) {
 | 
						|
                    offset = direction * 0.1f * delta;
 | 
						|
                    transform.position += offset;
 | 
						|
                    body.position += offset;
 | 
						|
                    targetTrackedPos -= offset;
 | 
						|
                }
 | 
						|
                else {
 | 
						|
                    for(int y = -80; y <= 80; y += 40) {
 | 
						|
                        var newDirection = Quaternion.Euler(0, y, 0) * direction;
 | 
						|
 | 
						|
                        if(!Physics.CheckCapsule(
 | 
						|
                            newDirection * 0.1f * delta + scale * transform.position + Vector3.up * scale * bodyCapsule.radius,
 | 
						|
                            newDirection * 0.1f * delta + transform.position - scale * Vector3.up * bodyCapsule.radius + scale * Vector3.up * bodyCapsule.height, 
 | 
						|
                            scale * bodyCapsule.radius,
 | 
						|
                            handPlayerMask, QueryTriggerInteraction.Ignore)) {
 | 
						|
                                offset = newDirection * 0.1f * delta;
 | 
						|
                                transform.position += offset;
 | 
						|
                                body.position = transform.position;
 | 
						|
                                targetTrackedPos -= offset;
 | 
						|
                                break;
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        protected virtual bool CanInputMove() {
 | 
						|
            return (allowClimbingMovement || !IsClimbing());
 | 
						|
        }
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
        protected virtual void UpdateTurn(float deltaTime) {
 | 
						|
 | 
						|
            //Snap turning
 | 
						|
            if(rotationType == RotationType.snap) {
 | 
						|
                if(Mathf.Abs(turningAxis) > turnDeadzone && axisReset) {
 | 
						|
                    var angle = turningAxis > turnDeadzone ? snapTurnAngle : -snapTurnAngle;
 | 
						|
 | 
						|
                    var targetPos = transform.position - headCamera.transform.position; targetPos.y = 0;
 | 
						|
 | 
						|
                    trackingContainer.position += targetPos;
 | 
						|
                    if(headPhysicsFollower != null) {
 | 
						|
                        headPhysicsFollower.transform.position += targetPos;
 | 
						|
                        headPhysicsFollower.body.position = headPhysicsFollower.transform.position;
 | 
						|
                    }
 | 
						|
                    lastUpdatePosition = new Vector3(transform.position.x, lastUpdatePosition.y, transform.position.z);
 | 
						|
 | 
						|
                    var handRightStartPos = handRight.transform.position;
 | 
						|
                    var handLeftStartPos = handLeft.transform.position;
 | 
						|
 | 
						|
                    trackingContainer.RotateAround(transform.position, Vector3.up, angle);
 | 
						|
 | 
						|
                    targetPosOffset = Vector3.zero;
 | 
						|
                    targetTrackedPos = new Vector3(trackingContainer.position.x, targetTrackedPos.y, trackingContainer.position.z);
 | 
						|
 | 
						|
                    handRight.SetMoveTo();
 | 
						|
                    handRight.SetHandLocation(handRight.moveTo.position, handRight.moveTo.rotation);
 | 
						|
                    handLeft.SetMoveTo();
 | 
						|
                    handLeft.SetHandLocation(handLeft.moveTo.position, handLeft.moveTo.rotation);
 | 
						|
 | 
						|
                    PreventHandClipping(handRight, handRightStartPos);
 | 
						|
                    PreventHandClipping(handLeft, handLeftStartPos);
 | 
						|
 | 
						|
                    OnSnapTurn?.Invoke(this);
 | 
						|
                    axisReset = false;
 | 
						|
                }
 | 
						|
            }
 | 
						|
            else if(Mathf.Abs(turningAxis) > turnDeadzone) {
 | 
						|
 | 
						|
                var targetPos = transform.position - headCamera.transform.position; targetPos.y = 0;
 | 
						|
 | 
						|
                trackingContainer.position += targetPos;
 | 
						|
                if(headPhysicsFollower != null) {
 | 
						|
                    headPhysicsFollower.transform.position += targetPos;
 | 
						|
                    headPhysicsFollower.body.position = headPhysicsFollower.transform.position;
 | 
						|
                }
 | 
						|
                lastUpdatePosition = new Vector3(transform.position.x, lastUpdatePosition.y, transform.position.z);
 | 
						|
 | 
						|
                trackingContainer.RotateAround(transform.position, Vector3.up, smoothTurnSpeed * (Mathf.MoveTowards(turningAxis, 0, turnDeadzone)) * deltaTime);
 | 
						|
 | 
						|
 | 
						|
                targetPosOffset = Vector3.zero;
 | 
						|
                targetTrackedPos = new Vector3(trackingContainer.position.x, targetTrackedPos.y, trackingContainer.position.z);
 | 
						|
 | 
						|
                axisReset = false;
 | 
						|
            }
 | 
						|
 | 
						|
            if(Mathf.Abs(turningAxis) < turnResetzone)
 | 
						|
                axisReset = true;
 | 
						|
        }
 | 
						|
 | 
						|
 | 
						|
 | 
						|
        protected virtual void Ground() {
 | 
						|
            
 | 
						|
            isGrounded = false;
 | 
						|
            newClosestHit = new RaycastHit();
 | 
						|
 | 
						|
            if(!tempDisableGrounding && useGrounding && !IsClimbing() && !(pushAxis.y > 0)) {
 | 
						|
                highestPoint = -1;
 | 
						|
 | 
						|
 | 
						|
 | 
						|
                float stepAngle;
 | 
						|
                float dist;
 | 
						|
                float scale = transform.lossyScale.x > transform.lossyScale.z ? transform.lossyScale.x : transform.lossyScale.z;
 | 
						|
 | 
						|
                var maxStepHeight = this.maxStepHeight;
 | 
						|
                maxStepHeight *= climbAxis.y > 0 ? climbUpStepHeightMultiplier : 1;
 | 
						|
                maxStepHeight *= pushAxis.y > 0 ? pushUpStepHeightMultiplier : 1;
 | 
						|
                maxStepHeight *= scale;
 | 
						|
 | 
						|
                var point1 = scale * bodyCapsule.center + transform.position + scale * bodyCapsule.height / 2 * -Vector3.up + (maxStepHeight + scale * bodyCapsule.radius * 2) * Vector3.up;
 | 
						|
                var point2 = scale * bodyCapsule.center + transform.position + (scale * bodyCapsule.height / 2f + groundedOffset) * -Vector3.up;
 | 
						|
                var radius = scale*bodyCapsule.radius*2 + Physics.defaultContactOffset*2;
 | 
						|
                var groundHits = Physics.SphereCastAll(point1, radius, -Vector3.up, Vector3.Distance(point1, point2) + scale * bodyCapsule.radius*4, groundLayerMask, QueryTriggerInteraction.Ignore);
 | 
						|
 | 
						|
                for(int i = 0; i < groundHits.Length; i++) {
 | 
						|
                    var hit = groundHits[i];
 | 
						|
 | 
						|
                    if(hit.collider != bodyCapsule) {
 | 
						|
                        if(hit.point.y >= point2.y && hit.point.y <= point2.y + maxStepHeight) {
 | 
						|
                            stepAngle = Vector3.Angle(hit.normal, Vector3.up);
 | 
						|
                            dist = hit.point.y - transform.position.y;
 | 
						|
 | 
						|
                            if(stepAngle < maxStepAngle && dist > highestPoint) {
 | 
						|
                                isGrounded = true;
 | 
						|
                                highestPoint = dist;
 | 
						|
                                newClosestHit = hit;
 | 
						|
                            }
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                }
 | 
						|
 | 
						|
                if(isGrounded) {
 | 
						|
                    body.velocity = new Vector3(body.velocity.x, 0, body.velocity.z);
 | 
						|
                    body.position = new Vector3(body.position.x, newClosestHit.point.y, body.position.z);
 | 
						|
                    transform.position = body.position;
 | 
						|
                }
 | 
						|
 | 
						|
                body.useGravity = !isGrounded;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        public bool IsGrounded() {
 | 
						|
            return isGrounded;
 | 
						|
        }
 | 
						|
 | 
						|
        public void ToggleFlying() {
 | 
						|
            useGrounding = !useGrounding;
 | 
						|
            body.useGravity = useGrounding;
 | 
						|
        }
 | 
						|
 | 
						|
        protected virtual void UpdatePlayerHeight() {
 | 
						|
            if(crouching != lastCrouching) {
 | 
						|
                if(lastCrouching)
 | 
						|
                    heightOffset += lastCrouchingHeight;
 | 
						|
                if(!lastCrouching)
 | 
						|
                    heightOffset -= crouchHeight;
 | 
						|
 | 
						|
                lastCrouching = crouching;
 | 
						|
                lastCrouchingHeight = crouchHeight;
 | 
						|
            }
 | 
						|
 | 
						|
            if(autoAdjustColliderHeight) {
 | 
						|
                playerHeight = Mathf.Clamp(headCamera.transform.position.y - transform.position.y, minMaxHeight.x, minMaxHeight.y);
 | 
						|
                bodyCapsule.height = playerHeight;
 | 
						|
                var centerHeight = playerHeight / 2f > bodyCapsule.radius ? playerHeight / 2f : bodyCapsule.radius;
 | 
						|
                bodyCapsule.center = new Vector3(0, centerHeight, 0);
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
 | 
						|
        protected void UpdatePlatform()
 | 
						|
        {
 | 
						|
            if (isGrounded && newClosestHit.transform != null && (platformingLayerMask == (platformingLayerMask | (1 << newClosestHit.collider.gameObject.layer)))) {
 | 
						|
                if (!newClosestHit.transform.Equals(closestHit.transform)) {
 | 
						|
                    closestHit = newClosestHit;
 | 
						|
                    lastPlatformPosition = closestHit.transform.position;
 | 
						|
                    lastPlatformRotation = closestHit.transform.rotation;
 | 
						|
                }
 | 
						|
                else if(newClosestHit.transform.Equals(closestHit.transform))
 | 
						|
                {
 | 
						|
                    if (closestHit.transform.position != lastPlatformPosition || closestHit.transform.rotation.eulerAngles != lastPlatformRotation.eulerAngles) {
 | 
						|
                        closestHit = newClosestHit;
 | 
						|
                        Transform ruler = AutoHandExtensions.transformRuler;
 | 
						|
                        ruler.position = transform.position;
 | 
						|
                        ruler.rotation = transform.rotation;
 | 
						|
                        ruler.position += closestHit.transform.position - lastPlatformPosition;
 | 
						|
 | 
						|
                        var deltaPos = ruler.transform.position - transform.position;
 | 
						|
                        var deltaRot = (closestHit.transform.rotation * Quaternion.Inverse(lastPlatformRotation));
 | 
						|
 | 
						|
                        ruler.transform.RotateAround(closestHit.transform.position, Vector3.up, deltaRot.eulerAngles.y);
 | 
						|
                        trackingContainer.RotateAround(headCamera.transform.position, Vector3.up, deltaRot.eulerAngles.y);
 | 
						|
 | 
						|
                        transform.position += deltaPos;
 | 
						|
                        body.position = transform.position;
 | 
						|
 | 
						|
                        trackingContainer.position += deltaPos;
 | 
						|
 | 
						|
                        lastUpdatePosition = transform.position;
 | 
						|
 | 
						|
                        targetPosOffset = Vector3.zero;
 | 
						|
 | 
						|
                        targetTrackedPos = new Vector3(trackingContainer.position.x, targetTrackedPos.y + deltaPos.y, trackingContainer.position.z);
 | 
						|
                        lastPlatformPosition = closestHit.transform.position;
 | 
						|
                        lastPlatformRotation = closestHit.transform.rotation;
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
 | 
						|
        public void Jump(float jumpPower = 1) {
 | 
						|
            if(isGrounded) {
 | 
						|
                DisableGrounding(0.1f);
 | 
						|
                body.useGravity = true;
 | 
						|
                body.AddForce(Vector3.up * jumpPower, ForceMode.VelocityChange);
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
 | 
						|
        public void DisableGrounding(float seconds) {
 | 
						|
            if(disableGroundingRoutine != null)
 | 
						|
                StopCoroutine(disableGroundingRoutine);
 | 
						|
            disableGroundingRoutine = StartCoroutine(DisableGroundingSecondsRoutine(seconds));
 | 
						|
        }
 | 
						|
 | 
						|
        Coroutine disableGroundingRoutine;
 | 
						|
        IEnumerator DisableGroundingSecondsRoutine(float seconds) {
 | 
						|
            tempDisableGrounding = true;
 | 
						|
            isGrounded = false;
 | 
						|
            yield return new WaitForSeconds(seconds);
 | 
						|
            tempDisableGrounding = false;
 | 
						|
 | 
						|
        }
 | 
						|
 | 
						|
        /// <summary>Legacy function, use body.addfoce instead</summary>
 | 
						|
        public void AddVelocity(Vector3 force, ForceMode mode = ForceMode.Acceleration) {
 | 
						|
            body.AddForce(force, mode);
 | 
						|
        }
 | 
						|
 | 
						|
        protected virtual void StartPush(Hand hand, GameObject other) {
 | 
						|
            if(!allowBodyPushing || IsClimbing())
 | 
						|
                return;
 | 
						|
 | 
						|
            if(other.CanGetComponent(out Pushable push) && push.enabled) {
 | 
						|
                if(hand.left) {
 | 
						|
                    if(!pushLeft.ContainsKey(push)) {
 | 
						|
                        pushLeft.Add(push, hand);
 | 
						|
                        pushLeftCount.Add(push, 1);
 | 
						|
                    }
 | 
						|
                    else {
 | 
						|
                        pushLeftCount[push]++;
 | 
						|
                    }
 | 
						|
                }
 | 
						|
 | 
						|
                if(!hand.left && !pushRight.ContainsKey(push)) {
 | 
						|
                    if(!pushRight.ContainsKey(push)) {
 | 
						|
                        pushRight.Add(push, hand);
 | 
						|
                        pushRightCount.Add(push, 1);
 | 
						|
                    }
 | 
						|
                    else {
 | 
						|
                        pushRightCount[push]++;
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        protected virtual void StopPush(Hand hand, GameObject other) {
 | 
						|
            if(!allowBodyPushing)
 | 
						|
                return;
 | 
						|
 | 
						|
            if(other.CanGetComponent(out Pushable push)) {
 | 
						|
                if(hand.left && pushLeft.ContainsKey(push)) {
 | 
						|
                    var count = --pushLeftCount[push];
 | 
						|
                    if(count == 0) {
 | 
						|
                        pushLeft.Remove(push);
 | 
						|
                        pushLeftCount.Remove(push);
 | 
						|
                    }
 | 
						|
                }
 | 
						|
                if(!hand.left && pushRight.ContainsKey(push)) {
 | 
						|
                    var count = --pushRightCount[push];
 | 
						|
                    if(count == 0) {
 | 
						|
                        pushRight.Remove(push);
 | 
						|
                        pushRightCount.Remove(push);
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        protected virtual void StartGrabPush(Hand hand, Grabbable grab) {
 | 
						|
            if(!allowBodyPushing)
 | 
						|
                return;
 | 
						|
 | 
						|
            if(grab.CanGetComponent(out Pushable push) && push.enabled) {
 | 
						|
                if(hand.left && !pushLeft.ContainsKey(push)) {
 | 
						|
                    pushLeft.Add(push, hand);
 | 
						|
                    pushLeftCount.Add(push, 1);
 | 
						|
                }
 | 
						|
 | 
						|
                if(!hand.left && !pushRight.ContainsKey(push)) {
 | 
						|
                    pushRight.Add(push, hand);
 | 
						|
                    pushRightCount.Add(push, 1);
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        protected virtual void EndGrabPush(Hand hand, Grabbable grab) {
 | 
						|
            if(grab != null && grab.CanGetComponent(out Pushable push)) {
 | 
						|
                if(hand.left && pushLeft.ContainsKey(push)) {
 | 
						|
                    pushLeft.Remove(push);
 | 
						|
                    pushLeftCount.Remove(push);
 | 
						|
                }
 | 
						|
                else if(!hand.left && pushRight.ContainsKey(push)) {
 | 
						|
                    pushRight.Remove(push);
 | 
						|
                    pushRightCount.Remove(push);
 | 
						|
                }
 | 
						|
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        protected virtual void ApplyPushingForce() {
 | 
						|
            pushAxis = Vector3.zero;
 | 
						|
            if(allowBodyPushing) {
 | 
						|
 | 
						|
                foreach(var push in pushRight) {
 | 
						|
                    if(push.Key.enabled && !push.Value.IsGrabbing()) {
 | 
						|
                        Vector3 offset = Vector3.zero;
 | 
						|
                        var distance = Vector3.Distance(push.Value.body.position, push.Value.moveTo.position);
 | 
						|
                        if(distance > 0)
 | 
						|
                            offset = Vector3.Scale((push.Value.body.position - push.Value.moveTo.position), push.Key.strengthScale);
 | 
						|
 | 
						|
                        offset = Vector3.Scale(offset, pushingStrength);
 | 
						|
                        pushAxis += offset / 2f;
 | 
						|
                    }
 | 
						|
                }
 | 
						|
 | 
						|
                foreach(var push in pushLeft) {
 | 
						|
                    if(push.Key.enabled && !push.Value.IsGrabbing()) {
 | 
						|
                        Vector3 offset = Vector3.zero;
 | 
						|
                        var distance = Vector3.Distance(push.Value.body.position, push.Value.moveTo.position);
 | 
						|
                        if(distance > 0)
 | 
						|
                            offset = Vector3.Scale((push.Value.body.position - push.Value.moveTo.position), push.Key.strengthScale);
 | 
						|
 | 
						|
                        offset = Vector3.Scale(offset, pushingStrength);
 | 
						|
                        pushAxis += offset / 2f;
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        public bool IsPushing() {
 | 
						|
            foreach(var push in pushRight)
 | 
						|
                if(push.Key.enabled)
 | 
						|
                    return true;
 | 
						|
            foreach(var push in pushLeft)
 | 
						|
                if(push.Key.enabled)
 | 
						|
                    return true;
 | 
						|
 | 
						|
            return false;
 | 
						|
        }
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
        protected virtual void StartClimb(Hand hand, Grabbable grab) {
 | 
						|
            if(!allowClimbing)
 | 
						|
                return;
 | 
						|
 | 
						|
            if(!climbing.ContainsKey(hand) && grab != null && grab.CanGetComponent(out Climbable climbbable) && climbbable.enabled) {
 | 
						|
                if(climbing.Count == 0) {
 | 
						|
                    pushRight.Clear();
 | 
						|
                    pushRightCount.Clear();
 | 
						|
                    pushLeft.Clear();
 | 
						|
                    pushLeftCount.Clear();
 | 
						|
                }
 | 
						|
 | 
						|
                if(climbing.Count == 0)
 | 
						|
                    body.velocity /= 4f;
 | 
						|
 | 
						|
                climbing.Add(hand, climbbable);
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        protected virtual void EndClimb(Hand hand, Grabbable grab) {
 | 
						|
            if(!allowClimbing)
 | 
						|
                return;
 | 
						|
 | 
						|
            if(climbing.ContainsKey(hand))
 | 
						|
                climbing.Remove(hand);
 | 
						|
 | 
						|
            foreach(var climb in climbing)
 | 
						|
                climb.Key.ResetGrabOffset();
 | 
						|
        }
 | 
						|
 | 
						|
        protected virtual void ApplyClimbingForce() {
 | 
						|
            climbAxis = Vector3.zero;
 | 
						|
            if(allowClimbing && climbing.Count > 0) {
 | 
						|
                foreach(var hand in climbing) {
 | 
						|
                    if(hand.Value.enabled) {
 | 
						|
                        var offset = Vector3.Scale(hand.Key.body.position - hand.Key.moveTo.position, hand.Value.axis);
 | 
						|
                        offset = Vector3.Scale(offset, climbingStrength);
 | 
						|
                        climbAxis += offset / climbing.Count;
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        public bool IsClimbing() {
 | 
						|
            foreach(var climb in climbing)
 | 
						|
                if(climb.Value.enabled)
 | 
						|
                    return true;
 | 
						|
            return false;
 | 
						|
        }
 | 
						|
 | 
						|
 | 
						|
 | 
						|
        public virtual void SetPosition(Vector3 position) {
 | 
						|
            SetPosition(position, headCamera.transform.rotation);
 | 
						|
        }
 | 
						|
 | 
						|
        public virtual void SetPosition(Vector3 position, Quaternion rotation) {
 | 
						|
            Vector3 deltaPos = position - transform.position;
 | 
						|
            transform.position += deltaPos;
 | 
						|
            //This code will move the tracking objects to match the body collider position when moving
 | 
						|
            var targetPos = transform.position - headCamera.transform.position; targetPos.y = deltaPos.y;
 | 
						|
            trackingContainer.position += targetPos;
 | 
						|
            lastUpdatePosition = transform.position;
 | 
						|
            targetTrackedPos = new Vector3(trackingContainer.position.x, targetTrackedPos.y + deltaPos.y, trackingContainer.position.z);
 | 
						|
            targetPosOffset = Vector3.zero;
 | 
						|
            body.position = transform.position;
 | 
						|
            if(headPhysicsFollower != null) {
 | 
						|
                headPhysicsFollower.transform.position += targetPos;
 | 
						|
                headPhysicsFollower.body.position = headPhysicsFollower.transform.position;
 | 
						|
            }
 | 
						|
 | 
						|
            handRight.body.position = handRight.transform.position;
 | 
						|
            handLeft.body.position = handLeft.transform.position;
 | 
						|
            handRight.SetHandLocation(handRight.transform.position);
 | 
						|
            handLeft.SetHandLocation(handLeft.transform.position);
 | 
						|
 | 
						|
            var deltaRot = rotation * Quaternion.Inverse(headCamera.transform.rotation);
 | 
						|
            trackingContainer.RotateAround(headCamera.transform.position, Vector3.up, deltaRot.eulerAngles.y);
 | 
						|
 | 
						|
            if(deltaRot.eulerAngles.magnitude > 10f || deltaPos.magnitude > 0.5f)
 | 
						|
                OnTeleported?.Invoke(this);
 | 
						|
        }
 | 
						|
 | 
						|
        public virtual void SetRotation(Quaternion rotation) {
 | 
						|
            var targetPos = transform.position - headCamera.transform.position; targetPos.y = 0;
 | 
						|
 | 
						|
            trackingContainer.position += targetPos;
 | 
						|
            if(headPhysicsFollower != null) {
 | 
						|
                headPhysicsFollower.transform.position += targetPos;
 | 
						|
                headPhysicsFollower.body.position = headPhysicsFollower.transform.position;
 | 
						|
            }
 | 
						|
            lastUpdatePosition = transform.position;
 | 
						|
 | 
						|
            var deltaRot = rotation * Quaternion.Inverse(headCamera.transform.rotation);
 | 
						|
            trackingContainer.RotateAround(headCamera.transform.position, Vector3.up, deltaRot.eulerAngles.y);
 | 
						|
 | 
						|
            targetPosOffset = Vector3.zero;
 | 
						|
            targetTrackedPos = new Vector3(trackingContainer.position.x, targetTrackedPos.y, trackingContainer.position.z);
 | 
						|
 | 
						|
            if(deltaRot.eulerAngles.magnitude > 10f)
 | 
						|
                OnTeleported?.Invoke(this);
 | 
						|
        }
 | 
						|
 | 
						|
        public virtual void AddRotation(Quaternion addRotation) {
 | 
						|
            var targetPos = transform.position - headCamera.transform.position; targetPos.y = 0;
 | 
						|
 | 
						|
            trackingContainer.position += targetPos;
 | 
						|
            if(headPhysicsFollower != null) {
 | 
						|
                headPhysicsFollower.transform.position += targetPos;
 | 
						|
                headPhysicsFollower.body.position = headPhysicsFollower.transform.position;
 | 
						|
            }
 | 
						|
            lastUpdatePosition = transform.position;
 | 
						|
 | 
						|
            trackingContainer.RotateAround(headCamera.transform.position, Vector3.up, addRotation.eulerAngles.y);
 | 
						|
 | 
						|
            targetPosOffset = Vector3.zero;
 | 
						|
            targetTrackedPos = new Vector3(trackingContainer.position.x, targetTrackedPos.y, trackingContainer.position.z);
 | 
						|
 | 
						|
            if(addRotation.eulerAngles.magnitude > 10f)
 | 
						|
                OnTeleported?.Invoke(this);
 | 
						|
        }
 | 
						|
 | 
						|
        public virtual void Recenter() {
 | 
						|
            var targetPos = transform.position - headCamera.transform.position; targetPos.y = 0;
 | 
						|
 | 
						|
            trackingContainer.position += targetPos;
 | 
						|
            if(headPhysicsFollower != null) {
 | 
						|
                headPhysicsFollower.transform.position += targetPos;
 | 
						|
                headPhysicsFollower.body.position = headPhysicsFollower.transform.position;
 | 
						|
            }
 | 
						|
            lastUpdatePosition = transform.position;
 | 
						|
 | 
						|
            targetPosOffset = Vector3.zero;
 | 
						|
            targetTrackedPos = new Vector3(trackingContainer.position.x, targetTrackedPos.y, trackingContainer.position.z);
 | 
						|
        }
 | 
						|
 | 
						|
        public bool IsHolding(Grabbable grab) {
 | 
						|
            return handRight.GetHeld() == grab || handLeft.GetHeld() == grab;
 | 
						|
        }
 | 
						|
 | 
						|
        Vector3 AlterDirection(Vector3 moveAxis) {
 | 
						|
            if(useGrounding)
 | 
						|
                return Quaternion.AngleAxis(forwardFollow.eulerAngles.y, Vector3.up) * (new Vector3(moveAxis.x, moveAxis.y, moveAxis.z));
 | 
						|
            else
 | 
						|
                return forwardFollow.rotation * (new Vector3(moveAxis.x, moveAxis.y, moveAxis.z));
 | 
						|
        }
 | 
						|
 | 
						|
 | 
						|
    }
 | 
						|
}
 |