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#

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