PlayerPhysics
unknown
csharp
24 days ago
5.4 kB
8
Indexable
Never
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; } }
Leave a Comment