using System; using Roads; using UnityEngine; namespace Wheels { public class LerpSlopeCollider : MonoBehaviour { public delegate void OnSlopeChangedEvent(float timestamp, float slope); private const float TOLERANCE = 0.005f; private const float MAX_RAYCAST_LENGTH = 2f; public int collisionLayer = 1; public Transform rearWheelContact; public Transform frontWheelContact; public Rigidbody bike; private Transform bikeTransform; private float currentFrontWheelMinY; private float currentFrontWheelSlope; private float distanceUntilCompletelyRotated; private bool rotate; public int SlopeDirection { private set; get; } = 1; private void Start() { bikeTransform = bike.transform; distanceUntilCompletelyRotated = Vector3.Distance(frontWheelContact.position, rearWheelContact.position); } private void FixedUpdate() { var fwHit = CastRay(frontWheelContact.position, Vector3.down); if (fwHit != null) { if (Mathf.Abs(currentFrontWheelSlope - fwHit.SlopeDeg) > TOLERANCE) { rotate = true; //begin rotation currentFrontWheelSlope = fwHit.SlopeDeg; OnSlopeChanged?.Invoke(Time.fixedTime, currentFrontWheelSlope); } var minYDif = currentFrontWheelMinY - fwHit.MinY; if (Mathf.Abs(minYDif) > TOLERANCE) { SlopeDirection = -Math.Sign(minYDif); currentFrontWheelMinY = fwHit.MinY; } } RotateIfNeeded(); } private void OnGUI() { GUI.TextField(new Rect(10, 200, 200, 20), $"Direction = {SlopeDirection}"); GUI.TextField(new Rect(10, 220, 200, 20), $"MinY = {currentFrontWheelMinY}"); GUI.TextField(new Rect(10, 250, 200, 20), $"rotation.x = {bikeTransform.localRotation.eulerAngles.x:n2}"); GUI.TextField(new Rect(10, 270, 200, 20), $"Slope = {currentFrontWheelSlope}"); } public event OnSlopeChangedEvent OnSlopeChanged; private IRoad CastRay(Vector3 pos, Vector3 direction) { var layerMask = 1 << collisionLayer; if (Physics.Raycast(pos, direction, out var hit, MAX_RAYCAST_LENGTH, layerMask)) { Debug.DrawRay(pos, direction * hit.distance, Color.green); var roadHit = hit.collider.gameObject.GetComponentInParent(); return roadHit; } Debug.DrawRay(pos, direction * MAX_RAYCAST_LENGTH, Color.white); return null; } private void RotateIfNeeded() { if (rotate) { var bikeAngle = bikeTransform.localRotation.eulerAngles.x; if (bikeAngle < -180) bikeAngle = Mathf.Abs(360 + bikeAngle); else if (bikeAngle > 180) bikeAngle = Mathf.Abs(-360 + bikeAngle); var slopeDif = SlopeDirection * (currentFrontWheelSlope - bikeAngle); if (Mathf.Abs(currentFrontWheelSlope - bikeAngle) < TOLERANCE) { rotate = false; return; } var forwardBikeSpeed = bikeTransform.InverseTransformDirection(bike.velocity).z; var rot = slopeDif * Time.fixedDeltaTime * forwardBikeSpeed / distanceUntilCompletelyRotated; if (slopeDif > 0) //lift front wheel bikeTransform.RotateAround(frontWheelContact.position, -frontWheelContact.right, rot); else //dip front wheel bikeTransform.RotateAround(frontWheelContact.position, frontWheelContact.right, -rot); } } } }