Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- using System.Collections;
- using System.Collections.Generic;
- using UnityEngine;
- using UnityEngine.InputSystem;
- public class PlayerRigidbodyControler : MonoBehaviour
- {
- [Header("References")]
- public Transform headTransform;
- Rigidbody rb;
- CapsuleCollider capsule;
- [Header("Movement")]
- public float pushPower = 2f;
- public float maxPushSpeed = 10f;
- public float torquePower = 1f;
- public float walkSpeed = 5f;
- public float sprintSpeed = 8f;
- public float acceleration = 30f;
- public float groundDrag = 5f;
- public float airDrag = 0.1f;
- [Header("Jump")]
- public float jumpForce = 7f;
- public int maxJumps = 1; // set 2 for double jump
- public float coyoteTime = 0.15f;
- public float JumpBuferTime = 0.12f;
- [Header("Ground Check")]
- public LayerMask groundMask;
- public float groundCheckOffset = 0.08f; // distance to cast downwards
- // NOTE: we do not use groundCheckRadius anymore for checks, only gizmo radius
- [Header("Look")]
- public float mouseSensitivity = 1.2f;
- public float maxLookAngle = 85f;
- Vector2 moveInput = Vector2.zero;
- Vector2 lookInput = Vector2.zero;
- bool jumpPressed = false;
- bool isGrounded = false;
- bool wasGrounded = false; // previous frame
- float lastGroundedTime = -10f;
- float lastJumpPressedTime = -10f;
- int jumpsRemaining;
- float verticalLook = 0f;
- bool isAlive = true;
- void Awake()
- {
- rb = GetComponent<Rigidbody>();
- capsule = GetComponent<CapsuleCollider>();
- rb.interpolation = RigidbodyInterpolation.Interpolate;
- rb.collisionDetectionMode = CollisionDetectionMode.Continuous;
- rb.constraints = RigidbodyConstraints.FreezeRotationX | RigidbodyConstraints.FreezeRotationZ;
- jumpsRemaining = maxJumps;
- }
- void Start()
- {
- if (headTransform == null) Debug.LogWarning("Assign headTransform in PlayerRigidbodyControler.");
- Cursor.lockState = CursorLockMode.Locked;
- Cursor.visible = false;
- }
- // Input callbacks (Send Messages)
- private void OnMove(InputValue value)
- {
- if (!isAlive) return;
- moveInput = value.Get<Vector2>();
- }
- private void OnLook(InputValue value)
- {
- if (!isAlive) return;
- lookInput = value.Get<Vector2>();
- }
- private void OnJump(InputValue value)
- {
- // store press time for buffering
- if (!isAlive) return;
- if (value.isPressed)
- {
- lastJumpPressedTime = Time.time;
- jumpPressed = true;
- }
- else
- {
- jumpPressed = false;
- }
- }
- void HandleMovement()
- {
- if (headTransform == null) return;
- Vector3 forward = Vector3.ProjectOnPlane(headTransform.forward, Vector3.up).normalized;
- Vector3 right = Vector3.ProjectOnPlane(headTransform.right, Vector3.up).normalized;
- float targetSpeed = Input.GetKey(KeyCode.LeftShift) ? sprintSpeed : walkSpeed;
- Vector3 desired = (forward * moveInput.y + right * moveInput.x);
- float inputMag = Mathf.Clamp01(desired.magnitude);
- Vector3 desiredVelocity = (inputMag > 0f) ? desired.normalized * targetSpeed * inputMag : Vector3.zero;
- float currentY = rb.velocity.y;
- Vector3 currentHorizontal = new Vector3(rb.velocity.x, 0f, rb.velocity.z);
- Vector3 newHorizontal = Vector3.MoveTowards(currentHorizontal, desiredVelocity, acceleration * Time.fixedDeltaTime);
- rb.velocity = new Vector3(newHorizontal.x, currentY, newHorizontal.z);
- rb.drag = isGrounded ? groundDrag : airDrag;
- }
- void HandleJump()
- {
- // debug: uncomment if you need logs
- // Debug.Log($"HJ: isGrounded={isGrounded} lastGroundedDiff={Time.time - lastGroundedTime:F3} jumpsRem={jumpsRemaining} lastJumpDiff={Time.time - lastJumpPressedTime:F3}");
- bool canUseCoyote = (Time.time - lastGroundedTime) <= coyoteTime;
- bool hasBufferedJump = (Time.time - lastJumpPressedTime) <= JumpBuferTime;
- // Correct logical condition: allow jump if (on ground OR within coyote time OR have extra jumps) AND player pressed jump recently
- if ((isGrounded || canUseCoyote || jumpsRemaining > 0) && hasBufferedJump)
- {
- // reset vertical speed for stable jump
- Vector3 v = rb.velocity;
- v.y = 0f;
- rb.velocity = v;
- rb.AddForce(Vector3.up * jumpForce, ForceMode.VelocityChange);
- lastJumpPressedTime = -10f;
- jumpsRemaining = Mathf.Max(0, jumpsRemaining - 1);
- // after jumping, don't immediately re-trigger: leave isGrounded as false until ground check updates
- }
- }
- private void OnCollisionEnter(Collision col)
- {
- Rigidbody other = col.rigidbody;
- if (other != null && !other.isKinematic)
- {
- Vector3 contactPoint = (col.contacts != null && col.contacts.Length > 0) ? col.contacts[0].point : col.transform.position;
- Vector3 pushDir = contactPoint - transform.position;
- pushDir.y = 0f;
- if (pushDir.sqrMagnitude < 1e-6f) pushDir = transform.forward;
- pushDir.Normalize();
- Vector3 impulse = pushDir * pushPower;
- other.AddForceAtPosition(impulse, contactPoint, ForceMode.VelocityChange);
- Vector3 torqueAxis = Vector3.Cross(Vector3.up, pushDir);
- if (torqueAxis.sqrMagnitude < 1e-6f) torqueAxis = Vector3.up;
- other.AddTorque(torqueAxis.normalized * torquePower, ForceMode.VelocityChange);
- Vector3 vel = other.velocity;
- Vector3 horiz = new Vector3(vel.x, 0, vel.z);
- float speed = horiz.magnitude;
- if (speed > maxPushSpeed)
- {
- horiz = horiz.normalized * maxPushSpeed;
- other.velocity = new Vector3(horiz.x, vel.y, horiz.z);
- }
- }
- }
- private void OnDrawGizmosSelected()
- {
- if (capsule == null) capsule = GetComponent<CapsuleCollider>();
- if (capsule == null) return;
- Vector3 worldCenter = transform.TransformPoint(capsule.center);
- float halfHeight = Mathf.Max(0f, capsule.height * 0.5f - capsule.radius);
- Vector3 bottom = worldCenter - Vector3.up * halfHeight;
- float gizRadius = capsule.radius * 0.9f;
- float gizDist = groundCheckOffset + 0.02f;
- Gizmos.color = isGrounded ? Color.green : Color.red;
- Gizmos.DrawWireSphere(bottom + Vector3.down * 0.02f, gizRadius);
- Gizmos.color = Color.yellow;
- Gizmos.DrawLine(bottom + Vector3.up * 0.02f, bottom + Vector3.down * gizDist);
- }
- // --- reliable ground check using SphereCast downward from bottom of capsule ---
- void PerformGroundCheck()
- {
- if (capsule == null) capsule = GetComponent<CapsuleCollider>();
- if (capsule == null) { isGrounded = false; return; }
- // compute bottom point of capsule in world space
- Vector3 worldCenter = transform.TransformPoint(capsule.center);
- float halfHeight = Mathf.Max(0f, capsule.height * 0.5f - capsule.radius);
- Vector3 bottom = worldCenter - Vector3.up * halfHeight;
- // prepare spherecast parameters
- float sphereRadius = capsule.radius * 0.9f; // slightly smaller so it doesn't hit the player's own collider
- float castDistance = groundCheckOffset + 0.02f; // short distance downward
- wasGrounded = isGrounded;
- // SphereCast downward to detect ground under feet
- RaycastHit hitInfo;
- bool hit = Physics.SphereCast(bottom + Vector3.up * 0.02f, sphereRadius, Vector3.down, out hitInfo, castDistance, groundMask, QueryTriggerInteraction.Ignore);
- isGrounded = hit;
- // Only treat as "landed" on the frame we go from not grounded -> grounded
- if (!wasGrounded && isGrounded)
- {
- lastGroundedTime = Time.time;
- jumpsRemaining = maxJumps;
- }
- // Optionally: if you want to allow sliding on very steep slopes, you can examine hitInfo.normal here
- // Debug: uncomment if you need diagnostics
- // Debug.Log($"GroundCheck: hit={hit} bottom={bottom} radius={sphereRadius} dist={castDistance} wasGrounded={wasGrounded}");
- }
- void HandleLook()
- {
- if (headTransform == null) return;
- float mx = lookInput.x * mouseSensitivity;
- float my = lookInput.y * mouseSensitivity;
- transform.Rotate(Vector3.up * mx);
- verticalLook -= my;
- verticalLook = Mathf.Clamp(verticalLook, -maxLookAngle, maxLookAngle);
- headTransform.localEulerAngles = new Vector3(verticalLook, 0f, 0f);
- }
- void Update()
- {
- if (!isAlive) return;
- HandleLook();
- PerformGroundCheck();
- // Do NOT reset jumpsRemaining here every frame — it is handled only at the exact frame of landing inside PerformGroundCheck()
- }
- void FixedUpdate()
- {
- if (!isAlive) return;
- HandleMovement();
- HandleJump();
- }
- // Public helper to enable/disable player control
- public void SetAlive(bool Alive)
- {
- isAlive = Alive;
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment