2026-02-05 05:07:55 -08:00
using UnityEngine ;
using System.Collections ;
using System.Collections.Generic ;
public class vehicleGravityControl : gravityObjectManager
{
[Header ("Main Settings")]
[Space]
public bool useGravity = true ;
public float gravityForce = 9.8f ;
public bool searchNewSurfaceOnHighFallSpeed = true ;
public bool checkSurfaceBelowOnRegularState ;
public float timeToSetNullParentOnAir = 0.5f ;
public bool changeDriverGravityWhenGetsOff = true ;
public bool setVehicleRegularGravityOnDriverGetOff ;
[Space]
[Header ("Circumnavigation Settings")]
[Space]
public float circumnavigateRotationSpeed = 10 ;
public bool circumnavigateCurrentSurfaceActive ;
public bool useLerpRotation ;
public bool checkSurfaceBelowLedge ;
public float surfaceBelowLedgeRaycastDistance = 3 ;
public float belowLedgeRotationSpeed = 10 ;
public float minAngleDifferenceToAdhereToSurface = 4 ;
[Space]
[Header ("Current Normal/Gravity Settings")]
[Space]
public Vector3 regularGravity = new Vector3 ( 0 , 1 , 0 ) ;
public Vector3 currentNormal = new Vector3 ( 0 , 1 , 0 ) ;
[Space]
[Header ("Gravity At Start Settings")]
[Space]
public bool startWithNewGravity ;
public bool useVehicleRotation ;
public bool adjustRotationToSurfaceFound ;
public Vector3 newGravityToStart ;
2026-03-29 23:03:14 -07:00
[Space]
[Header ("Shake Camera Settings")]
[Space]
public bool shakeCameraOnHighFallSpeed = true ;
public float minSpeedToShakeCamera = 10 ;
public bool cameraShakeCanBeUsed = true ;
2026-02-05 05:07:55 -08:00
[Space]
[Header ("Others Settings")]
[Space]
public otherSettings settings ;
[Space]
[Header ("Debug")]
[Space]
public bool gravityControlEnabled ;
public bool OnGround ;
public bool gravityPowerActive ;
public bool powerActivated ;
public bool recalculatingSurface ;
public bool searchingSurface ;
public bool searchingNewSurfaceBelow ;
public bool searchAround ;
public bool rotating ;
public bool circumnavigableSurfaceFound ;
public bool useCenterPointActive ;
public Transform currentCenterPoint ;
[Space]
[Header ("Gizmo Settings")]
[Space]
public bool showGizmo ;
[Space]
[Header ("Components")]
[Space]
public Transform gravityAdherenceRaycastParent ;
public Transform surfaceBelowRaycastTransform ;
public vehicleController mainVehicleController ;
public Collider gravityCenterCollider ;
public Rigidbody mainRigidbody ;
public vehicleCameraController vehicleCameraManager ;
public vehicleHUDManager HUDManager ;
public inputActionManager actionManager ;
2026-03-29 23:03:14 -07:00
2026-02-05 05:07:55 -08:00
Vector3 previousNormalDetected ;
Transform vehicleCamera ;
2026-03-29 23:03:14 -07:00
float lastTimeParentFound ;
2026-02-05 05:07:55 -08:00
bool conservateSpeed ;
bool accelerating ;
Vector3 forwardAxisCamera ;
Vector3 rightAxisCamera ;
Vector3 forwardAxisMovement ;
Vector3 surfaceNormal ;
Vector3 previousVelocity ;
float normalGravityMultiplier ;
float horizontalAxis ;
float verticalAxis ;
Transform pivot ;
GameObject fatherDetected ;
RaycastHit hit ;
Coroutine rotateCharacterState ;
Vector2 axisValues ;
Coroutine rotateVehicleCoroutine ;
bool gravityForcePaused ;
bool onGroundChecked ;
float currentCircumnagivateRotationSpeed ;
bool surfaceFound ;
RaycastHit currentSurfaceFound ;
Vector3 currentPosition ;
Vector3 rayPosition ;
Vector3 rayDirection ;
float gravityAdherenceRaycastParentAngleRotation ;
bool checkDownSpeedActive = true ;
GameObject currentObjectBelowVehicle ;
GameObject previousSurfaceBelowVehicle ;
Vector3 pos ;
Vector3 dir ;
Vector3 currentUp ;
bool mainVehicleControllerLocated ;
float originalGravityForce ;
bool addPreviousVelocityMagnitude ;
float previousVelocityMagnitude ;
float lastTimeRotateVehicleToRaycastDirection ;
2026-03-29 23:03:14 -07:00
bool cameraShakeActive ;
2026-02-05 05:07:55 -08:00
void Start ( )
{
//set the current normal in the vehicle controller
if ( mainVehicleController = = null ) {
mainVehicleController = GetComponent < vehicleController > ( ) ;
}
mainVehicleControllerLocated = mainVehicleController ! = null ;
if ( mainVehicleControllerLocated ) {
mainVehicleController . setNormal ( currentNormal ) ;
}
if ( startWithNewGravity ) {
if ( useVehicleRotation ) {
currentUp = transform . up ;
Vector3 normalDirection = - currentUp ;
if ( adjustRotationToSurfaceFound ) {
if ( Physics . Raycast ( transform . position , - currentUp , out hit , Mathf . Infinity , settings . layer ) ) {
normalDirection = hit . normal ;
}
}
if ( currentUp ! = currentNormal ) {
setCustomNormal ( normalDirection ) ;
}
} else {
setCustomNormal ( newGravityToStart ) ;
}
}
vehicleCamera = vehicleCameraManager . transform ;
originalGravityForce = gravityForce ;
}
void Update ( )
{
currentPosition = transform . position ;
//if the vehicle is searching a new surface
if ( searchingSurface ) {
//set the vehicle movement direction in the air (local X and Y axis)
forwardAxisCamera = pivot . TransformDirection ( Vector3 . up ) ;
rightAxisCamera = pivot . TransformDirection ( Vector3 . right ) ;
//new position and direction of a raycast to detect a surface
pos = Vector3 . zero ;
dir = Vector3 . zero ;
//if the player has activate the gravity control in the vehicle, then apply force in the camera direction and set the raycast position in the center of mass
if ( ! searchingNewSurfaceBelow ) {
//this function apply force to the vehicle in the new direction and allows to move it in the air in the left, right, forward and backward direction using the direction
//of the movement as local Y axis
moveInTheAir ( ( gravityForce * ( mainRigidbody . mass / settings . massDivider ) * settings . speed ) * forwardAxisMovement , settings . speed , mainRigidbody . mass / settings . massDivider , forwardAxisMovement ) ;
pos = settings . centerOfMass . position ;
dir = forwardAxisMovement ;
}
//else, the player is falling in the air while the gravity control was enabled, so the ray direction is the negavite local Y axis or -currentNormal and the position is the
//vehicle itself
else {
pos = currentPosition ;
dir = - currentNormal ;
}
if ( showGizmo ) {
Debug . DrawRay ( pos , settings . vehicleRadius * dir , Color . yellow ) ;
}
//if the raycast found a new surface, then
if ( Physics . Raycast ( pos , dir , out hit , settings . vehicleRadius , settings . layer ) ) {
//check is not a trigger or a rigidbody
if ( ! hit . collider . isTrigger & & hit . rigidbody = = null ) {
//a new valid surface has been detected, so stop the search of a new surface
searchingNewSurfaceBelow = false ;
searchingSurface = false ;
searchAround = false ;
powerActivated = false ;
//set to 0 the vehicle velocity
mainRigidbody . linearVelocity = Vector3 . zero ;
//disable the collider in the center of mass
gravityCenterCollider . enabled = false ;
//if the surface can be circumnavigate
if ( hit . collider . gameObject . CompareTag ( settings . tagForCircumnavigate ) ) {
circumnavigableSurfaceFound = true ;
}
//if the surface is moving
if ( hit . collider . gameObject . CompareTag ( settings . tagForMovingObjects ) ) {
addParent ( hit . collider . gameObject ) ;
}
//if the new normal is different from the current normal, which means that is other surface inclination, then
if ( hit . normal ! = currentNormal ) {
//rotate the vehicle according to that normal
if ( rotateVehicleCoroutine ! = null ) {
StopCoroutine ( rotateVehicleCoroutine ) ;
}
rotateVehicleCoroutine = StartCoroutine ( rotateToSurface ( hit . normal , 2 ) ) ;
}
//disable the gravity control in the vehicle controller
if ( mainVehicleControllerLocated ) {
mainVehicleController . changeGravityControlUse ( false ) ;
}
2026-03-29 23:03:14 -07:00
disableCameraShake ( ) ;
2026-02-05 05:07:55 -08:00
}
}
}
//if the gravity control is enabled, and the vehicle falls in its negative loxal Y axis,
//then check if there is any surface below the vehicle which will become the new ground surface
//in case the vehicle reachs a certain velocity
if ( ! OnGround & & ! powerActivated ) {
if ( checkDownSpeedActive & & shakeCameraOnHighFallSpeed & & transform . InverseTransformDirection ( mainRigidbody . linearVelocity ) . y < - minSpeedToShakeCamera ) {
2026-03-29 23:03:14 -07:00
enableCameraShake ( ) ;
2026-02-05 05:07:55 -08:00
}
if ( searchNewSurfaceOnHighFallSpeed & & transform . InverseTransformDirection ( mainRigidbody . linearVelocity ) . y < - settings . velocityToSearch & & ! searchingNewSurfaceBelow & & ! powerActivated ) {
//check that the current normal is not the regular one
if ( currentNormal ! = regularGravity ) {
//enable the collider in the center of mass
gravityCenterCollider . enabled = true ;
//searching a new surface
searchingNewSurfaceBelow = true ;
searchingSurface = true ;
//stop to recalculate the normal of the vehicle
recalculatingSurface = false ;
circumnavigableSurfaceFound = false ;
//enable the gravity control in the vehicle controller
if ( mainVehicleControllerLocated ) {
mainVehicleController . changeGravityControlUse ( true ) ;
}
}
}
}
//if the vehicle is above a circumnavigable object, then recalculate the current normal of the vehicle according to the surface normal under the vehicle
if ( ! searchingSurface & & ( circumnavigableSurfaceFound | | fatherDetected ! = null ) & & recalculatingSurface ) {
//set the distance of the ray, if the vehicle is not on the ground, the distance is higher
float distance = settings . rayDistance + 0.05f ;
if ( ! OnGround ) {
distance = 10 ;
}
surfaceFound = false ;
//if the vehicle founds a surface below it, get its normal
if ( Physics . Raycast ( settings . centerOfMass . position , - currentNormal , out hit , distance , settings . layer ) ) {
currentSurfaceFound = hit ;
surfaceFound = true ;
currentCircumnagivateRotationSpeed = circumnavigateRotationSpeed ;
}
currentUp = transform . up ;
//Get the correct raycast orientation according to input, for example, it is no the same ray position and direction if the vehicle is moving forward or backward
if ( checkSurfaceBelowLedge ) {
rayPosition = currentPosition + 0.1f * currentUp ;
gravityAdherenceRaycastParentAngleRotation = Vector3 . SignedAngle ( transform . forward , mainRigidbody . linearVelocity , currentUp ) ;
gravityAdherenceRaycastParent . localRotation = Quaternion . Euler ( new Vector3 ( 0 , gravityAdherenceRaycastParentAngleRotation , 0 ) ) ;
}
if ( checkSurfaceBelowLedge ) {
rayDirection = - currentUp ;
if ( showGizmo ) {
Debug . DrawRay ( rayPosition , rayDirection , Color . white ) ;
}
if ( ! Physics . Raycast ( rayPosition , rayDirection , out hit , 1 , settings . layer ) ) {
if ( showGizmo ) {
Debug . DrawRay ( rayPosition , rayDirection , Color . green ) ;
}
rayPosition = surfaceBelowRaycastTransform . position ;
rayDirection = surfaceBelowRaycastTransform . forward ;
if ( Physics . Raycast ( rayPosition , rayDirection , out hit , surfaceBelowLedgeRaycastDistance , settings . layer ) ) {
if ( showGizmo ) {
Debug . DrawRay ( rayPosition , hit . distance * rayDirection , Color . yellow ) ;
}
currentSurfaceFound = hit ;
surfaceFound = true ;
currentCircumnagivateRotationSpeed = belowLedgeRotationSpeed ;
} else {
if ( showGizmo ) {
Debug . DrawRay ( rayPosition , surfaceBelowLedgeRaycastDistance * rayDirection , Color . red ) ;
}
}
} else {
if ( showGizmo ) {
Debug . DrawRay ( rayPosition , hit . distance * rayDirection , Color . red ) ;
}
}
}
if ( surfaceFound ) {
if ( ! currentSurfaceFound . collider . isTrigger & & currentSurfaceFound . rigidbody = = null ) {
//the object detected can be circumnavigate, so get the normal direction
if ( currentSurfaceFound . collider . gameObject . CompareTag ( settings . tagForCircumnavigate ) ) {
if ( useCenterPointActive ) {
Vector3 heading = settings . centerOfMass . position - currentCenterPoint . transform . position ;
surfaceNormal = heading / heading . magnitude ;
} else {
surfaceNormal = currentSurfaceFound . normal ;
}
}
//the object is moving, so get the normal direction and set the player as a children of the moving obejct
else if ( currentSurfaceFound . collider . gameObject . CompareTag ( settings . tagForMovingObjects ) ) {
if ( useCenterPointActive ) {
Vector3 heading = settings . centerOfMass . position - currentCenterPoint . transform . position ;
surfaceNormal = heading / heading . magnitude ;
} else {
surfaceNormal = currentSurfaceFound . normal ;
}
if ( fatherDetected = = null ) {
addParent ( currentSurfaceFound . collider . gameObject ) ;
}
}
}
} else {
if ( fatherDetected ! = null ) {
removeParent ( ) ;
}
}
if ( useLerpRotation ) {
//set the current normal according to the hit.normal
currentNormal = Vector3 . Lerp ( currentNormal , surfaceNormal , currentCircumnagivateRotationSpeed * Time . deltaTime ) ;
if ( Vector3 . Angle ( previousNormalDetected , surfaceNormal ) > minAngleDifferenceToAdhereToSurface ) {
previousNormalDetected = surfaceNormal ;
Vector3 myForward = Vector3 . Cross ( transform . right , currentNormal ) ;
Quaternion dstRot = Quaternion . LookRotation ( myForward , currentNormal ) ;
transform . rotation = Quaternion . Lerp ( transform . rotation , dstRot , currentCircumnagivateRotationSpeed * Time . deltaTime ) ;
Vector3 myForwardCamera = Vector3 . Cross ( vehicleCamera . right , currentNormal ) ;
Quaternion dstRotCamera = Quaternion . LookRotation ( myForwardCamera , currentNormal ) ;
vehicleCamera . rotation = Quaternion . Lerp ( vehicleCamera . rotation , dstRotCamera , currentCircumnagivateRotationSpeed * Time . deltaTime ) ;
}
} else {
//set the current normal according to the hit.normal
currentNormal = Vector3 . Slerp ( currentNormal , surfaceNormal , currentCircumnagivateRotationSpeed * Time . deltaTime ) ;
if ( Vector3 . Angle ( previousNormalDetected , currentNormal ) > minAngleDifferenceToAdhereToSurface ) {
previousNormalDetected = surfaceNormal ;
Vector3 myForward = Vector3 . Cross ( transform . right , currentNormal ) ;
Quaternion dstRot = Quaternion . LookRotation ( myForward , currentNormal ) ;
transform . rotation = Quaternion . Slerp ( transform . rotation , dstRot , currentCircumnagivateRotationSpeed * Time . deltaTime ) ;
}
Vector3 myForwardCamera = Vector3 . Cross ( vehicleCamera . right , currentNormal ) ;
Quaternion dstRotCamera = Quaternion . LookRotation ( myForwardCamera , currentNormal ) ;
vehicleCamera . rotation = Quaternion . Slerp ( vehicleCamera . rotation , dstRotCamera , currentCircumnagivateRotationSpeed * Time . deltaTime ) ;
}
if ( mainVehicleControllerLocated ) {
mainVehicleController . setNormal ( currentNormal ) ;
}
}
//if the vehicle is being rotated to a new surface, set its velocity to 0
if ( rotating ) {
mainRigidbody . linearVelocity = Vector3 . zero ;
}
if ( ( gravityPowerActive | | circumnavigateCurrentSurfaceActive ) & & ! powerActivated ) {
if ( currentObjectBelowVehicle ! = null ) {
//check if the object detected can be circumnavigate
if ( currentObjectBelowVehicle . CompareTag ( settings . tagForCircumnavigate ) ) {
circumnavigableSurfaceFound = true ;
} else {
circumnavigableSurfaceFound = false ;
}
//check if the object is moving to parent the player inside it
if ( currentObjectBelowVehicle . CompareTag ( settings . tagForMovingObjects ) ) {
if ( previousSurfaceBelowVehicle ! = currentObjectBelowVehicle ) {
previousSurfaceBelowVehicle = currentObjectBelowVehicle ;
addParent ( currentObjectBelowVehicle ) ;
}
} else if ( fatherDetected ! = null & & ! currentObjectBelowVehicle . transform . IsChildOf ( fatherDetected . transform ) ) {
removeParent ( ) ;
}
//if the surface where the player lands can be circumnavigated or an moving/rotating object, then keep recalculating the player throught the normal surface
if ( circumnavigableSurfaceFound | | fatherDetected ! = null ) {
recalculatingSurface = true ;
}
//else disable this state
else {
recalculatingSurface = false ;
}
} else {
if ( previousSurfaceBelowVehicle ! = null ) {
previousSurfaceBelowVehicle = null ;
}
if ( rotating ) {
circumnavigableSurfaceFound = false ;
}
}
} else {
if ( ( circumnavigableSurfaceFound | | fatherDetected ! = null ) & & rotating ) {
circumnavigableSurfaceFound = false ;
}
if ( checkSurfaceBelowOnRegularState & & ! gravityPowerActive & & ! HUDManager . isVehicletAsChildOfParent ( ) ) {
if ( currentObjectBelowVehicle ! = null ) {
//check if the object is moving to parent the player inside it
if ( currentObjectBelowVehicle . CompareTag ( settings . tagForMovingObjects ) ) {
if ( previousSurfaceBelowVehicle ! = currentObjectBelowVehicle ) {
previousSurfaceBelowVehicle = currentObjectBelowVehicle ;
addParent ( currentObjectBelowVehicle ) ;
lastTimeParentFound = Time . time ;
}
} else if ( fatherDetected ! = null & & ! currentObjectBelowVehicle . transform . IsChildOf ( fatherDetected . transform ) ) {
removeParent ( ) ;
}
} else {
if ( previousSurfaceBelowVehicle ! = null ) {
previousSurfaceBelowVehicle = null ;
}
if ( Time . time > timeToSetNullParentOnAir + lastTimeParentFound ) {
removeParent ( ) ;
}
}
}
}
}
void FixedUpdate ( )
{
// If the gravity control is not being used
if ( ! powerActivated & & ! gravityForcePaused ) {
// Apply force to the vehicle in the negative local Y axis, so the vehicle has a regular gravity
if ( useGravity ) {
mainRigidbody . AddForce ( ( - gravityForce * mainRigidbody . mass * settings . gravityMultiplier ) * currentNormal ) ;
}
// If the vehicle is search a new surface, and previously the gravity control was enabled, then
if ( searchingNewSurfaceBelow ) {
// Apply force in the negative local Y axis of the vehicle, and allow to move it left, right, backward and forward
moveInTheAir ( mainRigidbody . linearVelocity , 1 , 1 , - currentNormal ) ;
}
// Check if the vehicle is on the ground
OnGround = false ;
// Use a raycast to it, with the center of the mass as position and the negative local Y axis as direction, using the ray distance configured in the inspector
if ( Physics . Raycast ( settings . centerOfMass . position , - currentNormal , out hit , settings . rayDistance , settings . layer ) ) {
if ( showGizmo ) {
Debug . DrawLine ( settings . centerOfMass . position , hit . point , Color . cyan ) ;
}
// If the hit is not a trigger
if ( ! hit . collider . isTrigger ) {
// The vehicle is on the ground
OnGround = true ;
currentObjectBelowVehicle = hit . collider . gameObject ;
}
} else {
// Else the vehicle is in the air
OnGround = false ;
currentObjectBelowVehicle = null ;
}
} else {
// The vehicle is searching a new surface, so it is in the air
OnGround = false ;
}
if ( OnGround ) {
if ( ! onGroundChecked ) {
2026-03-29 23:03:14 -07:00
disableCameraShake ( ) ;
2026-02-05 05:07:55 -08:00
onGroundChecked = true ;
// If the gravity control is enabled, and the surface below the vehicle can be circumnavigate or is a moving object, allow to recalculate the normal
if ( ( circumnavigableSurfaceFound | | fatherDetected ! = null ) & & gravityPowerActive ) {
recalculatingSurface = true ;
} else {
recalculatingSurface = false ;
}
}
} else {
if ( onGroundChecked ) {
onGroundChecked = false ;
}
}
}
void moveInTheAir ( Vector3 newVel , float speedMultiplier , float massMultiplier , Vector3 movementDirection )
{
//get the new velocity to apply to the vehicle
Vector3 newVelocity = newVel ;
//get the current values of the horizontal and vertical axis, from the input manager
if ( actionManager ! = null ) {
axisValues = actionManager . getPlayerMovementAxis ( ) ;
} else {
axisValues = Vector3 . zero ;
}
horizontalAxis = axisValues . x ;
verticalAxis = axisValues . y ;
//allow to move the vehicle in its local X and Y axis while he falls or move in the air using the gravity control
Vector3 newmoveInput = verticalAxis * forwardAxisCamera + horizontalAxis * rightAxisCamera ;
if ( newmoveInput . magnitude > 1 ) {
newmoveInput . Normalize ( ) ;
}
//if the input axis are being used, set that movement to the vehicle
if ( newmoveInput . magnitude > 0 ) {
newVelocity + = ( settings . speed * ( mainRigidbody . mass / settings . massDivider ) * speedMultiplier ) * newmoveInput ;
}
//if the player is accelerating in the air, add more velocity to the vehicle
if ( accelerating ) {
newVelocity + = ( settings . accelerateSpeed * massMultiplier ) * forwardAxisMovement ;
}
//if the current local Y velocity is lower than the limit, clamp the velocity
if ( Mathf . Abs ( transform . InverseTransformDirection ( newVelocity ) . y ) > 40 ) {
newVelocity - = Mathf . Abs ( transform . InverseTransformDirection ( newVelocity ) . y ) * movementDirection ;
newVelocity + = 40 * movementDirection ;
}
//set the new velocity to the vehicle
mainRigidbody . linearVelocity = Vector3 . Lerp ( mainRigidbody . linearVelocity , newVelocity , Time . deltaTime * 2 ) ;
}
//when the colliders of the vehicle hits another surface
public void setCollisionDetected ( Collision currentCollision )
{
//if the vehicle was searching a new surface using the gravity control, then
if ( searchingSurface & & searchAround ) {
//check the type of collider
if ( ! currentCollision . gameObject . CompareTag ( "Player" ) & & currentCollision . rigidbody = = null & & ! currentCollision . collider . isTrigger ) {
//using the collision contact, get the direction of that new surface, so the vehicle change its movement to the position of that surface
//like this the searching of a new surface is more accurate, since a raycast and collisions are used to detect the new surface
Vector3 hitDirection = currentCollision . contacts [ 0 ] . point - settings . centerOfMass . position ;
//set the current direction of the vehicle
hitDirection = hitDirection / hitDirection . magnitude ;
forwardAxisMovement = hitDirection ;
//stop to search collisions around the vehicle
searchAround = false ;
}
}
}
//set and remove the parent of the vehicle according to the type of surface
void addParent ( GameObject obj )
{
fatherDetected = obj ;
parentAssignedSystem currentParentAssignedSystem = obj . GetComponent < parentAssignedSystem > ( ) ;
if ( currentParentAssignedSystem ! = null ) {
fatherDetected = currentParentAssignedSystem . getAssignedParent ( ) ;
}
setFatherDetectedAsParent ( ) ;
}
void removeParent ( )
{
setNullParent ( ) ;
fatherDetected = null ;
}
public void setFatherDetectedAsParent ( )
{
transform . SetParent ( fatherDetected . transform ) ;
vehicleCamera . SetParent ( fatherDetected . transform ) ;
}
public void setNullParent ( )
{
transform . SetParent ( null ) ;
vehicleCamera . SetParent ( null ) ;
}
//activate the collider in the center of mass of the vehicle to dectect collisions with a new surface when the gravity control is enabled
IEnumerator activateCollider ( )
{
//wait to avoid enable the collider when the vehicle stills in the ground
yield return new WaitForSeconds ( 0.2f ) ;
//if the vehicle is searching surface, then active the collider
if ( searchingSurface | | searchingNewSurfaceBelow | | searchAround ) {
gravityCenterCollider . enabled = true ;
}
}
//enable the gravity control in the direction of the camera
public void activateGravityPower ( Vector3 dir , Vector3 right )
{
StartCoroutine ( activateCollider ( ) ) ;
searchingNewSurfaceBelow = false ;
removeParent ( ) ;
circumnavigableSurfaceFound = false ;
searchingSurface = true ;
searchAround = true ;
gravityPowerActive = true ;
forwardAxisMovement = dir ;
rightAxisCamera = right ;
powerActivated = true ;
//enable the gravity control in the vehicle controller
2026-03-29 23:03:14 -07:00
enableCameraShake ( ) ;
2026-02-05 05:07:55 -08:00
if ( mainVehicleControllerLocated ) {
mainVehicleController . changeGravityControlUse ( true ) ;
}
}
//disaable the gravity control, rotatin the vehicle to the regular gravity
public void deactivateGravityPower ( )
{
//check that the vehicle was searching a new surface, or that the gravity is different than vector3.up
if ( searchingSurface | | searchingNewSurfaceBelow | | searchAround | | currentNormal ! = regularGravity ) {
conservateSpeed = true ;
gravityCenterCollider . enabled = false ;
accelerating = false ;
circumnavigableSurfaceFound = false ;
removeParent ( ) ;
searchingNewSurfaceBelow = false ;
searchingSurface = false ;
searchAround = false ;
recalculatingSurface = false ;
powerActivated = false ;
//if the current normal before reset the gravity to the regular one is different from vectore.up, then rotate the vehicle back to the regular state
if ( currentNormal ! = regularGravity ) {
if ( rotateVehicleCoroutine ! = null ) {
StopCoroutine ( rotateVehicleCoroutine ) ;
}
rotateVehicleCoroutine = StartCoroutine ( rotateToSurface ( regularGravity , 2 ) ) ;
}
gravityPowerActive = false ;
//set the normal in the vehicle controller
if ( mainVehicleControllerLocated ) {
mainVehicleController . setNormal ( regularGravity ) ;
}
//disable the gravity control in the vehicle controller
2026-03-29 23:03:14 -07:00
disableCameraShake ( ) ;
2026-02-05 05:07:55 -08:00
if ( mainVehicleControllerLocated ) {
mainVehicleController . changeGravityControlUse ( false ) ;
}
}
}
//the player is getting on or off from the vehicle, so enable or disable the graivty control component
public void changeGravityControlState ( bool state )
{
gravityControlEnabled = state ;
//if the vehicle is not being driving, and it wasn't in the ground, deactivate the gravity control
if ( ! gravityControlEnabled & & ! OnGround ) {
deactivateGravityPower ( ) ;
accelerating = false ;
}
}
//rotate the vehicle and its camera to the new found surface, using the normal of that surface
public IEnumerator rotateToSurface ( Vector3 normal , int rotSpeed )
{
previousVelocity = mainRigidbody . linearVelocity ;
//the vehicle is being rotate, so set its velocity to 0
rotating = true ;
currentNormal = normal ;
//set the new normal in the vehicle controller
if ( mainVehicleControllerLocated ) {
mainVehicleController . setNormal ( currentNormal ) ;
}
//get the current rotation of the vehicle and the camera
Quaternion currentVehicleRotation = transform . rotation ;
Quaternion currentVehicleCameraRotation = vehicleCamera . rotation ;
Vector3 currentVehicleForward = Vector3 . Cross ( transform . right , normal ) ;
Quaternion targetVehicleRotation = Quaternion . LookRotation ( currentVehicleForward , normal ) ;
Vector3 currentVehicleCameraForward = Vector3 . Cross ( vehicleCamera . right , normal ) ;
Quaternion targetVehicleCameraRotation = Quaternion . LookRotation ( currentVehicleCameraForward , normal ) ;
//rotate from their rotation to thew new surface normal direction
for ( float t = 0 ; t < 1 ; ) {
t + = Time . deltaTime * rotSpeed ;
vehicleCamera . rotation = Quaternion . Slerp ( currentVehicleCameraRotation , targetVehicleCameraRotation , t ) ;
transform . rotation = Quaternion . Slerp ( currentVehicleRotation , targetVehicleRotation , t ) ;
yield return null ;
}
rotating = false ;
//store the current vehicle velocity to set it again once the rotation is finished, is only applied when the gravity control is disabled
if ( conservateSpeed ) {
mainRigidbody . linearVelocity = previousVelocity ;
}
conservateSpeed = false ;
}
public void setUseCenterPointActiveState ( bool state , Transform newCenterPoint )
{
useCenterPointActive = state ;
currentCenterPoint = newCenterPoint ;
}
public void rotateVehicleToRaycastDirection ( Vector3 raycastDirection )
{
if ( rotating ) {
return ;
}
2026-03-29 23:03:14 -07:00
//print ("ROTATE");
2026-02-05 05:07:55 -08:00
if ( lastTimeRotateVehicleToRaycastDirection ! = 0 ) {
if ( Time . time < lastTimeRotateVehicleToRaycastDirection + 0.5f ) {
return ;
}
}
lastTimeRotateVehicleToRaycastDirection = Time . time ;
pos = settings . centerOfMass . position ;
dir = raycastDirection ;
//if the raycast found a new surface, then
if ( Physics . Raycast ( pos , dir , out hit , Mathf . Infinity , settings . layer ) ) {
//check is not a trigger or a rigidbody
if ( ! hit . collider . isTrigger & & hit . rigidbody = = null ) {
//a new valid surface has been detected, so stop the search of a new surface
searchingNewSurfaceBelow = false ;
searchingSurface = false ;
searchAround = false ;
powerActivated = false ;
addPreviousVelocityMagnitude = true ;
if ( mainRigidbody . linearVelocity . magnitude ! = 0 ) {
previousVelocityMagnitude = mainRigidbody . linearVelocity . magnitude ;
}
//set to 0 the vehicle velocity
mainRigidbody . linearVelocity = Vector3 . zero ;
//disable the collider in the center of mass
gravityCenterCollider . enabled = false ;
//if the surface can be circumnavigate
if ( hit . collider . gameObject . CompareTag ( settings . tagForCircumnavigate ) ) {
circumnavigableSurfaceFound = true ;
}
//if the surface is moving
if ( hit . collider . gameObject . CompareTag ( settings . tagForMovingObjects ) ) {
addParent ( hit . collider . gameObject ) ;
}
//if the new normal is different from the current normal, which means that is other surface inclination, then
if ( hit . normal ! = currentNormal ) {
//rotate the vehicle according to that normal
if ( rotateVehicleCoroutine ! = null ) {
StopCoroutine ( rotateVehicleCoroutine ) ;
}
rotateVehicleCoroutine = StartCoroutine ( rotateToSurface ( hit . normal , 2 ) ) ;
}
//disable the gravity control in the vehicle controller
if ( mainVehicleControllerLocated ) {
mainVehicleController . changeGravityControlUse ( false ) ;
}
2026-03-29 23:03:14 -07:00
disableCameraShake ( ) ;
2026-02-05 05:07:55 -08:00
if ( rotateVehicleCoroutine ! = null ) {
StopCoroutine ( rotateVehicleCoroutine ) ;
}
rotateVehicleCoroutine = StartCoroutine ( rotateVehicleToLandSurfaceCoroutine ( hit . normal ) ) ;
}
}
}
2026-03-29 23:03:14 -07:00
void accelerateCameraShake ( )
{
if ( cameraShakeCanBeUsed ) {
vehicleCameraManager . usingBoost ( true , "Quick Gravity Control" , true , true ) ;
cameraShakeActive = true ;
}
}
void enableCameraShake ( )
{
if ( cameraShakeCanBeUsed ) {
vehicleCameraManager . usingBoost ( true , "Regular Gravity Control" , true , true ) ;
cameraShakeActive = true ;
}
}
void disableCameraShake ( )
{
if ( cameraShakeCanBeUsed ) {
if ( cameraShakeActive ) {
vehicleCameraManager . usingBoost ( false , "StopShake" , true , true ) ;
cameraShakeActive = false ;
}
}
}
2026-02-05 05:07:55 -08:00
public void rotateVehicleToLandSurface ( Vector3 hitNormal )
{
if ( rotating & & hitNormal = = currentNormal ) {
return ;
}
if ( rotateVehicleCoroutine ! = null ) {
StopCoroutine ( rotateVehicleCoroutine ) ;
}
rotateVehicleCoroutine = StartCoroutine ( rotateVehicleToLandSurfaceCoroutine ( hitNormal ) ) ;
}
public IEnumerator rotateVehicleToLandSurfaceCoroutine ( Vector3 hitNormal )
{
rotating = true ;
currentNormal = hitNormal ;
//set the new normal in the vehicle controller
if ( mainVehicleControllerLocated ) {
mainVehicleController . setNormal ( currentNormal ) ;
}
//get the current rotation of the vehicle and the camera
Quaternion currentVehicleRotation = transform . rotation ;
Quaternion currentVehicleCameraRotation = vehicleCamera . rotation ;
Vector3 currentVehicleForward = Vector3 . Cross ( transform . right , hitNormal ) ;
Quaternion targetVehicleRotation = Quaternion . LookRotation ( currentVehicleForward , hitNormal ) ;
Vector3 currentVehicleCameraForward = Vector3 . Cross ( vehicleCamera . right , hitNormal ) ;
Quaternion targetVehicleCameraRotation = Quaternion . LookRotation ( currentVehicleCameraForward , hitNormal ) ;
//rotate from their rotation to thew new surface normal direction
for ( float t = 0 ; t < 1 ; ) {
t + = Time . deltaTime * 2 ;
vehicleCamera . rotation = Quaternion . Slerp ( currentVehicleCameraRotation , targetVehicleCameraRotation , t ) ;
transform . rotation = Quaternion . Slerp ( currentVehicleRotation , targetVehicleRotation , t ) ;
yield return null ;
}
rotating = false ;
pauseDownForce ( false ) ;
if ( addPreviousVelocityMagnitude ) {
mainRigidbody . linearVelocity + = previousVelocityMagnitude * ( - currentNormal ) ;
addPreviousVelocityMagnitude = false ;
}
}
//get the current camera pivot, according to the vehicle view, in fisrt or third person
public void getCurrentCameraPivot ( Transform newPivot )
{
pivot = newPivot ;
}
public void pauseDownForce ( bool state )
{
useGravity = ! state ;
}
public void setCustomNormal ( Vector3 normal )
{
Quaternion currentVehicleRotation = transform . rotation ;
Quaternion currentVehicleCameraRotation = vehicleCamera . rotation ;
Vector3 currentVehicleForward = Vector3 . Cross ( transform . right , normal ) ;
Quaternion targetVehicleRotation = Quaternion . LookRotation ( currentVehicleForward , normal ) ;
Vector3 currentVehicleCameraForward = Vector3 . Cross ( vehicleCamera . right , normal ) ;
Quaternion targetVehicleCameraRotation = Quaternion . LookRotation ( currentVehicleCameraForward , normal ) ;
transform . rotation = targetVehicleRotation ;
vehicleCamera . rotation = targetVehicleCameraRotation ;
currentNormal = normal ;
}
public override Vector3 getCurrentNormal ( )
{
return currentNormal ;
}
public float getGravityForce ( )
{
return gravityForce ;
}
public void setNewGravityForce ( float newValue )
{
gravityForce = newValue ;
}
public void setOriginalGravityForce ( )
{
setNewGravityForce ( originalGravityForce ) ;
}
public override bool isUsingRegularGravity ( )
{
return currentNormal = = regularGravity ;
}
public Vector3 getRegularGravity ( )
{
return regularGravity ;
}
public void updateCurrentNormalExternally ( Vector3 newValue )
{
currentNormal = newValue ;
}
public bool changeDriverGravityWhenGetsOffActive ( )
{
return changeDriverGravityWhenGetsOff ;
}
public bool isSetVehicleRegularGravityOnDriverGetOffActive ( )
{
return setVehicleRegularGravityOnDriverGetOff ;
}
public void setGravityForcePausedState ( bool state )
{
gravityForcePaused = state ;
}
public bool isGravityPowerActive ( )
{
return gravityPowerActive ;
}
public bool isGravityControlEnabled ( )
{
return gravityControlEnabled ;
}
//CALL INPUT FUNCTIONS
public void inputEnableOrDisableGravityControl ( bool enableGravityControl )
{
if ( HUDManager . vehicleIsDestroyed ( ) ) {
return ;
}
if ( ! mainVehicleController . isPlayerMovingOn3dWorld ( ) ) {
return ;
}
//if the vehicle is being driving
if ( gravityControlEnabled & & settings . canUseGravityControl ) {
//activate the gravity control to search a new surface in the camera direction
if ( enableGravityControl ) {
activateGravityPower ( pivot . TransformDirection ( Vector3 . forward ) , pivot . TransformDirection ( Vector3 . right ) ) ;
} else {
//deactivate the gravity control
deactivateGravityPower ( ) ;
}
}
}
public void inputIncreasOrDecreaseGravitySpeed ( bool increaseGravitySpeed )
{
if ( HUDManager . vehicleIsDestroyed ( ) ) {
return ;
}
if ( gravityControlEnabled & & settings . canUseGravityControl ) {
if ( searchingSurface | | searchingNewSurfaceBelow | | gravityPowerActive ) {
//if the vehicle is searching a new surface, increase its velocity, only when the gravity power is active
if ( increaseGravitySpeed ) {
accelerating = true ;
2026-03-29 23:03:14 -07:00
accelerateCameraShake ( ) ;
2026-02-05 05:07:55 -08:00
} else {
//stop to increase the velocity of the vehiclee in the air
accelerating = false ;
2026-03-29 23:03:14 -07:00
enableCameraShake ( ) ;
2026-02-05 05:07:55 -08:00
}
}
}
}
public void setCheckDownSpeedActiveState ( bool state )
{
checkDownSpeedActive = state ;
}
#if UNITY_EDITOR
//draw the ray distance radius in the vehicle
void OnDrawGizmosSelected ( )
{
if ( showGizmo ) {
if ( settings . centerOfMass ! = null ) {
Gizmos . color = Color . yellow ;
Gizmos . DrawWireSphere ( settings . centerOfMass . position , settings . vehicleRadius ) ;
Gizmos . color = Color . cyan ;
Gizmos . DrawLine ( settings . centerOfMass . position , settings . centerOfMass . position - settings . rayDistance * settings . centerOfMass . up ) ;
}
}
}
#endif
[System.Serializable]
public class otherSettings
{
public LayerMask layer ;
public float speed = 10 ;
public float accelerateSpeed = 20 ;
public float velocityToSearch = 10 ;
public float gravityMultiplier = 1 ;
public float rayDistance ;
public float vehicleRadius ;
public Transform centerOfMass ;
public bool canUseGravityControl ;
public float massDivider = 1000 ;
public string tagForCircumnavigate = "sphere" ;
public string tagForMovingObjects = "moving" ;
}
}