Hasli4

Contoller

Dec 17th, 2025
57
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 9.25 KB | None | 0 0
  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. using UnityEngine.InputSystem;
  5.  
  6. public class PlayerRigidbodyControler : MonoBehaviour
  7. {
  8.     [Header("References")]
  9.     public Transform headTransform;
  10.     Rigidbody rb;
  11.     CapsuleCollider capsule;
  12.  
  13.     [Header("Movement")]
  14.     public float pushPower = 2f;
  15.     public float maxPushSpeed = 10f;
  16.     public float torquePower = 1f;
  17.     public float walkSpeed = 5f;
  18.     public float sprintSpeed = 8f;
  19.     public float acceleration = 30f;
  20.     public float groundDrag = 5f;
  21.     public float airDrag = 0.1f;
  22.  
  23.     [Header("Jump")]
  24.     public float jumpForce = 7f;
  25.     public int maxJumps = 1;                 // set 2 for double jump
  26.     public float coyoteTime = 0.15f;
  27.     public float JumpBuferTime = 0.12f;
  28.  
  29.     [Header("Ground Check")]
  30.     public LayerMask groundMask;
  31.     public float groundCheckOffset = 0.08f;  // distance to cast downwards
  32.     // NOTE: we do not use groundCheckRadius anymore for checks, only gizmo radius
  33.  
  34.     [Header("Look")]
  35.     public float mouseSensitivity = 1.2f;
  36.     public float maxLookAngle = 85f;
  37.  
  38.     Vector2 moveInput = Vector2.zero;
  39.     Vector2 lookInput = Vector2.zero;
  40.  
  41.     bool jumpPressed = false;
  42.     bool isGrounded = false;
  43.     bool wasGrounded = false; // previous frame
  44.  
  45.     float lastGroundedTime = -10f;
  46.     float lastJumpPressedTime = -10f;
  47.  
  48.     int jumpsRemaining;
  49.     float verticalLook = 0f;
  50.     bool isAlive = true;
  51.  
  52.     void Awake()
  53.     {
  54.         rb = GetComponent<Rigidbody>();
  55.         capsule = GetComponent<CapsuleCollider>();
  56.  
  57.         rb.interpolation = RigidbodyInterpolation.Interpolate;
  58.         rb.collisionDetectionMode = CollisionDetectionMode.Continuous;
  59.         rb.constraints = RigidbodyConstraints.FreezeRotationX | RigidbodyConstraints.FreezeRotationZ;
  60.  
  61.         jumpsRemaining = maxJumps;
  62.     }
  63.  
  64.     void Start()
  65.     {
  66.         if (headTransform == null) Debug.LogWarning("Assign headTransform in PlayerRigidbodyControler.");
  67.         Cursor.lockState = CursorLockMode.Locked;
  68.         Cursor.visible = false;
  69.     }
  70.  
  71.     // Input callbacks (Send Messages)
  72.     private void OnMove(InputValue value)
  73.     {
  74.         if (!isAlive) return;
  75.         moveInput = value.Get<Vector2>();
  76.     }
  77.  
  78.     private void OnLook(InputValue value)
  79.     {
  80.         if (!isAlive) return;
  81.         lookInput = value.Get<Vector2>();
  82.     }
  83.  
  84.     private void OnJump(InputValue value)
  85.     {
  86.         // store press time for buffering
  87.         if (!isAlive) return;
  88.         if (value.isPressed)
  89.         {
  90.             lastJumpPressedTime = Time.time;
  91.             jumpPressed = true;
  92.         }
  93.         else
  94.         {
  95.             jumpPressed = false;
  96.         }
  97.     }
  98.  
  99.     void HandleMovement()
  100.     {
  101.         if (headTransform == null) return;
  102.  
  103.         Vector3 forward = Vector3.ProjectOnPlane(headTransform.forward, Vector3.up).normalized;
  104.         Vector3 right = Vector3.ProjectOnPlane(headTransform.right, Vector3.up).normalized;
  105.         float targetSpeed = Input.GetKey(KeyCode.LeftShift) ? sprintSpeed : walkSpeed;
  106.         Vector3 desired = (forward * moveInput.y + right * moveInput.x);
  107.         float inputMag = Mathf.Clamp01(desired.magnitude);
  108.  
  109.         Vector3 desiredVelocity = (inputMag > 0f) ? desired.normalized * targetSpeed * inputMag : Vector3.zero;
  110.         float currentY = rb.velocity.y;
  111.         Vector3 currentHorizontal = new Vector3(rb.velocity.x, 0f, rb.velocity.z);
  112.         Vector3 newHorizontal = Vector3.MoveTowards(currentHorizontal, desiredVelocity, acceleration * Time.fixedDeltaTime);
  113.         rb.velocity = new Vector3(newHorizontal.x, currentY, newHorizontal.z);
  114.         rb.drag = isGrounded ? groundDrag : airDrag;
  115.     }
  116.  
  117.     void HandleJump()
  118.     {
  119.         // debug: uncomment if you need logs
  120.         // Debug.Log($"HJ: isGrounded={isGrounded} lastGroundedDiff={Time.time - lastGroundedTime:F3} jumpsRem={jumpsRemaining} lastJumpDiff={Time.time - lastJumpPressedTime:F3}");
  121.  
  122.         bool canUseCoyote = (Time.time - lastGroundedTime) <= coyoteTime;
  123.         bool hasBufferedJump = (Time.time - lastJumpPressedTime) <= JumpBuferTime;
  124.  
  125.         // Correct logical condition: allow jump if (on ground OR within coyote time OR have extra jumps) AND player pressed jump recently
  126.         if ((isGrounded || canUseCoyote || jumpsRemaining > 0) && hasBufferedJump)
  127.         {
  128.             // reset vertical speed for stable jump
  129.             Vector3 v = rb.velocity;
  130.             v.y = 0f;
  131.             rb.velocity = v;
  132.             rb.AddForce(Vector3.up * jumpForce, ForceMode.VelocityChange);
  133.  
  134.             lastJumpPressedTime = -10f;
  135.             jumpsRemaining = Mathf.Max(0, jumpsRemaining - 1);
  136.             // after jumping, don't immediately re-trigger: leave isGrounded as false until ground check updates
  137.         }
  138.     }
  139.  
  140.     private void OnCollisionEnter(Collision col)
  141.     {
  142.         Rigidbody other = col.rigidbody;
  143.         if (other != null && !other.isKinematic)
  144.         {
  145.             Vector3 contactPoint = (col.contacts != null && col.contacts.Length > 0) ? col.contacts[0].point : col.transform.position;
  146.             Vector3 pushDir = contactPoint - transform.position;
  147.             pushDir.y = 0f;
  148.             if (pushDir.sqrMagnitude < 1e-6f) pushDir = transform.forward;
  149.             pushDir.Normalize();
  150.             Vector3 impulse = pushDir * pushPower;
  151.             other.AddForceAtPosition(impulse, contactPoint, ForceMode.VelocityChange);
  152.             Vector3 torqueAxis = Vector3.Cross(Vector3.up, pushDir);
  153.             if (torqueAxis.sqrMagnitude < 1e-6f) torqueAxis = Vector3.up;
  154.             other.AddTorque(torqueAxis.normalized * torquePower, ForceMode.VelocityChange);
  155.             Vector3 vel = other.velocity;
  156.             Vector3 horiz = new Vector3(vel.x, 0, vel.z);
  157.             float speed = horiz.magnitude;
  158.             if (speed > maxPushSpeed)
  159.             {
  160.                 horiz = horiz.normalized * maxPushSpeed;
  161.                 other.velocity = new Vector3(horiz.x, vel.y, horiz.z);
  162.             }
  163.         }
  164.     }
  165.  
  166.     private void OnDrawGizmosSelected()
  167.     {
  168.         if (capsule == null) capsule = GetComponent<CapsuleCollider>();
  169.         if (capsule == null) return;
  170.  
  171.         Vector3 worldCenter = transform.TransformPoint(capsule.center);
  172.         float halfHeight = Mathf.Max(0f, capsule.height * 0.5f - capsule.radius);
  173.         Vector3 bottom = worldCenter - Vector3.up * halfHeight;
  174.  
  175.         float gizRadius = capsule.radius * 0.9f;
  176.         float gizDist = groundCheckOffset + 0.02f;
  177.         Gizmos.color = isGrounded ? Color.green : Color.red;
  178.         Gizmos.DrawWireSphere(bottom + Vector3.down * 0.02f, gizRadius);
  179.         Gizmos.color = Color.yellow;
  180.         Gizmos.DrawLine(bottom + Vector3.up * 0.02f, bottom + Vector3.down * gizDist);
  181.     }
  182.  
  183.     // --- reliable ground check using SphereCast downward from bottom of capsule ---
  184.     void PerformGroundCheck()
  185.     {
  186.         if (capsule == null) capsule = GetComponent<CapsuleCollider>();
  187.         if (capsule == null) { isGrounded = false; return; }
  188.  
  189.         // compute bottom point of capsule in world space
  190.         Vector3 worldCenter = transform.TransformPoint(capsule.center);
  191.         float halfHeight = Mathf.Max(0f, capsule.height * 0.5f - capsule.radius);
  192.         Vector3 bottom = worldCenter - Vector3.up * halfHeight;
  193.  
  194.         // prepare spherecast parameters
  195.         float sphereRadius = capsule.radius * 0.9f; // slightly smaller so it doesn't hit the player's own collider
  196.         float castDistance = groundCheckOffset + 0.02f; // short distance downward
  197.  
  198.         wasGrounded = isGrounded;
  199.  
  200.         // SphereCast downward to detect ground under feet
  201.         RaycastHit hitInfo;
  202.         bool hit = Physics.SphereCast(bottom + Vector3.up * 0.02f, sphereRadius, Vector3.down, out hitInfo, castDistance, groundMask, QueryTriggerInteraction.Ignore);
  203.         isGrounded = hit;
  204.  
  205.         // Only treat as "landed" on the frame we go from not grounded -> grounded
  206.         if (!wasGrounded && isGrounded)
  207.         {
  208.             lastGroundedTime = Time.time;
  209.             jumpsRemaining = maxJumps;
  210.         }
  211.  
  212.         // Optionally: if you want to allow sliding on very steep slopes, you can examine hitInfo.normal here
  213.         // Debug: uncomment if you need diagnostics
  214.         // Debug.Log($"GroundCheck: hit={hit} bottom={bottom} radius={sphereRadius} dist={castDistance} wasGrounded={wasGrounded}");
  215.     }
  216.  
  217.     void HandleLook()
  218.     {
  219.         if (headTransform == null) return;
  220.  
  221.         float mx = lookInput.x * mouseSensitivity;
  222.         float my = lookInput.y * mouseSensitivity;
  223.         transform.Rotate(Vector3.up * mx);
  224.         verticalLook -= my;
  225.         verticalLook = Mathf.Clamp(verticalLook, -maxLookAngle, maxLookAngle);
  226.         headTransform.localEulerAngles = new Vector3(verticalLook, 0f, 0f);
  227.     }
  228.  
  229.     void Update()
  230.     {
  231.         if (!isAlive) return;
  232.         HandleLook();
  233.         PerformGroundCheck();
  234.         // Do NOT reset jumpsRemaining here every frame — it is handled only at the exact frame of landing inside PerformGroundCheck()
  235.     }
  236.  
  237.     void FixedUpdate()
  238.     {
  239.         if (!isAlive) return;
  240.         HandleMovement();
  241.         HandleJump();
  242.     }
  243.  
  244.     // Public helper to enable/disable player control
  245.     public void SetAlive(bool Alive)
  246.     {
  247.         isAlive = Alive;
  248.     }
  249. }
  250.  
Advertisement
Add Comment
Please, Sign In to add comment