PlayerPhysics
unknown
csharp
a year ago
5.4 kB
26
Indexable
using System;
using System.Collections;
using UnityEngine;
public class PlayerPhysics : MonoBehaviour
{
// Public Rigidbody component and layer mask for ground detection
public Rigidbody RB;
public LayerMask layermask;
// Horizontal velocity (velocity along the plane perpendicular to the up direction)
public Vector3 horizontalVelocity => Vector3.ProjectOnPlane(RB.velocity, RB.transform.up);
// Vertical velocity (velocity in the direction of the up vector)
public Vector3 verticalVelocity => Vector3.Project(RB.velocity, RB.transform.up);
// Speed in the vertical direction (dot product of velocity and up vector)
public float verticalSpeed => Vector3.Dot(RB.velocity, RB.transform.up);
// Horizontal speed (magnitude of horizontal velocity)
public float speed => horizontalVelocity.magnitude;
// Serialized gravity variable to control gravity strength
[SerializeField] float gravity;
// Delegate that allows other scripts to hook into the player's physics update
public Action onPlayerPhysicsUpdate;
// FixedUpdate is called at a consistent rate for physics calculations
void FixedUpdate()
{
// Invoke any functions hooked to the physics update
onPlayerPhysicsUpdate?.Invoke();
// If the player is not grounded, apply gravity
if(!groundInfo.ground)
{
Gravity();
}
// If grounded and the vertical speed is below the sleep threshold, keep only horizontal velocity
if(groundInfo.ground && verticalSpeed < RB.sleepThreshold)
RB.velocity = horizontalVelocity;
// Start coroutine for LateFixedUpdate
StartCoroutine(LateFixedUpdateRoutine());
// Coroutine to ensure LateFixedUpdate happens after physics updates
IEnumerator LateFixedUpdateRoutine()
{
yield return new WaitForFixedUpdate();
LateFixedUpdate();
}
}
// Apply gravity by modifying the player's velocity
void Gravity()
{
RB.velocity -= Vector3.up * gravity * Time.deltaTime;
}
// LateFixedUpdate handles ground detection and snapping to the ground surface
void LateFixedUpdate()
{
Ground();
Snap();
// If grounded, set the velocity to only include horizontal movement
if(groundInfo.ground)
RB.velocity = horizontalVelocity;
}
// Ground detection distance, defined in the Inspector
[SerializeField] float groundDistance;
// Struct to store information about ground detection
public struct GroundInfo
{
public Vector3 point; // Point where the ground is detected
public bool ground; // Whether the player is grounded
public Vector3 normal; // Normal of the ground surface
}
// Stores information about the current ground state
[HideInInspector] public GroundInfo groundInfo;
// Actions to be invoked when the player enters or exits the ground
public Action onGroundEnter;
public Action onGroundExit;
// Ground detection method using raycasting
void Ground()
{
// Calculate the maximum distance for raycasting to detect the ground
float maxDistance = Mathf.Max(RB.centerOfMass.y, 0) + (RB.sleepThreshold * Time.fixedDeltaTime);
// Extend the detection range if the player is grounded and moving slowly
if(groundInfo.ground && verticalSpeed < RB.sleepThreshold)
maxDistance += groundDistance;
// Perform the raycast from the player's center of mass downward
bool ground = Physics.Raycast(RB.worldCenterOfMass, -RB.transform.up, out RaycastHit hit, maxDistance, layermask, QueryTriggerInteraction.Ignore);
// If ground is detected, update the hit point and normal, else use current position and default normal
Vector3 point = ground ? hit.point : RB.transform.position;
Vector3 normal = ground ? hit.normal : Vector3.up;
// Trigger ground enter/exit events if the ground state changes
if (ground != groundInfo.ground)
{
if (ground)
onGroundEnter?.Invoke(); // Ground entered
else
onGroundExit?.Invoke(); // Ground exited
}
// Update ground information
groundInfo = new()
{
point = point,
normal = normal,
ground = ground
};
}
// Adjust the player's position and orientation to snap to the ground surface
void Snap()
{
// Align the player's up direction with the ground's normal
RB.transform.up = groundInfo.normal;
// Calculate the desired position (the point of ground contact)
Vector3 goal = groundInfo.point;
// Calculate the difference between the current position and goal position
Vector3 difference = goal - RB.transform.position;
// Perform a sweep test to check for obstructions before snapping to the ground
if (RB.SweepTest(difference, out _, difference.magnitude, QueryTriggerInteraction.Ignore)) return;
// Snap the player to the goal position if no obstructions are detected
RB.transform.position = goal;
}
}
Editor is loading...
Leave a Comment