- Home /
How do I test if car is grounded to stop it from driving in mid-air?
I have a script that makes my car drive but i really cant figure out how to test if my car is grounded so i can stop it from moving forwards in the "Z" axis whilst in mid air.
Here Is My Script:
public var CarGravity : float = -20;
private var accel : Vector3;
public var throttle : float;
private var deadZone : float = .001;
private var myRight : Vector3;
private var velo : Vector3;
private var flatVelo : Vector3;
private var relativeVelocity : Vector3;
private var dir: Vector3;
private var flatDir : Vector3;
private var carUp : Vector3;
private var carTransform : Transform;
private var carRigidbody : Rigidbody;
private var engineForce : Vector3;
private var turnVec : Vector3;
private var imp : Vector3;
private var rev : float;
private var actualTurn : float;
private var carMass : float;
private var wheelTransform : Transform[] = new Transform[4];
public var actualGrip : float;
public var horizontal : float;
private var maxSpeedToTurn : float = .2;
public var frontLeftWheel : Transform;
public var frontRightWheel : Transform;
public var rearLeftWheel : Transform;
public var rearRightWheel : Transform;
public var LFWheelTransform : Transform;
public var RFWheelTransform : Transform;
public var power : float = 300;
public var maxSpeed : float = 50;
public var carGrip : float = 70;
public var turnSpeed : float = 3.0;
private var slideSpeed : float;
public var mySpeed : float;
private var carRight : Vector3;
private var carFwd : Vector3;
private var tempVEC : Vector3;
function Start()
{
Initialize();
Physics.gravity.y = CarGravity;
}
function Initialize()
{
carTransform = transform;
carRigidbody = rigidbody;
carUp = carTransform.up;
carMass = rigidbody.mass;
carFwd = Vector3.forward;
carRight = Vector3.down;
setUpWheels();
carRigidbody.centerOfMass = Vector3(0,-0.7,.35);
}
function Update()
{
checkInput();
carPhysicsUpdate();
}
function LateUpdate()
{
rotateVisualWheels();
engineSound();
}
function setUpWheels()
{
if((null == frontLeftWheel || null == frontRightWheel || null == rearLeftWheel || null == rearRightWheel ))
{
Debug.LogError("One or more of the wheel transformshave not been plugged in on the car");
Debug.Break();
}
else
{
wheelTransform[0] = frontLeftWheel;
wheelTransform[1] = rearLeftWheel;
wheelTransform[2] = frontRightWheel;
wheelTransform[3] = rearRightWheel;
}
}
private var rotationAmount : Vector3;
function rotateVisualWheels()
{
LFWheelTransform.localEulerAngles.y = horizontal * 30;
RFWheelTransform.localEulerAngles.y = horizontal * 30;
rotationAmount = carRight * (relativeVelocity.z * 1.6 * Time.deltaTime * Mathf.Rad2Deg);
wheelTransform[0].Rotate(rotationAmount);
wheelTransform[1].Rotate(rotationAmount);
wheelTransform[2].Rotate(rotationAmount);
wheelTransform[3].Rotate(rotationAmount);
}
private var deviceAccelerometerSensitivity : float = 2;
function checkInput()
{
if (Application.platform == RuntimePlatform.IPhonePlayer ||(Application.platform == RuntimePlatform.Android))
{
accel = Input.acceleration * deviceAccelerometerSensitivity;
if(accel.x > deadZone || accel.x < -deadZone)
{
horizontal = accel.x;
}
else
{
horizontal = 0;
}
throttle =0;
for (var touch : Touch in Input.touches)
{
if(touch.position.x > Screen.width -Screen.width/3 && touch.position.y < Screen.height/3)
{
throttle = 1;
}
else if(touch.position.x < Screen.width -Screen.width/3 && touch.position.y > Screen.height/3)
{
throttle= -1;
}
}
}
else if (Application.platform == RuntimePlatform.WindowsEditor || RuntimePlatform.WindowsWebPlayer || RuntimePlatform.WindowsPlayer )
{
horizontal = Input.GetAxis("Horizontal");
throttle = Input.GetAxis("Vertical");
}
}
function carPhysicsUpdate()
{
myRight = carTransform.right;
velo = carRigidbody.velocity;
tempVEC = Vector3(velo.x,0,velo.z);
flatVelo = tempVEC;
dir = transform.TransformDirection(carFwd);
tempVEC = Vector3(dir.x,0,dir.z);
flatDir = Vector3.Normalize(tempVEC);
relativeVelocity = carTransform.InverseTransformDirection(flatVelo);
slideSpeed = Vector3.Dot(myRight,flatVelo);
mySpeed = flatVelo.magnitude;
rev = Mathf.Sign(Vector3.Dot(flatVelo,flatDir));
engineForce = ( flatDir * ( power * throttle ) * carMass);
actualTurn = horizontal;
if(rev < 0.1f)
{
actualTurn =- actualTurn;
}
turnVec =((( carUp * turnSpeed ) * actualTurn ) * carMass )* 800;
actualGrip = Mathf.Lerp(100, carGrip, mySpeed * 0.02);
imp = myRight * ( -slideSpeed * carMass * actualGrip);
}
function slowVelocity()
{
carRigidbody.AddForce(-flatVelo * 0.8);
}
function engineSound()
{
audio.pitch = 0.30 + mySpeed * 0.025;
if (mySpeed > 30)
{
audio.pitch = 0.25 + mySpeed * 0.015;
}
if (mySpeed > 40)
{
audio.pitch = 0.20 + mySpeed * 0.013;
}
if (mySpeed > 49)
{
audio.pitch = 0.15 + mySpeed * 0.011;
}
if ( audio.pitch > 2.0 )
{
audio.pitch = 2.0;
}
}
function FixedUpdate()
{
if(mySpeed < maxSpeed)
{
carRigidbody.AddForce( engineForce * Time.deltaTime);
if (mySpeed > maxSpeedToTurn)
{
carRigidbody.AddTorque ( turnVec * Time.deltaTime);
}
else if(mySpeed < maxSpeedToTurn)
{
return;
}
carRigidbody.AddForce( imp * Time.deltaTime );
}
}
Answer by Mysta · Feb 12, 2014 at 09:15 PM
I think(and I'm new myself) you'd just make a function that does maybe a raycast to check for the distance to ground being within a certain amount..
function IsGrounded() : boolean { //change or dynamically set the 0.1 to how far you want to allow your car to go up return Physics.Raycast(transform.position, -Vector3.up, 0.1); }
then before your car movement include an if(IsGrounded()) check.
im not sure how raycasts work i tried using it and it didnt work, would you be able to supply a piece of code i could test out.
Raycasts work as indicated. Quite simple actually, Physics.Raycast(startposition, direction, distance) So the transform.position gets the position of the transform this script is attached to(Your car), and then sends it in negative up(-Vector.up) so it sends it down. then if it goes .2 distance and doesn't find anything, it returns false, if it does hit the ground, it returns true.(You may have to work with the 0.2 distance a bit to get it right)
Should be as I posted, sorry forgot to write as code. Towards bottom put a function:
function IsGrounded() : boolean {
Physics.Raycast(transform.position, -Vector3.up, 0.2);
}
Try that, then in your Update function put if(IsGrounded()){ } around your checkInput();, like so:
function Update()
{
if(IsGrounded()){
checkInput();
}
carPhysicsUpdate();
}
Sorry i fail to understand where the IsGrounded function goes, what triggers that function to start?
You just put it in your script like any of your other functions, like slowVelocity and everything else. and you call it as my second code block shows. you could literally replace your "function Update()" block with that. $$anonymous$$ay lose some formatting but here it is:
but i tried doing that and nothing happens, it now wont let me drive.
Answer by nesis · Feb 13, 2014 at 01:36 AM
Presuming you're using Unity's built in WheelCollider for each wheel, Unity has a WheelHit class that will let you know what each wheel is hitting. You can use that to tell if your car should be able to keep driving.
An example of using WheelHit from the Unity reference manual:
// When attached to the WheelCollider, modifies tire friction based on
// static friction of the ground material.
function FixedUpdate() {
var hit : WheelHit;
var wheel : WheelCollider = GetComponent(WheelCollider);
if( wheel.GetGroundHit( hit ) ) {
wheel.forwardFriction.stiffness = hit.collider.material.staticFriction;
wheel.sidewaysFriction.stiffness = hit.collider.material.staticFriction;
}
}
For your need of detecting when the car is grounded, you could do something like this:
var wheelColliders : WheelCollider[]; //add each wheel's WheelCollider to this array in the Inspector
function IsGrounded() {
//Check if any wheelColliders[] element is touching
//the ground - if one is, then we're grounded so
//return true.
//You could have slightly more complex requirements
//here like needing at least 2 wheels touching the
//ground to drive.
for (var w : WheelCollider in wheelColliders) {
if (w.collider) return true; //at least one wheel is touching the ground
}
return false;
}
Im not using WheelColliders as they kept making my car unstable when turning.
An alternative would be making one trigger per wheel that sits at the wheel's base. You'd need to add a rigidbody to the trigger (so it can be moved and still get triggered) and make it follow the wheel during FixedUpdate() by using rigidbody.$$anonymous$$ovePosition(wheel.position + offsetFromWheelToBeAtItsBase);
.
Then for each wheel's trigger, add a script that during OnTriggerStay(Collider other)
will do myCarGameObject.Send$$anonymous$$essage("OnGrounded")
. This will call any methods called OnGrounded() in all scripts attached to the GameObject myCarGameObject.
In that OnGrounded() method, you can then set a flag (eg isGrounded = true
) to let that script know the car is currently grounded this frame. Then at the end of each frame, set isGrounded = false
.
Further to this, you'd need to add Physics.IgnoreCollision(wheelCollider,collider);
and perhaps Physics.IgnoreCollision(carCollider,collider);
in your wheel trigger script's Awake() method, so your trigger isn't always calling OnTriggerStay() when touching its wheel or the car. Alternatively, you could use layers to ignore collisions between the triggers and the car and / or its wheels.
Okay i've kind of got what your saying hear somethings are a bit hit and miss but would you be able to supply example code at all? that would be very helpful even more helpful if you could supply the code specialized to what i need. so for this script