Player Physics Script for Unity
unknown
csharp
a year ago
3.9 kB
18
Indexable
using System;
using System.Collections;
using UnityEngine;
public class PlayerPhysics : MonoBehaviour
{
public Rigidbody RB;
public LayerMask layermask;
public Vector3 horizontalVelocity => Vector3.ProjectOnPlane(RB.velocity, RB.transform.up);
public Vector3 verticalVelocity => Vector3.Project(RB.velocity, RB.transform.up);
public float verticalSpeed => Vector3.Dot(RB.velocity, RB.transform.up);
public float speed => horizontalVelocity.magnitude;
[SerializeField] float gravity;
[SerializeField] float smoothRotationSpeed = 5f; // Speed for smoothing rotation
[SerializeField] float smoothPositionSpeed = 5f; // Speed for smoothing position
public Action onPlayerPhysicsUpdate;
void FixedUpdate()
{
onPlayerPhysicsUpdate?.Invoke();
if(!groundInfo.ground)
{
Gravity();
}
if(groundInfo.ground && verticalSpeed < RB.sleepThreshold)
RB.velocity = horizontalVelocity;
StartCoroutine(LateFixedUpdateRoutine());
IEnumerator LateFixedUpdateRoutine()
{
yield return new WaitForFixedUpdate();
LateFixedUpdate();
}
}
void Gravity()
{
RB.velocity -= Vector3.up * gravity * Time.deltaTime;
}
void LateFixedUpdate()
{
Ground();
Snap();
if (groundInfo.ground)
RB.velocity = horizontalVelocity;
}
[SerializeField] float groundDistance;
public struct GroundInfo
{
public Vector3 point;
public bool ground;
public Vector3 normal;
}
[HideInInspector] public GroundInfo groundInfo;
public Action onGroundEnter;
public Action onGroundExit;
void Ground()
{
float maxDistance = Mathf.Max(RB.centerOfMass.y, 0) + (RB.sleepThreshold * Time.fixedDeltaTime);
if (groundInfo.ground && verticalSpeed < RB.sleepThreshold)
maxDistance += groundDistance;
bool ground = Physics.Raycast(RB.worldCenterOfMass, -RB.transform.up, out RaycastHit hit, maxDistance, layermask, QueryTriggerInteraction.Ignore);
Vector3 point = ground ? hit.point : RB.transform.position;
Vector3 normal = ground ? hit.normal : Vector3.up;
if (ground != groundInfo.ground)
{
if (ground)
onGroundEnter?.Invoke();
else
onGroundExit?.Invoke();
}
groundInfo = new()
{
point = point,
normal = normal,
ground = ground
};
}
void Snap()
{
Vector3 targetUp = groundInfo.normal;
if (IsOnSlope())
{
RB.transform.up = Vector3.Slerp(RB.transform.up, targetUp, Time.deltaTime * smoothRotationSpeed);
}
else
{
RB.transform.up = targetUp;
}
Vector3 goal = groundInfo.point;
Vector3 difference = goal - RB.transform.position;
if (!RB.SweepTest(difference, out _, difference.magnitude, QueryTriggerInteraction.Ignore))
{
if (IsOnSlope())
{
RB.transform.position = Vector3.Lerp(RB.transform.position, goal, Time.deltaTime * smoothPositionSpeed);
}
else
{
RB.transform.position = goal;
}
}
}
bool IsOnSlope()
{
Vector3 forward = RB.transform.forward;
Vector3 projectedForward = Vector3.ProjectOnPlane(forward, Vector3.up).normalized;
float cosAngle = Vector3.Dot(projectedForward, forward.normalized);
float angleInDegrees = Mathf.Acos(cosAngle) * Mathf.Rad2Deg;
return Mathf.Abs(angleInDegrees) > 0.1f;
}
}
Editor is loading...
Leave a Comment