From 6d53bf8018f3835fa07e377647136b48897a28d4 Mon Sep 17 00:00:00 2001 From: John Wigner Date: Mon, 27 Jan 2025 23:17:46 -0500 Subject: [PATCH] greatly simplify movement logic --- Assets/Scenes/SampleScene.unity | 206 ++++++++++--- Assets/Scripts/ArcadeKart.cs | 506 +++----------------------------- 2 files changed, 217 insertions(+), 495 deletions(-) diff --git a/Assets/Scenes/SampleScene.unity b/Assets/Scenes/SampleScene.unity index f2cdda5..e6d02c3 100644 --- a/Assets/Scenes/SampleScene.unity +++ b/Assets/Scenes/SampleScene.unity @@ -131,6 +131,7 @@ GameObject: - component: {fileID: 330585545} - component: {fileID: 330585544} - component: {fileID: 330585547} + - component: {fileID: 330585548} m_Layer: 0 m_Name: Main Camera m_TagString: MainCamera @@ -179,7 +180,7 @@ Camera: height: 1 near clip plane: 0.3 far clip plane: 1000 - field of view: 60 + field of view: 60.000004 orthographic: 0 orthographic size: 5 m_Depth: -1 @@ -205,8 +206,8 @@ Transform: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 330585543} serializedVersion: 2 - m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} - m_LocalPosition: {x: 0, y: 1, z: -10} + m_LocalRotation: {x: 0.18910757, y: -0.000000014030909, z: 0.0000000027021065, w: 0.9819564} + m_LocalPosition: {x: 2.631, y: 3.709, z: -3.085} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] @@ -256,6 +257,38 @@ MonoBehaviour: m_MipBias: 0 m_VarianceClampScale: 0.9 m_ContrastAdaptiveSharpening: 0 +--- !u!114 &330585548 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 330585543} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 72ece51f2901e7445ab60da3685d6b5f, type: 3} + m_Name: + m_EditorClassIdentifier: + ShowDebugText: 0 + ShowCameraFrustum: 1 + IgnoreTimeScale: 0 + WorldUpOverride: {fileID: 0} + ChannelMask: -1 + UpdateMethod: 2 + BlendUpdateMethod: 1 + LensModeOverride: + Enabled: 0 + DefaultMode: 2 + DefaultBlend: + Style: 1 + Time: 2 + CustomCurve: + serializedVersion: 2 + m_Curve: [] + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + CustomBlends: {fileID: 0} --- !u!1 &343153336 GameObject: m_ObjectHideFlags: 0 @@ -517,35 +550,16 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 438e8be3307824743b12d4e4f02825ea, type: 3} m_Name: m_EditorClassIdentifier: - baseStats: - TopSpeed: 10 - Acceleration: 5 - ReverseSpeed: 5 - ReverseAcceleration: 5 - AccelerationCurve: 1 - Braking: 10 - CoastingDrag: 4 - Grip: 0.95 - floatHeight: 3 - minimumFloatHeight: 0.5 - Steer: 5 - AddedGravity: 1 - m_VisualWheels: [] - CenterOfMass: {fileID: 439250409} - AirborneReorientationCoefficient: 3 - DriftGrip: 0.4 - DriftAdditionalSteer: 5 - MinAngleToFinishDrift: 10 - MinSpeedPercentToFinishDrift: 0.5 - DriftControl: 10 - DriftDampening: 10 - SuspensionHeight: 0.2 - SuspensionSpring: 20000 - SuspensionDamp: 500 - WheelsPositionVerticalOffset: 0 - GroundLayers: + hoverHeight: 2.5 + hoverForce: 100 + groundLayer: serializedVersion: 2 - m_Bits: 51 + m_Bits: 4294967295 + hoverDampening: 5 + forwardSpeed: 30 + turnSpeed: 3 + strafeSpeed: 15 + drag: 0.5 --- !u!4 &439250409 Transform: m_ObjectHideFlags: 0 @@ -572,7 +586,7 @@ Rigidbody: serializedVersion: 4 m_Mass: 1 m_Drag: 0 - m_AngularDrag: 0.05 + m_AngularDrag: 0.5 m_CenterOfMass: {x: 0, y: 0, z: 0} m_InertiaTensor: {x: 1, y: 1, z: 1} m_InertiaRotation: {x: 0, y: 0, z: 0, w: 1} @@ -587,7 +601,7 @@ Rigidbody: m_UseGravity: 1 m_IsKinematic: 0 m_Interpolate: 0 - m_Constraints: 0 + m_Constraints: 80 m_CollisionDetection: 0 --- !u!65 &439250411 BoxCollider: @@ -2208,6 +2222,131 @@ Transform: m_Children: [] m_Father: {fileID: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &2109556991 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2109556995} + - component: {fileID: 2109556994} + - component: {fileID: 2109556993} + - component: {fileID: 2109556992} + m_Layer: 0 + m_Name: CinemachineCamera + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &2109556992 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2109556991} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: f38bda98361e1de48a4ca2bd86ea3c17, type: 3} + m_Name: + m_EditorClassIdentifier: + Composition: + ScreenPosition: {x: 0, y: 0} + DeadZone: + Enabled: 0 + Size: {x: 0.2, y: 0.2} + HardLimits: + Enabled: 0 + Size: {x: 0.8, y: 0.8} + Offset: {x: 0, y: 0} + CenterOnActivate: 1 + TargetOffset: {x: 0, y: 0, z: 0} + Damping: {x: 0.5, y: 0.5} + Lookahead: + Enabled: 0 + Time: 0 + Smoothing: 0 + IgnoreY: 0 +--- !u!114 &2109556993 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2109556991} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: b617507da6d07e749b7efdb34e1173e1, type: 3} + m_Name: + m_EditorClassIdentifier: + TrackerSettings: + BindingMode: 3 + PositionDamping: {x: 1, y: 1, z: 1} + AngularDampingMode: 0 + RotationDamping: {x: 1, y: 1, z: 1} + QuaternionDamping: 1 + FollowOffset: {x: 0, y: 2, z: -5} +--- !u!114 &2109556994 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2109556991} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: f9dfa5b682dcd46bda6128250e975f58, type: 3} + m_Name: + m_EditorClassIdentifier: + Priority: + Enabled: 0 + m_Value: 0 + OutputChannel: 1 + StandbyUpdate: 2 + m_StreamingVersion: 20241001 + m_LegacyPriority: 0 + Target: + TrackingTarget: {fileID: 439250409} + LookAtTarget: {fileID: 0} + CustomLookAtTarget: 0 + Lens: + FieldOfView: 60.000004 + OrthographicSize: 5 + NearClipPlane: 0.3 + FarClipPlane: 1000 + Dutch: 0 + ModeOverride: 0 + PhysicalProperties: + GateFit: 2 + SensorSize: {x: 21.946, y: 16.002} + LensShift: {x: 0, y: 0} + FocusDistance: 10 + Iso: 200 + ShutterSpeed: 0.005 + Aperture: 16 + BladeCount: 5 + Curvature: {x: 2, y: 11} + BarrelClipping: 0.25 + Anamorphism: 0 + BlendHint: 0 +--- !u!4 &2109556995 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2109556991} + serializedVersion: 2 + m_LocalRotation: {x: 0.18910754, y: -0.000000014030908, z: 0.000000002702106, w: 0.9819564} + m_LocalPosition: {x: 2.631, y: 3.709, z: -3.085} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1660057539 &9223372036854775807 SceneRoots: m_ObjectHideFlags: 0 @@ -2219,3 +2358,4 @@ SceneRoots: - {fileID: 1095225674} - {fileID: 439250409} - {fileID: 1273423010} + - {fileID: 2109556995} diff --git a/Assets/Scripts/ArcadeKart.cs b/Assets/Scripts/ArcadeKart.cs index a9281dd..4ce37c7 100644 --- a/Assets/Scripts/ArcadeKart.cs +++ b/Assets/Scripts/ArcadeKart.cs @@ -7,498 +7,80 @@ namespace SRL { public class ArcadeKart : MonoBehaviour { - [System.Serializable] - public class StatPowerup - { - public ArcadeKart.Stats modifiers; - public string PowerUpID; - public float ElapsedTime; - public float MaxTime; - } - [System.Serializable] - public struct Stats - { - [Header("Movement Settings")] - [Min(0.001f), Tooltip("Top speed attainable when moving forward.")] - public float TopSpeed; - - [Tooltip("How quickly the kart reaches top speed.")] - public float Acceleration; - - [Min(0.001f), Tooltip("Top speed attainable when moving backward.")] - public float ReverseSpeed; - - [Tooltip("How quickly the kart reaches top speed, when moving backward.")] - public float ReverseAcceleration; - - [Tooltip("How quickly the kart starts accelerating from 0. A higher number means it accelerates faster sooner.")] - [Range(0.2f, 1)] - public float AccelerationCurve; - - [Tooltip("How quickly the kart slows down when the brake is applied.")] - public float Braking; - - [Tooltip("How quickly the kart will reach a full stop when no inputs are made.")] - public float CoastingDrag; - - [Range(0.0f, 1.0f)] - [Tooltip("The amount of side-to-side friction.")] - public float Grip; - [Tooltip("The target height to float above the ground.")] - public float floatHeight; - [Tooltip("The minimum height to float above the ground.")] - public float minimumFloatHeight; - - [Tooltip("How tightly the kart can turn left or right.")] - public float Steer; - - [Tooltip("Additional gravity for when the kart is in the air.")] - public float AddedGravity; - - // allow for stat adding for powerups. - public static Stats operator +(Stats a, Stats b) - { - return new Stats - { - Acceleration = a.Acceleration + b.Acceleration, - AccelerationCurve = a.AccelerationCurve + b.AccelerationCurve, - Braking = a.Braking + b.Braking, - CoastingDrag = a.CoastingDrag + b.CoastingDrag, - AddedGravity = a.AddedGravity + b.AddedGravity, - Grip = a.Grip + b.Grip, - floatHeight = a.floatHeight + b.floatHeight, - minimumFloatHeight = a.minimumFloatHeight + b.minimumFloatHeight, - ReverseAcceleration = a.ReverseAcceleration + b.ReverseAcceleration, - ReverseSpeed = a.ReverseSpeed + b.ReverseSpeed, - TopSpeed = a.TopSpeed + b.TopSpeed, - Steer = a.Steer + b.Steer, - }; - } - } - - public Rigidbody Rigidbody { get; private set; } public InputData Input { get; private set; } - public float AirPercent { get; private set; } - public float GroundPercent { get; private set; } - - public ArcadeKart.Stats baseStats = new ArcadeKart.Stats - { - TopSpeed = 10f, - Acceleration = 5f, - AccelerationCurve = 4f, - Braking = 10f, - ReverseAcceleration = 5f, - ReverseSpeed = 5f, - Steer = 5f, - CoastingDrag = 4f, - Grip = .95f, - floatHeight = 1f, - minimumFloatHeight = .5f, - AddedGravity = 1f, - }; - - [Header("Vehicle Visual")] - public List m_VisualWheels; - - [Header("Vehicle Physics")] - [Tooltip("The transform that determines the position of the kart's mass.")] - public Transform CenterOfMass; - - [Range(0.0f, 20.0f), Tooltip("Coefficient used to reorient the kart in the air. The higher the number, the faster the kart will readjust itself along the horizontal plane.")] - public float AirborneReorientationCoefficient = 3.0f; - - [Header("Drifting")] - [Range(0.01f, 1.0f), Tooltip("The grip value when drifting.")] - public float DriftGrip = 0.4f; - [Range(0.0f, 10.0f), Tooltip("Additional steer when the kart is drifting.")] - public float DriftAdditionalSteer = 5.0f; - [Range(1.0f, 30.0f), Tooltip("The higher the angle, the easier it is to regain full grip.")] - public float MinAngleToFinishDrift = 10.0f; - [Range(0.01f, 0.99f), Tooltip("Mininum speed percentage to switch back to full grip.")] - public float MinSpeedPercentToFinishDrift = 0.5f; - [Range(1.0f, 20.0f), Tooltip("The higher the value, the easier it is to control the drift steering.")] - public float DriftControl = 10.0f; - [Range(0.0f, 20.0f), Tooltip("The lower the value, the longer the drift will last without trying to control it by steering.")] - public float DriftDampening = 10.0f; - - [Header("Suspensions")] - [Tooltip("The maximum extension possible between the kart's body and the wheels.")] - [Range(0.0f, 1.0f)] - public float SuspensionHeight = 0.2f; - [Range(10.0f, 100000.0f), Tooltip("The higher the value, the stiffer the suspension will be.")] - public float SuspensionSpring = 20000.0f; - [Range(0.0f, 5000.0f), Tooltip("The higher the value, the faster the kart will stabilize itself.")] - public float SuspensionDamp = 500.0f; - [Tooltip("Vertical offset to adjust the position of the wheels relative to the kart's body.")] - [Range(-1.0f, 1.0f)] - public float WheelsPositionVerticalOffset = 0.0f; - - [Tooltip("Which layers the wheels will detect.")] - public LayerMask GroundLayers = Physics.DefaultRaycastLayers; - // the input sources that can control the kart IInput[] m_Inputs; - const float k_NullInput = 0.01f; - const float k_NullSpeed = 0.01f; - Vector3 m_VerticalReference = Vector3.up; - - // Drift params - public bool WantsToDrift { get; private set; } = false; - public bool IsDrifting { get; private set; } = false; - float m_CurrentGrip = 1.0f; - float m_DriftTurningPower = 0.0f; - float m_PreviousGroundPercent = 1.0f; - readonly List<(GameObject trailRoot, WheelCollider wheel, TrailRenderer trail)> m_DriftTrailInstances = new List<(GameObject, WheelCollider, TrailRenderer)>(); - readonly List<(WheelCollider wheel, float horizontalOffset, float rotation, ParticleSystem sparks)> m_DriftSparkInstances = new List<(WheelCollider, float, float, ParticleSystem)>(); - - // can the kart move? - bool m_CanMove = true; - List m_ActivePowerupList = new List(); - ArcadeKart.Stats m_FinalStats; - - Quaternion m_LastValidRotation; - Vector3 m_LastValidPosition; - Vector3 m_LastCollisionNormal; - bool m_HasCollision; - bool m_InAir = false; - - public void AddPowerup(StatPowerup statPowerup) => m_ActivePowerupList.Add(statPowerup); - public void SetCanMove(bool move) => m_CanMove = move; - public float GetMaxSpeed() => Mathf.Max(m_FinalStats.TopSpeed, m_FinalStats.ReverseSpeed); - - void UpdateSuspensionParams(WheelCollider wheel) - { - wheel.suspensionDistance = SuspensionHeight; - wheel.center = new Vector3(0.0f, WheelsPositionVerticalOffset, 0.0f); - JointSpring spring = wheel.suspensionSpring; - spring.spring = SuspensionSpring; - spring.damper = SuspensionDamp; - wheel.suspensionSpring = spring; - } - - void Awake() - { - Rigidbody = GetComponent(); - m_Inputs = GetComponents(); - - m_CurrentGrip = baseStats.Grip; - } - - void FixedUpdate() - { - - GatherInputs(); - // apply our powerups to create our finalStats - TickPowerups(); - - // apply our physics properties - Rigidbody.centerOfMass = transform.InverseTransformPoint(CenterOfMass.position); - - int groundedCount = 4; - // if (FrontLeftWheel.isGrounded && FrontLeftWheel.GetGroundHit(out WheelHit hit)) - // groundedCount++; - // if (FrontRightWheel.isGrounded && FrontRightWheel.GetGroundHit(out hit)) - // groundedCount++; - // if (RearLeftWheel.isGrounded && RearLeftWheel.GetGroundHit(out hit)) - // groundedCount++; - // if (RearRightWheel.isGrounded && RearRightWheel.GetGroundHit(out hit)) - // groundedCount++; - - // calculate how grounded and airborne we are - GroundPercent = (float) groundedCount / 4.0f; - AirPercent = 1 - GroundPercent; - - // apply vehicle physics - if (m_CanMove) - { - MoveVehicle(Input.Accelerate, Input.Brake, Input.TurnInput); - } - GroundAirbourne(); - - m_PreviousGroundPercent = GroundPercent; - } - void GatherInputs() { // reset input Input = new InputData(); - WantsToDrift = false; // gather nonzero input from our sources for (int i = 0; i < m_Inputs.Length; i++) { Input = m_Inputs[i].GenerateInput(); - WantsToDrift = Input.Brake && Vector3.Dot(Rigidbody.linearVelocity, transform.forward) > 0.0f; } } - void TickPowerups() + + + [Header("Hover Settings")] + public float hoverHeight = 2.0f; + public float hoverForce = 100f; + public LayerMask groundLayer; + public float hoverDampening = 5f; + + [Header("Movement Settings")] + public float forwardSpeed = 50f; + public float turnSpeed = 20f; + public float strafeSpeed = 15f; + public float drag = 2f; + + + private Rigidbody rb; + + void Awake() { - // remove all elapsed powerups - m_ActivePowerupList.RemoveAll((p) => { return p.ElapsedTime > p.MaxTime; }); - - // zero out powerups before we add them all up - var powerups = new Stats(); - - // add up all our powerups - for (int i = 0; i < m_ActivePowerupList.Count; i++) - { - var p = m_ActivePowerupList[i]; - - // add elapsed time - p.ElapsedTime += Time.fixedDeltaTime; - - // add up the powerups - powerups += p.modifiers; - } - - // add powerups to our final stats - m_FinalStats = baseStats + powerups; - - // clamp values in finalstats - m_FinalStats.Grip = Mathf.Clamp(m_FinalStats.Grip, 0, 1); + rb = GetComponent(); + m_Inputs = GetComponents(); } - void GroundAirbourne() + void FixedUpdate() { - RaycastHit hit; - - // Check if the object is hitting the ground using a raycast - - if (Physics.Raycast(transform.position, Vector3.down, out hit, baseStats.floatHeight + 0.1f)) + GatherInputs(); + Hover(); + Move(); + } + void Hover() + { + // Raycast to ground to simulate hover + Ray ray = new Ray(transform.position, -transform.up); + if (Physics.Raycast(ray, out RaycastHit hit, hoverHeight * 2, groundLayer)) { + float hoverError = hoverHeight - hit.distance; + float upwardSpeed = rb.linearVelocity.y; + float appliedHoverForce = hoverError * hoverForce - upwardSpeed * hoverDampening; - // Calculate the upward force needed to maintain the desired height - - float upwardForce = (baseStats.floatHeight - (hit.distance - baseStats.minimumFloatHeight)) * 10f; - - Rigidbody.AddForce(Vector3.up * upwardForce, ForceMode.Acceleration); - + rb.AddForce(Vector3.up * appliedHoverForce, ForceMode.Acceleration); } } - public void Reset() + void Move() { - Vector3 euler = transform.rotation.eulerAngles; - euler.x = euler.z = 0f; - transform.rotation = Quaternion.Euler(euler); - } + // Forward movement + float forwardInput = Input.Accelerate ? 1f : 0f; + Vector3 forwardForce = transform.forward * forwardInput * forwardSpeed; + rb.AddForce(forwardForce, ForceMode.Acceleration); - public float LocalSpeed() - { - if (m_CanMove) - { - float dot = Vector3.Dot(transform.forward, Rigidbody.linearVelocity); - if (Mathf.Abs(dot) > 0.1f) - { - float speed = Rigidbody.linearVelocity.magnitude; - return dot < 0 ? -(speed / m_FinalStats.ReverseSpeed) : (speed / m_FinalStats.TopSpeed); - } - return 0f; - } - else - { - // use this value to play kart sound when it is waiting the race start countdown. - return Input.Accelerate ? 1.0f : 0.0f; - } - } + // Turning (rotate around Y-axis) + float turnInput = Input.TurnInput; + rb.AddTorque(Vector3.up * turnInput * turnSpeed, ForceMode.Acceleration); - void OnCollisionEnter(Collision collision) => m_HasCollision = true; - void OnCollisionExit(Collision collision) => m_HasCollision = false; - - void OnCollisionStay(Collision collision) - { - m_HasCollision = true; - m_LastCollisionNormal = Vector3.zero; - float dot = -1.0f; - - foreach (var contact in collision.contacts) - { - if (Vector3.Dot(contact.normal, Vector3.up) > dot) - m_LastCollisionNormal = contact.normal; - } - } - - void MoveVehicle(bool accelerate, bool brake, float turnInput) - { - float accelInput = (accelerate ? 1.0f : 0.0f) - (brake ? 1.0f : 0.0f); - - // manual acceleration curve coefficient scalar - float accelerationCurveCoeff = 5; - Vector3 localVel = transform.InverseTransformVector(Rigidbody.linearVelocity); - - bool accelDirectionIsFwd = accelInput >= 0; - bool localVelDirectionIsFwd = localVel.z >= 0; - - // use the max speed for the direction we are going--forward or reverse. - float maxSpeed = localVelDirectionIsFwd ? m_FinalStats.TopSpeed : m_FinalStats.ReverseSpeed; - float accelPower = accelDirectionIsFwd ? m_FinalStats.Acceleration : m_FinalStats.ReverseAcceleration; - - float currentSpeed = Rigidbody.linearVelocity.magnitude; - float accelRampT = currentSpeed / maxSpeed; - float multipliedAccelerationCurve = m_FinalStats.AccelerationCurve * accelerationCurveCoeff; - float accelRamp = Mathf.Lerp(multipliedAccelerationCurve, 1, accelRampT * accelRampT); - - bool isBraking = (localVelDirectionIsFwd && brake) || (!localVelDirectionIsFwd && accelerate); - - // if we are braking (moving reverse to where we are going) - // use the braking accleration instead - float finalAccelPower = isBraking ? m_FinalStats.Braking : accelPower; - - float finalAcceleration = finalAccelPower * accelRamp; - - // apply inputs to forward/backward - float turningPower = IsDrifting ? m_DriftTurningPower : turnInput * m_FinalStats.Steer; - - Quaternion turnAngle = Quaternion.AngleAxis(turningPower, transform.up); - Vector3 fwd = turnAngle * transform.forward; - Vector3 movement = fwd * accelInput * finalAcceleration * ((m_HasCollision || GroundPercent > 0.0f) ? 1.0f : 0.0f); - - // forward movement - bool wasOverMaxSpeed = currentSpeed >= maxSpeed; - - // if over max speed, cannot accelerate faster. - if (wasOverMaxSpeed && !isBraking) - movement *= 0.0f; - - Vector3 newVelocity = Rigidbody.linearVelocity + movement * Time.fixedDeltaTime; - newVelocity.y = Rigidbody.linearVelocity.y; - - // clamp max speed if we are on ground - if (GroundPercent > 0.0f && !wasOverMaxSpeed) - { - newVelocity = Vector3.ClampMagnitude(newVelocity, maxSpeed); - } - - // coasting is when we aren't touching accelerate - if (Mathf.Abs(accelInput) < k_NullInput && GroundPercent > 0.0f) - { - newVelocity = Vector3.MoveTowards(newVelocity, new Vector3(0, Rigidbody.linearVelocity.y, 0), Time.fixedDeltaTime * m_FinalStats.CoastingDrag); - } - - Rigidbody.linearVelocity = newVelocity; - - // Drift - if (GroundPercent > 0.0f) - { - if (m_InAir) - { - m_InAir = false; - } - - // manual angular velocity coefficient - float angularVelocitySteering = 0.4f; - float angularVelocitySmoothSpeed = 20f; - - // turning is reversed if we're going in reverse and pressing reverse - if (!localVelDirectionIsFwd && !accelDirectionIsFwd) - angularVelocitySteering *= -1.0f; - - var angularVel = Rigidbody.angularVelocity; - - // move the Y angular velocity towards our target - angularVel.y = Mathf.MoveTowards(angularVel.y, turningPower * angularVelocitySteering, Time.fixedDeltaTime * angularVelocitySmoothSpeed); - - // apply the angular velocity - Rigidbody.angularVelocity = angularVel; - - // rotate rigidbody's velocity as well to generate immediate velocity redirection - // manual velocity steering coefficient - float velocitySteering = 25f; - - // If the karts lands with a forward not in the velocity direction, we start the drift - if (GroundPercent >= 0.0f && m_PreviousGroundPercent < 0.1f) - { - Vector3 flattenVelocity = Vector3.ProjectOnPlane(Rigidbody.linearVelocity, m_VerticalReference).normalized; - if (Vector3.Dot(flattenVelocity, transform.forward * Mathf.Sign(accelInput)) < Mathf.Cos(MinAngleToFinishDrift * Mathf.Deg2Rad)) - { - IsDrifting = true; - m_CurrentGrip = DriftGrip; - m_DriftTurningPower = 0.0f; - } - } - - // Drift Management - if (!IsDrifting) - { - if ((WantsToDrift || isBraking) && currentSpeed > maxSpeed * MinSpeedPercentToFinishDrift) - { - IsDrifting = true; - m_DriftTurningPower = turningPower + (Mathf.Sign(turningPower) * DriftAdditionalSteer); - m_CurrentGrip = DriftGrip; - } - } - - if (IsDrifting) - { - float turnInputAbs = Mathf.Abs(turnInput); - if (turnInputAbs < k_NullInput) - m_DriftTurningPower = Mathf.MoveTowards(m_DriftTurningPower, 0.0f, Mathf.Clamp01(DriftDampening * Time.fixedDeltaTime)); - - // Update the turning power based on input - float driftMaxSteerValue = m_FinalStats.Steer + DriftAdditionalSteer; - m_DriftTurningPower = Mathf.Clamp(m_DriftTurningPower + (turnInput * Mathf.Clamp01(DriftControl * Time.fixedDeltaTime)), -driftMaxSteerValue, driftMaxSteerValue); - - bool facingVelocity = Vector3.Dot(Rigidbody.linearVelocity.normalized, transform.forward * Mathf.Sign(accelInput)) > Mathf.Cos(MinAngleToFinishDrift * Mathf.Deg2Rad); - - bool canEndDrift = true; - if (isBraking) - canEndDrift = false; - else if (!facingVelocity) - canEndDrift = false; - else if (turnInputAbs >= k_NullInput && currentSpeed > maxSpeed * MinSpeedPercentToFinishDrift) - canEndDrift = false; - - if (canEndDrift || currentSpeed < k_NullSpeed) - { - // No Input, and car aligned with speed direction => Stop the drift - IsDrifting = false; - m_CurrentGrip = m_FinalStats.Grip; - } - - } - - // rotate our velocity based on current steer value - Rigidbody.linearVelocity = Quaternion.AngleAxis(turningPower * Mathf.Sign(localVel.z) * velocitySteering * m_CurrentGrip * Time.fixedDeltaTime, transform.up) * Rigidbody.linearVelocity; - } - else - { - m_InAir = true; - } - - bool validPosition = false; - if (Physics.Raycast(transform.position + (transform.up * 0.1f), -transform.up, out RaycastHit hit, 3.0f, 1 << 9 | 1 << 10 | 1 << 11)) // Layer: ground (9) / Environment(10) / Track (11) - { - Vector3 lerpVector = (m_HasCollision && m_LastCollisionNormal.y > hit.normal.y) ? m_LastCollisionNormal : hit.normal; - m_VerticalReference = Vector3.Slerp(m_VerticalReference, lerpVector, Mathf.Clamp01(AirborneReorientationCoefficient * Time.fixedDeltaTime * (GroundPercent > 0.0f ? 10.0f : 1.0f))); // Blend faster if on ground - } - else - { - Vector3 lerpVector = (m_HasCollision && m_LastCollisionNormal.y > 0.0f) ? m_LastCollisionNormal : Vector3.up; - m_VerticalReference = Vector3.Slerp(m_VerticalReference, lerpVector, Mathf.Clamp01(AirborneReorientationCoefficient * Time.fixedDeltaTime)); - } - - validPosition = GroundPercent > 0.7f && !m_HasCollision && Vector3.Dot(m_VerticalReference, Vector3.up) > 0.9f; - - // Airborne / Half on ground management - if (GroundPercent < 0.7f) - { - Rigidbody.angularVelocity = new Vector3(0.0f, Rigidbody.angularVelocity.y * 0.98f, 0.0f); - Vector3 finalOrientationDirection = Vector3.ProjectOnPlane(transform.forward, m_VerticalReference); - finalOrientationDirection.Normalize(); - if (finalOrientationDirection.sqrMagnitude > 0.0f) - { - Rigidbody.MoveRotation(Quaternion.Lerp(Rigidbody.rotation, Quaternion.LookRotation(finalOrientationDirection, m_VerticalReference), Mathf.Clamp01(AirborneReorientationCoefficient * Time.fixedDeltaTime))); - } - } - else if (validPosition) - { - m_LastValidPosition = transform.position; - m_LastValidRotation.eulerAngles = new Vector3(0.0f, transform.rotation.y, 0.0f); - } + // Apply drag to reduce endless acceleration + rb.linearVelocity = Vector3.Lerp(rb.linearVelocity, Vector3.zero, drag * Time.fixedDeltaTime); } } }