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));
|
|
}
|
|
|
|
|
|
}
|
|
}
|