/// <summary> /// Calculates the velocity of the kart. /// </summary> void CalculateDrivingVelocity(float deltaTime, GroundInfo groundInfo, Quaternion rotationStream) { Vector3 localVelocity = Quaternion.Inverse(rotationStream) * Quaternion.Inverse(m_DriftOffset) * m_Velocity; if (groundInfo.isGrounded) { localVelocity.x = Mathf.MoveTowards(localVelocity.x, 0f, m_ModifiedStats.grip * deltaTime); float acceleration = m_HasControl ? m_Input.Acceleration : localVelocity.z > 0.05f ? -1f : 0f; if (acceleration > -k_Deadzone && acceleration < k_Deadzone) // No acceleration input. { localVelocity.z = Mathf.MoveTowards(localVelocity.z, 0f, m_ModifiedStats.coastingDrag * deltaTime); } else if (acceleration > k_Deadzone) // Positive acceleration input. { localVelocity.z = Mathf.MoveTowards(localVelocity.z, m_ModifiedStats.topSpeed, acceleration * m_ModifiedStats.acceleration * deltaTime); } else if (localVelocity.z > k_Deadzone) // Negative acceleration input and going forwards. { localVelocity.z = Mathf.MoveTowards(localVelocity.z, 0f, -acceleration * m_ModifiedStats.braking * deltaTime); } else // Negative acceleration input and not going forwards. { localVelocity.z = Mathf.MoveTowards(localVelocity.z, -m_ModifiedStats.reverseSpeed, -acceleration * m_ModifiedStats.reverseAcceleration * deltaTime); } } if (groundInfo.isCapsuleTouching) { localVelocity.y = Mathf.Max(0f, localVelocity.y); } m_Velocity = m_DriftOffset * rotationStream * localVelocity; if (!groundInfo.isCapsuleTouching) { m_Velocity += Vector3.down * m_ModifiedStats.gravity * deltaTime; } }
/// <summary> /// Starts a drift if the kart lands with a sufficient turn. /// </summary> void StartDrift(GroundInfo currentGroundInfo, GroundInfo nextGroundInfo, Quaternion rotationStream) { if (m_HasControl && m_DriftState == DriftState.NotDrifting) { Vector3 kartForward = rotationStream * Vector3.forward; kartForward.y = 0f; kartForward.Normalize(); Vector3 flatVelocity = m_Velocity; flatVelocity.y = 0f; flatVelocity.Normalize(); float signedAngle = Vector3.SignedAngle(kartForward, flatVelocity, Vector3.up); if (signedAngle > minDriftStartAngle && signedAngle < maxDriftStartAngle) { m_DriftOffset = Quaternion.Euler(0f, signedAngle, 0f); m_DriftState = DriftState.FacingLeft; OnDriftStarted.Invoke(); } else if (signedAngle < -minDriftStartAngle && signedAngle > -maxDriftStartAngle) { m_DriftOffset = Quaternion.Euler(0f, signedAngle, 0f); m_DriftState = DriftState.FacingRight; OnDriftStarted.Invoke(); } else { StopDrift(Time.deltaTime); } } else { StopDrift(Time.deltaTime); } }
/// <summary> /// Affects the rotation stream so that the kart is level with the ground. /// </summary> void GroundNormal(float deltaTime, ref Quaternion rotationStream, GroundInfo currentGroundInfo, GroundInfo nextGroundInfo) { Vector3 rigidbodyUp = m_Rigidbody.rotation * Vector3.up; Quaternion currentTargetRotation = Quaternion.FromToRotation(rigidbodyUp, currentGroundInfo.normal); Quaternion nextTargetRotation = Quaternion.FromToRotation(rigidbodyUp, nextGroundInfo.normal); if (nextGroundInfo.isCloseToGround) { rotationStream = Quaternion.RotateTowards(currentTargetRotation, nextTargetRotation, 0.5f) * rotationStream; } else { rotationStream = Quaternion.RotateTowards(rotationStream, nextTargetRotation, airborneOrientationSpeed * deltaTime); } }
/// <summary> /// Checks whether or not the kart is grounded given a rotation and offset. /// </summary> /// <param name="deltaTime">The time between frames.</param> /// <param name="rotationStream">The rotation the kart will have.</param> /// <param name="offset">The offset from the kart's current position.</param> /// <returns>Information about the ground from the offset position.</returns> GroundInfo CheckForGround(float deltaTime, Quaternion rotationStream, Vector3 offset) { GroundInfo groundInfo = new GroundInfo(); Vector3 defaultPosition = offset + m_Velocity * deltaTime; Vector3 direction = rotationStream * Vector3.down; float capsuleRadius = m_Capsule.radius; float capsuleTouchingDistance = capsuleRadius + Physics.defaultContactOffset; float groundedDistance = capsuleTouchingDistance + k_GroundToCapsuleOffsetDistance; float closeToGroundDistance = Mathf.Max(groundedDistance + capsuleRadius, m_Velocity.y); int hitCount = 0; Ray ray = new Ray(defaultPosition + frontGroundRaycast.position, direction); bool didHitFront = GetNearestFromRaycast(ray, closeToGroundDistance, groundLayers, QueryTriggerInteraction.Ignore, out RaycastHit frontHit); if (didHitFront) { hitCount++; } ray.origin = defaultPosition + rightGroundRaycast.position; bool didHitRight = GetNearestFromRaycast(ray, closeToGroundDistance, groundLayers, QueryTriggerInteraction.Ignore, out RaycastHit rightHit); if (didHitRight) { hitCount++; } ray.origin = defaultPosition + leftGroundRaycast.position; bool didHitLeft = GetNearestFromRaycast(ray, closeToGroundDistance, groundLayers, QueryTriggerInteraction.Ignore, out RaycastHit leftHit); if (didHitLeft) { hitCount++; } ray.origin = defaultPosition + rearGroundRaycast.position; bool didHitRear = GetNearestFromRaycast(ray, closeToGroundDistance, groundLayers, QueryTriggerInteraction.Ignore, out RaycastHit rearHit); if (didHitRear) { hitCount++; } groundInfo.isCapsuleTouching = frontHit.distance <= capsuleTouchingDistance || rightHit.distance <= capsuleTouchingDistance || leftHit.distance <= capsuleTouchingDistance || rearHit.distance <= capsuleTouchingDistance; groundInfo.isGrounded = frontHit.distance <= groundedDistance || rightHit.distance <= groundedDistance || leftHit.distance <= groundedDistance || rearHit.distance <= groundedDistance; groundInfo.isCloseToGround = hitCount > 0; // No hits - normal = Vector3.up if (hitCount == 0) { groundInfo.normal = Vector3.up; } // 1 hit - normal = hit.normal else if (hitCount == 1) { if (didHitFront) { groundInfo.normal = frontHit.normal; } else if (didHitRight) { groundInfo.normal = rightHit.normal; } else if (didHitLeft) { groundInfo.normal = leftHit.normal; } else if (didHitRear) { groundInfo.normal = rearHit.normal; } } // 2 hits - normal = hits average else if (hitCount == 2) { groundInfo.normal = (frontHit.normal + rightHit.normal + leftHit.normal + rearHit.normal) * 0.5f; } // 3 hits - normal = normal of plane from 3 points else if (hitCount == 3) { if (!didHitFront) { groundInfo.normal = Vector3.Cross(rearHit.point - rightHit.point, leftHit.point - rightHit.point); } if (!didHitRight) { groundInfo.normal = Vector3.Cross(rearHit.point - frontHit.point, leftHit.point - frontHit.point); } if (!didHitLeft) { groundInfo.normal = Vector3.Cross(rightHit.point - frontHit.point, rearHit.point - frontHit.point); } if (!didHitRear) { groundInfo.normal = Vector3.Cross(leftHit.point - rightHit.point, frontHit.point - rightHit.point); } } // 4 hits - normal = average of normals from 4 planes else { Vector3 normal0 = Vector3.Cross(rearHit.point - rightHit.point, leftHit.point - rightHit.point); Vector3 normal1 = Vector3.Cross(rearHit.point - frontHit.point, leftHit.point - frontHit.point); Vector3 normal2 = Vector3.Cross(rightHit.point - frontHit.point, rearHit.point - frontHit.point); Vector3 normal3 = Vector3.Cross(leftHit.point - rightHit.point, frontHit.point - rightHit.point); groundInfo.normal = (normal0 + normal1 + normal2 + normal3) * 0.25f; } if (groundInfo.isGrounded) { float dot = Vector3.Dot(groundInfo.normal, m_Velocity.normalized); if (dot > k_VelocityNormalAirborneDot) { groundInfo.isGrounded = false; } } return(groundInfo); }
void FixedUpdate() { if (Mathf.Approximately(Time.timeScale, 0f)) { return; } if (m_RepositionPositionDelta.sqrMagnitude > float.Epsilon || m_RepositionRotationDelta != Quaternion.identity) { m_Rigidbody.MovePosition(m_Rigidbody.position + m_RepositionPositionDelta); m_Rigidbody.MoveRotation(m_RepositionRotationDelta * m_Rigidbody.rotation); m_RepositionPositionDelta = Vector3.zero; m_RepositionRotationDelta = Quaternion.identity; return; } m_RigidbodyPosition = m_Rigidbody.position; KartStats.GetModifiedStats(m_CurrentModifiers, defaultStats, ref m_ModifiedStats); ClearTempModifiers(); Quaternion rotationStream = m_Rigidbody.rotation; float deltaTime = Time.deltaTime; m_CurrentGroundInfo = CheckForGround(deltaTime, rotationStream, Vector3.zero); Hop(rotationStream, m_CurrentGroundInfo); if (m_CurrentGroundInfo.isGrounded && !m_IsGrounded) { OnBecomeGrounded.Invoke(); } if (!m_CurrentGroundInfo.isGrounded && m_IsGrounded) { OnBecomeAirborne.Invoke(); } m_IsGrounded = m_CurrentGroundInfo.isGrounded; ApplyAirborneModifier(m_CurrentGroundInfo); GroundInfo nextGroundInfo = CheckForGround(deltaTime, rotationStream, m_Velocity * deltaTime); GroundNormal(deltaTime, ref rotationStream, m_CurrentGroundInfo, nextGroundInfo); TurnKart(deltaTime, ref rotationStream); StartDrift(m_CurrentGroundInfo, nextGroundInfo, rotationStream); StopDrift(deltaTime); CalculateDrivingVelocity(deltaTime, m_CurrentGroundInfo, rotationStream); Vector3 penetrationOffset = SolvePenetration(rotationStream); penetrationOffset = ProcessVelocityCollisions(deltaTime, rotationStream, penetrationOffset); rotationStream = Quaternion.RotateTowards(m_Rigidbody.rotation, rotationStream, rotationCorrectionSpeed * deltaTime); AdjustVelocityByPenetrationOffset(deltaTime, ref penetrationOffset); m_Rigidbody.MoveRotation(rotationStream); m_Rigidbody.MovePosition(m_RigidbodyPosition + m_Movement); }
void FixedUpdate() { if (Mathf.Approximately(Time.timeScale, 0f)) { return; } if (m_RepositionPositionDelta.sqrMagnitude > float.Epsilon || m_RepositionRotationDelta != Quaternion.identity) { m_Rigidbody.MovePosition(m_Rigidbody.position + m_RepositionPositionDelta); m_Rigidbody.MoveRotation(m_RepositionRotationDelta * m_Rigidbody.rotation); m_RepositionPositionDelta = Vector3.zero; m_RepositionRotationDelta = Quaternion.identity; return; } m_RigidbodyPosition = m_Rigidbody.position; KartStats.GetModifiedStats(m_CurrentModifiers, defaultStats, ref m_ModifiedStats); ClearTempModifiers(); Quaternion rotationStream = m_Rigidbody.rotation; float deltaTime = Time.deltaTime; //Para MRU if (m_Racer.GetLapTime() > 1 && m_ModifiedStats.acceleration == 5) { m_ModifiedStats.acceleration = 0f; //Debug.Log(deltaTime); } ; // Para MRUV if (m_Racer.GetLapTime() > 0f && m_Racer.GetLapTime() < 0.5f && m_ModifiedStats.acceleration == 1) { if (ace) { acelerar_mruv += 1.5f; ace = false; } m_ModifiedStats.acceleration = acelerar_mruv; //Debug.Log(deltaTime); } ; if (m_Racer.GetLapTime() > 0.5f) { ace = true; } m_CurrentGroundInfo = CheckForGround(deltaTime, rotationStream, Vector3.zero); Hop(rotationStream, m_CurrentGroundInfo); if (m_CurrentGroundInfo.isGrounded && !m_IsGrounded) { OnBecomeGrounded.Invoke(); } if (!m_CurrentGroundInfo.isGrounded && m_IsGrounded) { OnBecomeAirborne.Invoke(); } m_IsGrounded = m_CurrentGroundInfo.isGrounded; ApplyAirborneModifier(m_CurrentGroundInfo); GroundInfo nextGroundInfo = CheckForGround(deltaTime, rotationStream, m_Velocity * deltaTime); GroundNormal(deltaTime, ref rotationStream, m_CurrentGroundInfo, nextGroundInfo); TurnKart(deltaTime, ref rotationStream); StartDrift(m_CurrentGroundInfo, nextGroundInfo, rotationStream); StopDrift(deltaTime); CalculateDrivingVelocity(deltaTime, m_CurrentGroundInfo, rotationStream); Vector3 penetrationOffset = SolvePenetration(rotationStream); penetrationOffset = ProcessVelocityCollisions(deltaTime, rotationStream, penetrationOffset); rotationStream = Quaternion.RotateTowards(m_Rigidbody.rotation, rotationStream, rotationCorrectionSpeed * deltaTime); AdjustVelocityByPenetrationOffset(deltaTime, ref penetrationOffset); m_Rigidbody.MoveRotation(rotationStream); m_Rigidbody.MovePosition(m_RigidbodyPosition + m_Movement); //rb.GetPointVelocity(transform.TransformPoint(localWheelPos)); //Debug.Log(LocalSpeed); }