using UnityEngine; using System.Collections; using System.Collections.Generic; using GameKitController.Audio; public class carController : vehicleController { [Header ("Custom Settings")] [Space] public List wheelsList = new List (); public List gearsList = new List (); public OtherCarParts otherCarParts; public carSettings settings; public bool rotateVehicleUpward = true; public float minInclinationToRotateVehicle = 60; public float rotateVehicleUpwardSpeed = 3; public bool bounceVehicleOnPassengerGettingOnOff; [Space] [Header ("Debug")] [Space] public int currentGear; public float currentRearRPM = 0; public float currentRPM = 0; public bool anyOnGround; public bool externalBrakeActive; public bool reversing; public bool pauseAutoRotationActiveState; [Space] [Header ("Components")] [Space] public skidsManager skidMarksManager; public shipInterfaceInfo interfaceInfo; List boostingParticles = new List (); bool colliding; bool rotating; int i, j; int collisionForceLimit = 5; float horizontalLean = 0; float verticalLean = 0; float resetTimer = 0; float steerInput = 0; float acceleration; float lastVelocity = 0; float defSteerAngle; float driftAngle; float timeToStabilize = 0; float currentTimeStabilizing = 0; float originalJumpPower; RaycastHit hit; bool interfaceActive; bool bouncingVehicle; Coroutine bounceCoroutine; Transform currentWheelColliderTransform; WheelCollider currentWheelCollider; Transform currentWheelMesh; Transform currentMudGuard; Transform currentSuspension; Wheels currentWheel; Quaternion currentWheelColliderRotation; float localScaleY; WheelHit wheelGroundHitFront = new WheelHit (); WheelHit wheelGroundHitRear = new WheelHit (); WheelHit FrontWheelHit; WheelHit RearWheelHit; float currentSteerInput; Vector3 newLocalEulerAngles; Vector3 vector3Zero = Vector3.zero; Vector3 currentWheelColliderLocalEulerAngles; float currentWheelColliderZAxisAngle; float currentSteeringWheelZAxisAngle; Vector3 wheelCenterPosition; bool currentHasMudGuardValue; bool currentHasSuspensionValue; Vector3 currentWheelUp; float currentWheelRadius; float currentWheelSuspensionDistance; RaycastHit [] raycastResults = new RaycastHit [1]; int hitsAmount; float currentSkidsVolume; int wheelsListCount; int gearListCount; ParticleSystem currentParticleSystem; protected override void InitializeAudioElements () { otherCarParts.InitializeAudioElements (); foreach (var gear in gearsList) { gear.InitializeAudioElements (); if (otherCarParts.gearShiftingSound != null) { gear.gearShiftingAudioElement.audioSource = otherCarParts.gearShiftingSound; } } } public override void Awake () { base.Awake (); } public override void Start () { base.Start (); //get every wheel component, like the mudguard, suspension and the slip smoke particles wheelsListCount = wheelsList.Count; gearListCount = gearsList.Count; for (i = 0; i < wheelsListCount; i++) { currentWheel = wheelsList [i]; currentWheel.hasMudGuard = currentWheel.mudGuard != null; if (currentWheel.hasMudGuard) { currentMudGuard = currentWheel.mudGuard.transform; currentWheel.mudGuardOriginalLocalEuler = transform.localEulerAngles; currentWheel.mudGuardOffset = currentMudGuard.localPosition - currentWheel.wheelMesh.transform.localPosition; } currentWheel.hasSuspension = currentWheel.suspension != null; if (currentWheel.hasSuspension) { currentSuspension = currentWheel.suspension.transform; currentWheel.suspensionOffset = currentSuspension.localPosition - currentWheel.wheelMesh.transform.localPosition; } currentWheel.hasParticles = currentWheel.wheelParticleSystem != null; } //set the sound components setAudioState (otherCarParts.engineAudioElement, 5, 0, true, false, false); setAudioState (otherCarParts.skidAudioElement, 5, 0, true, false, false); setAudioState (otherCarParts.engineStartAudioElement, 5, 0.7f, false, false, false); //get the vehicle rigidbody mainRigidbody.maxAngularVelocity = 5; //store the max sterr angle defSteerAngle = settings.steerAngleLimit; //get the boost particles inside the vehicle if (otherCarParts.boostParticles) { Component [] boostParticlesComponents = otherCarParts.boostParticles.GetComponentsInChildren (typeof (ParticleSystem)); foreach (ParticleSystem child in boostParticlesComponents) { boostingParticles.Add (child); child.gameObject.SetActive (false); } } originalJumpPower = vehicleControllerSettings.jumpPower; } public override void vehicleUpdate () { base.vehicleUpdate (); //set the current axis input in the motor input currentRearRPM = 0; float currentDeltaTime = Time.deltaTime; //steering input steerInput = Mathf.Lerp (steerInput, horizontalAxis, currentDeltaTime * settings.steerInputSpeed); currentSteerInput = settings.steerAngleLimit * steerInput; //set the steer angle in every steerable wheel and get the currentRearRPM from the wheel that power the vehicle for (i = 0; i < wheelsListCount; i++) { currentWheel = wheelsList [i]; currentWheelCollider = currentWheel.wheelCollider; if (currentWheel.steerable) { if (currentWheel.reverseSteer) { currentWheelCollider.steerAngle = -currentSteerInput; } else { currentWheelCollider.steerAngle = currentSteerInput; } } if (currentWheel.powered) { currentRearRPM += currentWheelCollider.rpm; } } //change gears if (!changingGear && !usingGravityControl) { if (currentGear + 1 < gearListCount) { if (currentSpeed >= gearsList [currentGear].gearSpeed && currentRearRPM > 0) { //print ("mas"+gearsList [currentGear+1].Name + " " + gearsList [currentGear].gearSpeed); StartCoroutine (changeGear (currentGear + 1)); } } if (currentGear - 1 >= 0) { if (currentSpeed < gearsList [currentGear - 1].gearSpeed) { //print ("menos"+gearsList [currentGear-1].Name + " " + gearsList [currentGear-1].gearSpeed); StartCoroutine (changeGear (currentGear - 1)); } } } //reset the vehicle rotation if it is upside down if (isUseOfGravityActive () && currentSpeed < 5) { //check the current rotation of the vehicle with respect to the normal of the gravity normal component, which always point the up direction float angle = Vector3.Angle (currentNormal, transform.up); //&& !colliding if (angle > minInclinationToRotateVehicle && !rotating) { resetTimer += currentDeltaTime; if (resetTimer > 1.5f) { resetTimer = 0; if (rotateVehicleUpward && !pauseAutoRotationActiveState) { rotateVehicle (); } } } //set the current gear to 0 if (currentGear > 0) { StartCoroutine (changeGear (0)); } } //if the vehicle has a steering Wheel, rotate it according to the steer input if (otherCarParts.SteeringWheel != null) { currentSteeringWheelZAxisAngle = -settings.steerAngleLimit * steerInput + (driftAngle / settings.steeringAssistanceDivider) * 2; if (Mathf.Abs (currentSteeringWheelZAxisAngle) > 0.01f) { otherCarParts.SteeringWheel.localEulerAngles = new Vector3 (otherCarParts.SteeringWheel.localEulerAngles.x, otherCarParts.SteeringWheel.localEulerAngles.y, currentSteeringWheelZAxisAngle); } } } void FixedUpdate () { if (usingHoverBoardWaypoint) { return; } float currentDeltaTime = Time.deltaTime; localScaleY = transform.localScale.y; //check every wheel collider of the vehicle, to move it and apply rotation to it correctly using raycast WheelHit wheelGroundHit = new WheelHit (); for (i = 0; i < wheelsListCount; i++) { currentWheel = wheelsList [i]; //get the center position of the wheel currentWheelCollider = currentWheel.wheelCollider; currentWheelColliderTransform = currentWheelCollider.transform; currentWheelMesh = currentWheel.wheelMesh.transform; currentHasMudGuardValue = currentWheel.hasMudGuard; if (currentHasMudGuardValue) { currentMudGuard = currentWheel.mudGuard.transform; } currentHasSuspensionValue = currentWheel.hasSuspension; if (currentHasSuspensionValue) { currentSuspension = currentWheel.suspension.transform; } currentWheelRadius = currentWheelCollider.radius; currentWheelSuspensionDistance = currentWheelCollider.suspensionDistance; wheelCenterPosition = currentWheelColliderTransform.TransformPoint (currentWheelCollider.center); //use a raycast in the ground direction currentWheelCollider.GetGroundHit (out wheelGroundHit); currentWheelUp = currentWheelColliderTransform.up; //if the wheel is close enough to the ground, then hitsAmount = Physics.RaycastNonAlloc (wheelCenterPosition, -currentWheelUp, raycastResults, (currentWheelSuspensionDistance + currentWheelRadius) * localScaleY, settings.layer); if (hitsAmount > 0) { //set the wheel mesh position according to the values of the wheel collider currentWheelMesh.position = raycastResults [0].point + (currentWheelRadius * localScaleY) * currentWheelUp; //set the suspension spring position of the wheel collider currentWheel.suspensionSpringPos = -(raycastResults [0].distance - currentWheelRadius); } //the wheel is in the air else { //set the suspension spring position of the wheel collider currentWheel.suspensionSpringPos = -currentWheelSuspensionDistance; //set the wheel mesh position according to the values of the wheel collider currentWheelMesh.position = wheelCenterPosition - (currentWheelSuspensionDistance * localScaleY) * currentWheelUp; } //set the rotation value in the wheel collider currentWheel.rotationValue += currentWheelCollider.rpm * (6) * currentDeltaTime; currentWheelColliderRotation = currentWheelColliderTransform.rotation; //if the wheel powers the vehicle if (currentWheel.powered) { //rotate the wheel mesh only according to the current speed currentWheelMesh.rotation = currentWheelColliderRotation * Quaternion.Euler (currentWheel.rotationValue, 0, currentWheelColliderRotation.z); } //if the wheel is used to change the vehicle direction if (currentWheel.steerable) { //rotate the wheel mesh according to the current speed and rotate in the local Y axis according to the rotation of the steering wheel currentWheelMesh.rotation = currentWheelColliderRotation * Quaternion.Euler (currentWheel.rotationValue, currentWheelCollider.steerAngle + (driftAngle / settings.steeringAssistanceDivider), currentWheelColliderRotation.z); } //if the wheel has a mudguard if (currentHasMudGuardValue) { if (currentWheel.steerable) { newLocalEulerAngles = currentWheel.mudGuardOriginalLocalEuler + new Vector3 (0, settings.steerAngleLimit * steerInput, 0); //if the wheel is steerable, rotate the mudguard according to that rotation if (!newLocalEulerAngles.Equals (vector3Zero)) { currentMudGuard.localEulerAngles = newLocalEulerAngles; } } //set its position according to the wheel position currentMudGuard.localPosition = currentWheelMesh.localPosition + currentWheel.mudGuardOffset; } //if the wheel has suspension, set its poition just like the mudguard if (currentHasSuspensionValue) { currentSuspension.localPosition = currentWheel.suspensionOffset + currentWheelMesh.localPosition; } //calculate the drift angle, using the right side of the vehicle if (currentWheel.powered && currentWheel.rightSide) { currentWheelCollider.GetGroundHit (out wheelGroundHit); driftAngle = Mathf.Lerp (driftAngle, (Mathf.Clamp (wheelGroundHit.sidewaysSlip, settings.driftAngleLimit.x, settings.driftAngleLimit.y)), currentDeltaTime * 2); } //rotate the wheel collider in its forward local axis float handling = Mathf.Lerp (-1, 1, wheelGroundHit.force / settings.steerWheelRotationPercentage); int mult = 1; if (currentWheel.leftSide) { mult = -1; } currentWheelColliderZAxisAngle = (handling * mult); if (Mathf.Abs (currentWheel.lastWheelColliderRotationValue - currentWheelColliderZAxisAngle) > 0.01f) { currentWheel.lastWheelColliderRotationValue = currentWheelColliderZAxisAngle; currentWheelColliderLocalEulerAngles = currentWheelColliderTransform.localEulerAngles; currentWheelColliderTransform.localEulerAngles = new Vector3 (currentWheelColliderLocalEulerAngles.x, currentWheelColliderLocalEulerAngles.y, currentWheelColliderZAxisAngle); } //check the right front and right rear wheel to play the skid audio according to their state //use a wheel hit to that if (currentWheel.powered && currentWheel.rightSide) { currentWheelCollider.GetGroundHit (out wheelGroundHitFront); } if (currentWheel.steerable && currentWheel.rightSide) { currentWheelCollider.GetGroundHit (out wheelGroundHitRear); } } currentSkidsVolume = otherCarParts.skidAudio.volume; if (anyOnGround) { //if the values in the wheel hit are higher that if (Mathf.Abs (wheelGroundHitFront.sidewaysSlip) > 0.25f || Mathf.Abs (wheelGroundHitRear.forwardSlip) > 0.5f || Mathf.Abs (wheelGroundHitFront.forwardSlip) > 0.5f) { //and the vehicle is moving, then if (mainRigidbody.linearVelocity.magnitude > 1) { //set the skid volume value according to the vehicle skid currentSkidsVolume = Mathf.Abs (wheelGroundHitFront.sidewaysSlip) + ((Mathf.Abs (wheelGroundHitFront.forwardSlip) + Mathf.Abs (wheelGroundHitRear.forwardSlip)) / 4f); } else { //set the skid volume value to 0 currentSkidsVolume -= currentDeltaTime; } } else { //set the skid volume value to 0 currentSkidsVolume -= currentDeltaTime; } } else { currentSkidsVolume -= currentDeltaTime * 10; } otherCarParts.skidAudio.volume = currentSkidsVolume; //rotate the vehicle chassis when the gear is being changed //get the vertical lean value verticalLean = Mathf.Clamp (Mathf.Lerp (verticalLean, mainRigidbody.angularVelocity.x * settings.chassisLean.y, currentDeltaTime * 3), -settings.chassisLeanLimit, settings.chassisLeanLimit); for (i = 0; i < wheelsListCount; i++) { currentWheel = wheelsList [i]; if (currentWheel.powered && currentWheel.rightSide) { currentWheelCollider = currentWheel.wheelCollider; currentWheelCollider.GetGroundHit (out wheelGroundHit); } } float normalizedLeanAngle = Mathf.Clamp (wheelGroundHit.sidewaysSlip, -1, 1); if (normalizedLeanAngle > 0) { normalizedLeanAngle = 1; } else { normalizedLeanAngle = -1; } if (!bouncingVehicle) { //get the horizontal lean value horizontalLean = Mathf.Clamp (Mathf.Lerp (horizontalLean, (Mathf.Abs (transform.InverseTransformDirection (mainRigidbody.angularVelocity).y) * -normalizedLeanAngle) * settings.chassisLean.x, currentDeltaTime * 3), -settings.chassisLeanLimit, settings.chassisLeanLimit); Quaternion chassisRotation = Quaternion.Euler (verticalLean, otherCarParts.chassis.transform.localRotation.y + (mainRigidbody.angularVelocity.z), horizontalLean); Vector3 targetLocalEulerAngles = chassisRotation.eulerAngles; //set the lean rotation value in the chassis transform if (Mathf.Abs (targetLocalEulerAngles.magnitude) > 0.01f) { if (!float.IsNaN (targetLocalEulerAngles.x) && !float.IsNaN (targetLocalEulerAngles.y) && !float.IsNaN (targetLocalEulerAngles.z)) { otherCarParts.chassis.transform.localEulerAngles = targetLocalEulerAngles; } } //set the vehicle mass center mainRigidbody.centerOfMass = new Vector3 ((otherCarParts.COM.localPosition.x) * transform.localScale.x, (otherCarParts.COM.localPosition.y) * localScaleY, (otherCarParts.COM.localPosition.z) * transform.localScale.z); } //allows vehicle to remain roughly pointing in the direction of travel //if the vehicle is not on the ground, not colliding, rotating and its speed is higher that 5 if (!anyOnGround && settings.preserveDirectionWhileInAir && !colliding && !rotating && currentSpeed > 5 && !pauseAutoRotationActiveState) { //check the time to stabilize if (timeToStabilize < settings.minTimeToPreserveDirectionWhileInAir) { timeToStabilize += currentDeltaTime; } else { bool rotateVehicleResult = true; if (settings.useMaxTimeToPreserveDirectionWhileOnAir) { currentTimeStabilizing += currentDeltaTime; if (currentTimeStabilizing >= settings.maxTimeToPreserveDirectionWhileOnAir) { rotateVehicleResult = false; } } if (rotateVehicleResult) { //rotate every axis of the vehicle in the rigidbody velocity direction mainRigidbody.freezeRotation = true; float angleX = Mathf.Asin (transform.InverseTransformDirection (Vector3.Cross (currentNormal.normalized, transform.up)).x) * Mathf.Rad2Deg; float angleZ = Mathf.Asin (transform.InverseTransformDirection (Vector3.Cross (currentNormal.normalized, transform.up)).z) * Mathf.Rad2Deg; float angleY = Mathf.Asin (transform.InverseTransformDirection (Vector3.Cross (mainRigidbody.linearVelocity.normalized, transform.forward)).y) * Mathf.Rad2Deg; transform.Rotate ((currentDeltaTime * -1) * new Vector3 (angleX, angleY, angleZ)); } } } //if any of the vehicle is on the groud, free the rigidbody rotation if (anyOnGround) { mainRigidbody.freezeRotation = false; timeToStabilize = 0; currentTimeStabilizing = 0; } bool brakeActive = (braking || isBrakeActive ()); //if the handbrake is pressed, set the brake torque value in every wheel for (i = 0; i < wheelsListCount; i++) { currentWheel = wheelsList [i]; currentWheelCollider = currentWheel.wheelCollider; if (brakeActive) { if (currentWheel.powered) { currentWheelCollider.brakeTorque = settings.brake * 15; } if (currentWheel.steerable) { if (settings.useBrakeValueOnSteerableWheels) { currentWheelCollider.brakeTorque = settings.brakeValueOnSteerableWheels * 15; } else { currentWheelCollider.brakeTorque = settings.brake / 10; } } } else { //else, check if the vehicle input is in forward or in backward direction //the vehicle is decelerating if (Mathf.Abs (motorInput) <= 0.05f && !changingGear) { currentWheelCollider.brakeTorque = settings.brake / 25; } //the vehicle is braking else if (motorInput < 0 && !reversing) { if (currentWheel.powered) { currentWheelCollider.brakeTorque = settings.brake * (Mathf.Abs (motorInput / 2)); } if (currentWheel.steerable) { currentWheelCollider.brakeTorque = settings.brake * (Mathf.Abs (motorInput)); } } else { currentWheelCollider.brakeTorque = 0; } } } //adhere the vehicle to the ground //check the front part float travel = 1; float totalTravel = 0; float antiRollForceFront; for (i = 0; i < wheelsListCount; i++) { currentWheel = wheelsList [i]; if (currentWheel.steerable) { currentWheelCollider = currentWheel.wheelCollider; //if the wheel is in the ground bool grounded = currentWheelCollider.GetGroundHit (out FrontWheelHit); if (grounded) { //get the value to the ground according to the wheel collider configuration travel = (-currentWheelCollider.transform.InverseTransformPoint (FrontWheelHit.point).y - currentWheelCollider.radius) / currentWheelCollider.suspensionDistance; } //if the wheel is the front wheel if (currentWheel.leftSide) { //add to the total travel totalTravel += travel; } //else if (currentWheel.rightSide) { //substract from the total travel totalTravel -= travel; } } travel = 1; } bool anyWheelOnGroundOnFront = false; //now, with the force multiplier which has to be applied in the front wheels for (i = 0; i < wheelsListCount; i++) { currentWheel = wheelsList [i]; if (currentWheel.steerable) { currentWheelCollider = currentWheel.wheelCollider; int mult = 1; if (currentWheel.leftSide) { mult = -1; } //get the total amount of force applied to every wheel antiRollForceFront = totalTravel * settings.antiRoll; //if the wheel is on the ground, apply the force bool grounded = currentWheelCollider.GetGroundHit (out FrontWheelHit); if (grounded) { mainRigidbody.AddForceAtPosition ((mult * antiRollForceFront) * currentWheelCollider.transform.up, currentWheelCollider.transform.position); anyWheelOnGroundOnFront = true; } } } //like the above code, but this time in the rear wheels bool groundRear = true; travel = 1; totalTravel = 0; float antiRollForceRear = 0; for (i = 0; i < wheelsListCount; i++) { currentWheel = wheelsList [i]; if (currentWheel.powered) { currentWheelCollider = currentWheel.wheelCollider; bool grounded = currentWheelCollider.GetGroundHit (out RearWheelHit); if (grounded) { travel = (-currentWheelCollider.transform.InverseTransformPoint (RearWheelHit.point).y - currentWheelCollider.radius) / currentWheelCollider.suspensionDistance; } if (currentWheel.leftSide) { totalTravel += travel; } if (currentWheel.rightSide) { totalTravel -= travel; } } travel = 1; } bool anyWheelOnGroundOnRear = false; for (i = 0; i < wheelsListCount; i++) { currentWheel = wheelsList [i]; if (currentWheel.powered) { currentWheelCollider = currentWheel.wheelCollider; int mult = 1; if (currentWheel.leftSide) { mult = -1; } antiRollForceRear = totalTravel * settings.antiRoll; bool grounded = currentWheelCollider.GetGroundHit (out RearWheelHit); if (grounded) { mainRigidbody.AddForceAtPosition ((mult * antiRollForceRear) * currentWheelCollider.transform.up, currentWheelCollider.transform.position); anyWheelOnGroundOnRear = true; } //if both rear wheels are not in the ground, then else { groundRear = false; } } } //if both rear wheels are in the ground, then if (groundRear) { //add an extra force to the main rigidbody of the vehicle mainRigidbody.AddRelativeTorque ((steerInput * 5000) * Vector3.up); } if (anyWheelOnGroundOnRear || anyWheelOnGroundOnFront) { //check if the jump input has been presses if (jumpInputPressed) { //apply force in the up direction mainRigidbody.AddForce ((mainRigidbody.mass * vehicleControllerSettings.jumpPower) * transform.up); jumpInputPressed = false; lastTimeJump = Time.time; } } //get the current speed value currentSpeed = mainRigidbody.linearVelocity.magnitude * 3; //calculate the current acceleration acceleration = 0; acceleration = (transform.InverseTransformDirection (mainRigidbody.linearVelocity).z - lastVelocity) / Time.fixedDeltaTime; lastVelocity = transform.InverseTransformDirection (mainRigidbody.linearVelocity).z; //set the drag according to vehicle acceleration mainRigidbody.linearDamping = Mathf.Clamp ((acceleration / 50), 0, 1); //set the steer limit settings.steerAngleLimit = Mathf.Lerp (defSteerAngle, settings.highSpeedSteerAngle, (currentSpeed / settings.highSpeedSteerAngleAtSpeed)); //set the current RPM float nextRPM = ((Mathf.Abs (currentRearRPM) * settings.gearShiftRate) + settings.minRPM) / (currentGear + 1); currentRPM = Mathf.Clamp (nextRPM, settings.minRPM, settings.maxRPM); //check if the vehicle is moving forwards or backwards if (motorInput <= 0 && currentRearRPM < 20) { reversing = true; } else { reversing = false; } //set the engine audio volume and pitch according to input and current RPM if (otherCarParts.engineAudio != null && !vehicleDestroyed) { if (!reversing) { otherCarParts.engineAudio.volume = Mathf.Lerp (otherCarParts.engineAudio.volume, Mathf.Clamp (motorInput, 0.35f, 0.85f), Time.deltaTime * 5); } else { otherCarParts.engineAudio.volume = Mathf.Lerp (otherCarParts.engineAudio.volume, Mathf.Clamp (Mathf.Abs (motorInput), 0.35f, 0.85f), Time.deltaTime * 5); } otherCarParts.engineAudio.pitch = Mathf.Lerp (otherCarParts.engineAudio.pitch, Mathf.Lerp (1, 2, (currentRPM - settings.minRPM / 1.5f) / (settings.maxRPM + settings.minRPM)), currentDeltaTime * 5); } //if the current speed is higher that the max speed, stop apply motor torque to the powered wheels if (currentSpeed > vehicleControllerSettings.maxForwardSpeed || Mathf.Abs (currentRearRPM / 2) > settings.rearRPMLimitForMaxSpeed || usingGravityControl) { for (i = 0; i < wheelsListCount; i++) { currentWheel = wheelsList [i]; if (currentWheel.powered) { currentWheelCollider = currentWheel.wheelCollider; currentWheelCollider.motorTorque = 0; } } } else if (!reversing) { //else if the vehicle is moving in fowards direction, apply motor torque to every powered wheel using the gear animation curve float speedMultiplier = 1; if (settings.useCurves) { speedMultiplier = gearsList [currentGear].engineTorqueCurve.Evaluate (currentSpeed); } float motorTorqueValue = settings.engineTorque * Mathf.Clamp (motorInput, 0, 1) * boostInput * speedMultiplier; for (i = 0; i < wheelsListCount; i++) { currentWheel = wheelsList [i]; if (currentWheel.powered) { currentWheelCollider = currentWheel.wheelCollider; currentWheelCollider.motorTorque = motorTorqueValue; } } } //if the vehicle is moving backwards, apply motor torque to every powered wheel if (reversing) { //if the current speed is lower than the maxBackWardSpeed, apply motor torque if (currentSpeed < settings.maxBackWardSpeed && Mathf.Abs (currentRearRPM / 2) < (settings.rearEngineTorque + 500)) { for (i = 0; i < wheelsListCount; i++) { currentWheel = wheelsList [i]; if (currentWheel.powered) { currentWheelCollider = currentWheel.wheelCollider; currentWheelCollider.motorTorque = settings.rearEngineTorque * motorInput; } } } //else, stop adding motor torque else { for (i = 0; i < wheelsListCount; i++) { currentWheel = wheelsList [i]; if (currentWheel.powered) { currentWheelCollider = currentWheel.wheelCollider; currentWheelCollider.motorTorque = 0; } } } } if (vehicleDestroyed) { if (vehicleControllerSettings.autoBrakeIfVehicleDestroyed) { for (i = 0; i < wheelsListCount; i++) { currentWheel = wheelsList [i]; currentWheelCollider = currentWheel.wheelCollider; currentWheelCollider.motorTorque = 0; currentWheelCollider.brakeTorque = settings.brake / 10; } } } else { //if the vehicle is not being driving if (!driving && !externalBrakeActive && !autobrakeActive) { //stop the motor torque and apply brake torque to every wheel for (i = 0; i < wheelsListCount; i++) { currentWheel = wheelsList [i]; currentWheelCollider = currentWheel.wheelCollider; currentWheelCollider.motorTorque = 0; currentWheelCollider.brakeTorque = settings.brake / 10; } } } //set the smoke skid particles in every wheel wheelGroundHit = new WheelHit (); for (i = 0; i < wheelsListCount; i++) { currentWheel = wheelsList [i]; currentWheelCollider = currentWheel.wheelCollider; currentWheelCollider.GetGroundHit (out wheelGroundHit); //set the skid marks under every wheel currentWheel.wheelSlipAmountSideways = Mathf.Abs (wheelGroundHit.sidewaysSlip); currentWheel.wheelSlipAmountForward = Mathf.Abs (wheelGroundHit.forwardSlip); if (currentWheel.wheelSlipAmountSideways > 0.25f || currentWheel.wheelSlipAmountForward > 0.5f) { Vector3 skidPoint = wheelGroundHit.point + (2 * currentDeltaTime) * mainRigidbody.linearVelocity; if (mainRigidbody.linearVelocity.magnitude > 1) { currentWheel.lastSkidmark = skidMarksManager.AddSkidMark (skidPoint, wheelGroundHit.normal, (currentWheel.wheelSlipAmountSideways / 2) + (currentWheel.wheelSlipAmountForward / 2.5f), currentWheel.lastSkidmark); } else { currentWheel.lastSkidmark = -1; } } else { currentWheel.lastSkidmark = -1; } if (currentWheel.hasParticles) { currentWheelCollider.GetGroundHit (out wheelGroundHit); if (Mathf.Abs (wheelGroundHit.sidewaysSlip) > 0.25f || Mathf.Abs (wheelGroundHit.forwardSlip) > 0.5f) { if (!currentWheel.wheelParticleSystem.isPlaying) { currentWheel.wheelParticleSystem.Play (); } } else { if (currentWheel.wheelParticleSystem.isPlaying) { currentWheel.wheelParticleSystem.Stop (); } } } } //set the exhaust particles state for (i = 0; i < otherCarParts.normalExhaust.Count; i++) { currentParticleSystem = otherCarParts.normalExhaust [i]; if (isTurnedOn) { if (currentSpeed < 10) { if (!currentParticleSystem.isPlaying) { currentParticleSystem.Play (); } } else { if (currentParticleSystem.isPlaying) { currentParticleSystem.Stop (); } } } else { if (currentParticleSystem.isPlaying) { currentParticleSystem.Stop (); } } } for (i = 0; i < otherCarParts.heavyExhaust.Count; i++) { currentParticleSystem = otherCarParts.heavyExhaust [i]; if (isTurnedOn) { if (currentSpeed > 10 && motorInput > 0.1f) { if (!currentParticleSystem.isPlaying) { currentParticleSystem.Play (); } } else { if (currentParticleSystem.isPlaying) { currentParticleSystem.Stop (); } } } else { if (currentParticleSystem.isPlaying) { currentParticleSystem.Stop (); } } } //check if the car is in the ground or not anyOnGround = true; int totalWheelsOnAir = 0; for (i = 0; i < wheelsListCount; i++) { if (!wheelsList [i].wheelCollider.isGrounded) { //if the current wheel is in the air, increase the number of wheels in the air totalWheelsOnAir++; } } //if the total amount of wheels in the air is equal to the number of wheel sin the vehicle, anyOnGround is false if (totalWheelsOnAir == wheelsListCount && anyOnGround) { anyOnGround = false; } if (interfaceInfo != null) { if (interfaceActive != isTurnedOn) { interfaceActive = isTurnedOn; interfaceInfo.shipEnginesState (interfaceActive); } } } public override bool isVehicleOnGround () { return anyOnGround; } public override bool isDrivingActive () { return driving; } public override void setEngineOnOrOffState () { base.setEngineOnOrOffState (); } public override void pressHorn () { base.pressHorn (); setAudioState (otherCarParts.hornAudioElement, 5, 1, false, true, false); } public override void passengerGettingOnOff () { vehicleBounceMovement (settings.horizontalLeanPassengerAmount, settings.verticalLeanPassengerAmount); } public void vehicleBounceMovement (float horizontalLeanAmount, float verticalLeanAmount) { if (!bounceVehicleOnPassengerGettingOnOff) { return; } if (bounceCoroutine != null) { StopCoroutine (bounceCoroutine); } bounceCoroutine = StartCoroutine (vehicleBounceMovementCoroutine (horizontalLeanAmount, verticalLeanAmount)); } IEnumerator vehicleBounceMovementCoroutine (float horizontalLeanAmount, float verticalLeanAmount) { bouncingVehicle = true; horizontalLean = Mathf.Clamp (horizontalLeanAmount, -settings.chassisLeanLimit, settings.chassisLeanLimit); verticalLean = Mathf.Clamp (verticalLeanAmount, -settings.chassisLeanLimit, settings.chassisLeanLimit); Quaternion chassisRotation = Quaternion.Euler (otherCarParts.chassis.transform.localRotation.x + verticalLean, otherCarParts.chassis.transform.localRotation.y, otherCarParts.chassis.transform.localRotation.z + horizontalLean); Vector3 targetRotation = chassisRotation.eulerAngles; Vector3 originalEuler = otherCarParts.chassis.transform.eulerAngles; float t = 0; while (t < 1 && otherCarParts.chassis.transform.localEulerAngles != targetRotation) { t += Time.deltaTime * settings.leanPassengerSpeed; otherCarParts.chassis.transform.localRotation = Quaternion.Slerp (otherCarParts.chassis.transform.localRotation, Quaternion.Euler (targetRotation), t); } bouncingVehicle = false; yield return null; } //if the vehicle is using the gravity control, set the state in this component public override void changeGravityControlUse (bool state) { base.changeGravityControlUse (state); if (usingGravityControl) { StartCoroutine (changeGear (0)); } } //the player is getting on or off from the vehicle, so public override void changeVehicleState () { base.changeVehicleState (); if (interfaceInfo != null) { interfaceInfo.enableOrDisableInterface (driving); } } public override void setTurnOnState () { setAudioState (otherCarParts.engineAudioElement, 5, 0, true, true, false); setAudioState (otherCarParts.skidAudioElement, 5, 0, true, true, false); setAudioState (otherCarParts.engineStartAudioElement, 5, 0.7f, false, true, false); } public override void setTurnOffState (bool previouslyTurnedOn) { base.setTurnOffState (previouslyTurnedOn); if (previouslyTurnedOn) { setAudioState (otherCarParts.engineAudioElement, 5, 0, false, false, true); setAudioState (otherCarParts.engineEndAudioElement, 5, 1, false, true, false); } steerInput = 0; } public override void turnOnOrOff (bool state, bool previouslyTurnedOn) { base.turnOnOrOff (state, previouslyTurnedOn); } //the vehicle has been destroyed, so disabled every component in it public override void disableVehicle () { //stop the audio sources setAudioState (otherCarParts.engineAudioElement, 5, 0, false, false, false); setAudioState (otherCarParts.skidAudioElement, 5, 0, false, false, false); setAudioState (otherCarParts.engineStartAudioElement, 5, 0.7f, false, false, false); skidMarksManager.setSkidsEnabledState (false); vehicleDestroyed = true; setTurnOffState (false); //disable the skid particles for (i = 0; i < wheelsListCount; i++) { if (wheelsList [i].hasParticles) { wheelsList [i].wheelParticleSystem.Stop (); } } //disable the exhausts particles for (i = 0; i < otherCarParts.normalExhaust.Count; i++) { otherCarParts.normalExhaust [i].Stop (); } for (i = 0; i < otherCarParts.heavyExhaust.Count; i++) { otherCarParts.heavyExhaust [i].Stop (); } //disable the controller this.enabled = false; if (interfaceInfo != null) { interfaceInfo.enableOrDisableInterface (false); } } Coroutine rotatingVehicleCoroutine; public void rotateVehicle () { if (rotatingVehicleCoroutine != null) { StopCoroutine (rotatingVehicleCoroutine); } rotatingVehicleCoroutine = StartCoroutine (rotateVehicleCoroutine ()); } //reset the vehicle rotation if it is upside down IEnumerator rotateVehicleCoroutine () { rotating = true; timeToStabilize = 0; currentTimeStabilizing = 0; Quaternion currentRotation = transform.rotation; //rotate in the forward direction of the vehicle Quaternion dstRotPlayer = Quaternion.LookRotation (transform.forward, currentNormal); for (float t = 0; t < 1;) { t += Time.deltaTime * rotateVehicleUpwardSpeed; transform.rotation = Quaternion.Slerp (currentRotation, dstRotPlayer, t); mainRigidbody.linearVelocity = vector3Zero; yield return null; } rotating = false; } //change the gear in the vehicle IEnumerator changeGear (int gear) { changingGear = true; setAudioState (gearsList [gear].gearShiftingAudioElement, 5, 0.3f, false, true, false); WaitForSeconds delay = new WaitForSeconds (gearsList [gear].changeGearDuration); yield return delay; changingGear = false; currentGear = gear; currentGear = Mathf.Clamp (currentGear, 0, gearListCount - 1); } //if the vehicle is colliding, then void OnCollisionStay (Collision collision) { //set the values to avoid stabilize the vehicle yet mainRigidbody.freezeRotation = false; colliding = true; timeToStabilize = 0; currentTimeStabilizing = 0; } //the vehicle is not colliding void OnCollisionExit (Collision collision) { colliding = false; } //if the vehicle is using the boost, set the boost particles public override void usingBoosting () { base.usingBoosting (); if (otherCarParts.boostParticles) { for (int i = 0; i < boostingParticles.Count; i++) { currentParticleSystem = boostingParticles [i]; if (usingBoost) { if (!currentParticleSystem.isPlaying) { currentParticleSystem.gameObject.SetActive (true); currentParticleSystem.Play (); var boostingParticlesMain = currentParticleSystem.main; boostingParticlesMain.loop = true; } } else { if (currentParticleSystem.isPlaying) { var boostingParticlesMain = currentParticleSystem.main; boostingParticlesMain.loop = false; } } } } } //use a jump platform public void useVehicleJumpPlatform (Vector3 direction) { mainRigidbody.AddForce (mainRigidbody.mass * direction, ForceMode.Impulse); } public void useJumpPlatformParable (Vector3 direction) { Vector3 jumpForce = direction; mainRigidbody.AddForce (jumpForce, ForceMode.VelocityChange); } public void setJumpPower (float newJumpPower) { vehicleControllerSettings.jumpPower = newJumpPower; } public void setNewJumpPower (float newJumpPower) { vehicleControllerSettings.jumpPower = newJumpPower * 100; } public void setOriginalJumpPower () { vehicleControllerSettings.jumpPower = originalJumpPower; } public void setMaxSpeed (float maxSpeedValue) { vehicleControllerSettings.maxForwardSpeed = maxSpeedValue; } public void setMaxAcceleration (float maxAccelerationValue) { settings.engineTorque = maxAccelerationValue; } public void setMaxBrakePower (float maxBrakePower) { settings.brake = maxBrakePower; } public void setMaxTurboPower (float maxTurboPower) { vehicleControllerSettings.maxBoostMultiplier = maxTurboPower; } public void setCanJumpState (bool state) { vehicleControllerSettings.canJump = state; } public void setRotateVehicleUpwardState (bool state) { rotateVehicleUpward = state; } public void setPreserveDirectionWhileInAirState (bool state) { settings.preserveDirectionWhileInAir = state; } public void setPauseAutoRotationActiveState (bool state) { pauseAutoRotationActiveState = state; } //OVERRIDE FUNCTIONS FOR VEHICLE CONTROLLER //if any collider in the vehicle collides, then public override void setCollisionDetected (Collision currentCollision) { //check that the collision is not with the player if (!currentCollision.gameObject.CompareTag ("Player")) { //if the velocity of the collision is higher that the limit if (currentCollision.relativeVelocity.magnitude > collisionForceLimit) { //set the collision audio with a random collision clip if (otherCarParts.crashAudioElements.Length > 0) { setAudioState (otherCarParts.crashAudioElements [UnityEngine.Random.Range (0, otherCarParts.crashAudioElements.Length)], 5, 1, false, true, false); } } } //reset the collision values mainRigidbody.freezeRotation = false; colliding = true; timeToStabilize = 0; currentTimeStabilizing = 0; } public override void startBrakeVehicleToStopCompletely () { braking = true; externalBrakeActive = braking; } public override void endBrakeVehicleToStopCompletely () { braking = false; externalBrakeActive = braking; } public override void activateAutoBrakeOnGetOff () { base.activateAutoBrakeOnGetOff (); } public override float getCurrentSpeed () { return currentSpeed; } //CALL INPUT FUNCTIONS public override void inputJump () { if (driving && !usingGravityControl && isTurnedOn) { //jump input if (vehicleControllerSettings.canJump) { if (usingHoverBoardWaypoint) { pickOrReleaseHoverboardVehicle (false, false); Vector3 totalJumpForce = mainRigidbody.mass * hoverboardJumpForce * (currentNormal + transform.forward); if (useHoverboardJumpClamp) { totalJumpForce = Vector3.ClampMagnitude (totalJumpForce, hoverboardJumpClamp); } mainRigidbody.AddForce (totalJumpForce, ForceMode.Impulse); return; } jumpInputPressed = true; } } } public override void inputHoldOrReleaseJump (bool holdingButton) { if (driving && !usingGravityControl && isTurnedOn) { if (vehicleControllerSettings.canImpulseHoldingJump && !usingHoverBoardWaypoint) { if (holdingButton) { if (Time.time > lastTimeJump + 0.2f) { usingImpulse = true; } } else { usingImpulse = false; } } } } public override void inputHoldOrReleaseTurbo (bool holdingButton) { if (driving && !usingGravityControl && isTurnedOn && !usingHoverBoardWaypoint) { if (holdingButton) { //boost input if (vehicleControllerSettings.canUseBoost) { usingBoost = true; //set the camera move away action mainVehicleCameraController.usingBoost (true, vehicleControllerSettings.boostCameraShakeStateName, vehicleControllerSettings.useBoostCameraShake, vehicleControllerSettings.moveCameraAwayOnBoost); } } else { //stop boost input usingBoost = false; //disable the camera move away action mainVehicleCameraController.usingBoost (false, vehicleControllerSettings.boostCameraShakeStateName, vehicleControllerSettings.useBoostCameraShake, vehicleControllerSettings.moveCameraAwayOnBoost); //disable the boost particles usingBoosting (); boostInput = 1; } } } public override void inputSetTurnOnState () { if (driving && !usingGravityControl) { if (mainVehicleHUDManager.canSetTurnOnState) { setEngineOnOrOffState (); } } } public override void inputHorn () { if (driving && !usingGravityControl) { pressHorn (); } } public override void inputHoldOrReleaseBrake (bool holdingButton) { if (driving && !usingGravityControl) { braking = holdingButton; } } [System.Serializable] public class Gears { public string Name; public float gearSpeed; public float changeGearDuration = 0.5f; [Space] public AnimationCurve engineTorqueCurve; public AudioClip gearShiftingClip; public AudioElement gearShiftingAudioElement; public void InitializeAudioElements () { if (gearShiftingClip != null) { gearShiftingAudioElement.clip = gearShiftingClip; } } } [System.Serializable] public class Wheels { public string Name; public WheelCollider wheelCollider; public GameObject wheelMesh; public GameObject mudGuard; public GameObject suspension; public bool steerable; public bool powered; public bool leftSide; public bool rightSide; public ParticleSystem wheelParticleSystem; public bool reverseSteer; [HideInInspector] public bool hasMudGuard; [HideInInspector] public bool hasSuspension; [HideInInspector] public bool hasParticles; [HideInInspector] public float lastWheelColliderRotationValue; [HideInInspector] public Vector3 mudGuardOriginalLocalEuler; [HideInInspector] public Vector3 mudGuardOffset; [HideInInspector] public Vector3 suspensionOffset; [HideInInspector] public float suspensionSpringPos; [HideInInspector] public float rotationValue; [HideInInspector] public float wheelSlipAmountSideways; [HideInInspector] public float wheelSlipAmountForward; [HideInInspector] public int lastSkidmark = -1; } [System.Serializable] public class OtherCarParts { public Transform SteeringWheel; public Transform COM; public GameObject wheelSlipPrefab; public GameObject chassis; public AudioClip engineStartClip; public AudioElement engineStartAudioElement; public AudioClip engineClip; public AudioElement engineAudioElement; public AudioClip engineEndClip; public AudioElement engineEndAudioElement; public AudioClip skidClip; public AudioElement skidAudioElement; public AudioClip [] crashClips; public AudioElement [] crashAudioElements; public AudioClip hornClip; public AudioElement hornAudioElement; public List normalExhaust = new List (); public List heavyExhaust = new List (); public AudioSource engineStartAudio; public AudioSource engineAudio; public AudioSource skidAudio; public AudioSource crashAudio; public AudioSource gearShiftingSound; public AudioSource hornAudio; public GameObject boostParticles; public void InitializeAudioElements () { if (engineStartClip != null) { engineStartAudioElement.clip = engineStartClip; } if (engineClip != null) { engineAudioElement.clip = engineClip; } if (engineEndClip != null) { engineEndAudioElement.clip = engineEndClip; } if (skidClip != null) { skidAudioElement.clip = skidClip; } if (crashClips != null && crashClips.Length > 0) { crashAudioElements = new AudioElement [crashClips.Length]; for (var i = 0; i < crashClips.Length; i++) { crashAudioElements [i] = new AudioElement { clip = crashClips [i] }; } } if (hornClip != null) { hornAudioElement.clip = hornClip; } if (engineStartAudio != null) { engineStartAudioElement.audioSource = engineStartAudio; } if (engineAudio != null) { engineAudioElement.audioSource = engineAudio; engineEndAudioElement.audioSource = engineAudio; } if (skidAudio != null) { skidAudioElement.audioSource = skidAudio; } foreach (var audioElement in crashAudioElements) { if (crashAudio != null) { audioElement.audioSource = crashAudio; } } if (hornAudio != null) { hornAudioElement.audioSource = hornAudio; } } } [System.Serializable] public class carSettings { public float engineTorque = 2500; public float rearEngineTorque = 2500; public float maxRPM = 6000; public float minRPM = 1000; public float steerAngleLimit; public float highSpeedSteerAngle = 10; public float highSpeedSteerAngleAtSpeed = 100; public Vector2 driftAngleLimit = new Vector2 (-35, 35); public float steerWheelRotationPercentage = 8000; public int steeringAssistanceDivider = 5; [Space] public float rearRPMLimitForMaxSpeed = 3000; [Space] public float steerInputSpeed = 10; public float brake = 4000; public float maxBackWardSpeed; public float antiRoll = 10000; public bool useBrakeValueOnSteerableWheels; public float brakeValueOnSteerableWheels; [Space] [Space] [Space] public Vector2 chassisLean; public float chassisLeanLimit; [Space] [Space] [Space] public LayerMask layer; public float gearShiftRate = 10; public bool preserveDirectionWhileInAir; public float minTimeToPreserveDirectionWhileInAir = 0.6f; public bool useMaxTimeToPreserveDirectionWhileOnAir; public float maxTimeToPreserveDirectionWhileOnAir; public bool useCurves; [Space] [Space] [Space] public float horizontalLeanPassengerAmount; public float verticalLeanPassengerAmount; public float leanPassengerSpeed; } }