Untitled

 avatar
unknown
csharp
2 days ago
6.1 kB
38
Indexable
using UnityEngine;
using UnityEngine.Splines;
using Unity.Mathematics;
using System.Collections;

public class RailGrind : MonoBehaviour
{
    public float grindSpeed = 5f;
    public float minGrindSpeed = 0.2f;
    public SplineContainer currentSpline;
    public bool isGrinding = false;
    public float splineProgress = 0f;
    public bool isReversing = false;
    [SerializeField] private PlayerPhysics playerPhysics;
    public float grindHeightOffset = 0.2f;

    private static readonly int GrindingHash = Animator.StringToHash("IsGrinding");
    private static readonly int LandOnRailHash = Animator.StringToHash("LandOnRail");

    private Vector3 previousPosition;
    private Vector3 grindVelocity;
    private Vector3 initialModelLocalPosition;
    private Vector3 targetModelPosition;

    private void Start()
    {
        if (playerModel != null)
        {
            initialModelLocalPosition = playerModel.localPosition;
        }
    }

    private void Update()
    {
        if (isGrinding)
        {
            moveAction.enabled = false;

            if (jumpAction != null && jumpAction.currentJumps > 0 && Input.GetButtonDown("Jump"))
            {
                jumpAction.Jump();
                ExitGrind();
                return;
            }
        }
    }

    private void FixedUpdate()
    {
        if (isGrinding)
        {
            if (isReversing)
            {
                splineProgress -= grindSpeed * Time.fixedDeltaTime;
            }
            else
            {
                splineProgress += grindSpeed * Time.fixedDeltaTime;
            }

            splineProgress = Mathf.Clamp01(splineProgress);

            Vector3 railPosition = currentSpline.EvaluatePosition(splineProgress);
            playerPhysics.RB.MovePosition(railPosition);

            Vector3 tangent = math.normalize(currentSpline.EvaluateTangent(splineProgress));
            playerPhysics.RB.MoveRotation(Quaternion.LookRotation(tangent));

            grindVelocity = (railPosition - previousPosition) / Time.fixedDeltaTime;
            previousPosition = railPosition;

            if (splineProgress <= 0f || splineProgress >= 1f)
            {
                ExitGrind();
            }
        }
    }

    private void LateUpdate()
    {
        if (isGrinding && playerModel != null)
        {
            Vector3 railWorldPosition = currentSpline.EvaluatePosition(splineProgress);
            Vector3 targetWorldPosition = railWorldPosition + (Vector3.up * grindHeightOffset);
            playerModel.position = targetWorldPosition;
        }
    }

    private void OnTriggerEnter(Collider other)
    {
        SplineContainer spline = other.GetComponent<SplineContainer>();
        if (spline != null && other is MeshCollider)
        {
            RailProperties railProperties = other.GetComponent<RailProperties>();
            if (railProperties != null)
            {
                grindSpeed = railProperties.grindSpeed;
            }
            else
            {
                grindSpeed = minGrindSpeed;
            }
            
            StartGrind(spline);
        }
    }

    private void StartGrind(SplineContainer spline)
    {
        currentSpline = spline;
        Vector3 playerDirection = playerPhysics.RB.velocity.normalized;
        splineProgress = FindClosestPointOnSpline(currentSpline, transform.position);

        Vector3 splineTangent = math.normalize(currentSpline.EvaluateTangent(splineProgress));
        float dotProduct = Vector3.Dot(playerDirection, splineTangent);

        isReversing = dotProduct < 0;
        isGrinding = true;
        playerPhysics.DisableGroundCheck = true;
        animator.SetBool("IsJumping", false);
        animator.SetBool("IsRising", false);
        animator.SetBool("IsFalling", false);
        if (risingAndFalling != null)
        {
            risingAndFalling.IsAnimationOverridden = true;
        }

        if (bounceAction != null)
        {
            bounceAction.CancelBounce();
        }

        jumpAction.currentJumps = jumpAction.jumps;

        animator.SetTrigger(LandOnRailHash);

        spinBall.SetActive(false);
        spinFX.SetActive(false);

        previousPosition = currentSpline.EvaluatePosition(splineProgress);
        targetModelPosition = initialModelLocalPosition + new Vector3(0, grindHeightOffset, 0);
        StartCoroutine(TransitionToGrindAnimation());
    }

    private IEnumerator TransitionToGrindAnimation()
    {
        yield return new WaitForSeconds(0.35f);
        animator.SetBool(GrindingHash, true);
    }

    private float FindClosestPointOnSpline(SplineContainer spline, Vector3 position)
    {
        float closestProgress = 0f;
        float closestDistance = float.MaxValue;

        for (float t = 0; t <= 1; t += 0.01f)
        {
            Vector3 splinePosition = spline.EvaluatePosition(t);
            float distance = Vector3.Distance(position, splinePosition);

            if (distance < closestDistance)
            {
                closestDistance = distance;
                closestProgress = t;
            }
        }

        return closestProgress;
    }

    public void ExitGrind()
    {
        isGrinding = false;

        Vector3 exitTangent = math.normalize(currentSpline.EvaluateTangent(splineProgress));
        Vector3 exitDirection = isReversing ? -exitTangent : exitTangent;
        playerPhysics.RB.velocity = (exitDirection * grindSpeed) + playerPhysics.verticalVelocity;

        currentSpline = null;
        playerPhysics.DisableGroundCheck = false;
        
        moveAction.enabled = true;
        animator.SetBool(GrindingHash, false);
        animator.ResetTrigger(LandOnRailHash);

        if (risingAndFalling != null)
        {
            risingAndFalling.IsAnimationOverridden = false;
        }

        if (playerModel != null)
        {
            playerModel.localPosition = initialModelLocalPosition;
        }
    }
}
Editor is loading...
Leave a Comment