- Home /
How to steer a hovercraft that aligns itself to a surface normal?
How can I do this? I've created a hover car, and I can move it around and that's cool. But if I move it over any sort of slope it will work until the center of the car is past the peak of the slope. Then the car flies into infinity or falls really slowly. I'm casting a ray from the car's transform straight down, and I'd like to know how to get the angle of the ground and match it on my hover car. Kind of like a magnetic system but it doesn't have to go upside down or on walls or anything. Basically I'm trying to achieve a Halo Ghost or Destiny Sparrow kind of a vehicle. It can flip over with normal physics but it floats. I can't use wheelcolliders with no mesh because the vehicle must also strafe. This is very perplexing and I can't seem to figure it out. Help is much appreciated.
I'm using AndyMane84's hover car script (I think that's the name iirc) but slightly modified. Here's the modified script.
var driveForce: float;
var hoverHeight: float;
var thrustForce: float;
var steerForce: float;
var steerPosZ: float;
var thrusters: Vector3[];
var maxVelocity: float;
var layerMask: LayerMask;
var enableControl: boolean = false;
static var driveMulti: float =1;
static var steerMulti: float =-1;
var steer: float;
var targetNormal : Vector3;
var hoverHit: RaycastHit;
var boostMultiplier : float;
var force : float:
var offHeight : float;
var mainBooster : GameObject;
var speedBooster : GameObject;
var mainCamera : GameObject;
private var motBlur : MotionBlur;
var target : Transform;
function Start ()
{
mainBooster.SetActive(true);
enableControl = true;
speedBooster.SetActive(false);
motBlur = mainCamera.GetComponent(MotionBlur);
}
function Update ()
{
var dwn = transform.TransformDirection (Vector3.down);
if(Physics.Raycast(transform.position, dwn, hoverHit))
{
if(hoverHit.distance >= offHeight)
{
enableControl = false;
rigidbody.drag = 0.0;
}
else
{
enableControl = true;
rigidbody.drag = 1.5;
}
}
if(Input.GetButton("Fire2"))
{
force = (driveForce * boostMultiplier);
mainBooster.SetActive (false);
speedBooster.SetActive (true);
motBlur.enabled = true;
}
else
{
force = driveForce;
mainBooster.SetActive (true);
speedBooster.SetActive (false);
motBlur.enabled = false;
}
}
function FixedUpdate () {
var hit: RaycastHit;
var normalhit :RaycastHit;
var addedhit :RaycastHit;
var addedforce : float;
if (Physics.Raycast(transform.position, -transform.up, normalhit)){
targetNormal = normalHit.normal;
}
if (enableControl){
steer = Input.GetAxis("Mouse X");
rigidbody.AddRelativeForce(-targetNormal.up * 1000);
var fwd: float = Input.GetAxis("Vertical");
rigidbody.AddForceAtPosition (transform.forward * (driveForce * fwd), transform.TransformPoint(Vector3.forward * 1));
var rgt: float = Input.GetAxis("Horizontal");
rigidbody.AddForceAtPosition (transform.forward * (driveForce * rgt));
}
transform.up = Vector3.Lerp(transform.up, targetNormal, Time.deltaTime * 1);
rigidbody.AddRelativeForce(transform.right * steer*steerForce);
rigidbody.AddForceAtPosition(transform.right * (steerForce * steer * steerMulti), transform.TransformPoint(Vector3.forward * steerPosZ));
if (Physics.Raycast(transform.position, -transform.up, addedhit))
{
addedforce = (hoverHeight - addedhit.distance);
if (addedforce <=0){
addedforce=0;
}
}
if (addedhit.distance <=2){
rigidbody.AddRelativeForce(targetNormal.up *(addedforce*20));}
rigidbody.AddRelativeForce(targetNormal.up * (thrustForce*(addedforce*addedforce)));
//Create an array for the vertical thrusters and apply a force to each one.
for (i = 0; i < thrusters.Length; i++) {
var wdThruster: Vector3 = transform.TransformPoint(thrusters[i]);
if (Physics.Raycast(wdThruster, -transform.up, hoverHit))
{
discrep = (hoverHeight - hoverHit.distance);
if (discrep <=0.1){discrep=0;}
var upVel: float = rigidbody.GetRelativePointVelocity(wdThruster).y;
rigidbody.AddForceAtPosition(transform.up * (thrustForce *discrep), wdThruster);
}
}
}
else
{
transform.up = Vector3.Lerp(transform.up, Vector3.up, Time.delta time * 1.5);
}
}
I solved the aligning issue, but now I cannot steer the vehicle. The issue is caused by the section that aligns the vehicle to the ground (transform.up = Vector3.Lerp(transform.up, targetNormal, Time.deltaTime * 1);). Commenting out that line fixes the issue, because it constantly sets the up axis to zero. How can I match the rotation, but allow the y axis to change?
I just tried transform.LookAt (Vector3 (target.position.x, transform.position.y, target.position.z) and it kind of works, but the vehicle is no longer properly aligned. It won't align to the terrain. Works great on always perfectly flat areas but thats not what I need. I've also already tried directly changing the transform.rotation but as stated before, its being reset to every time FixedUpdate is called.
Use LookAt with transform.position + steerdirection as first and surface normal as second parameter
So for my code I would need to make a new Vector3 for the steer direction right?
EDIT: This partially works, where target is an object childed to my main camera. Problem is its super strict and doesn't really allow for rotation to align (but it sort of does).
transform.LookAt(transform.position + target.TransformDirection(transform.forward), targetNormal);
transform.position + target.TransformDirection(transform.forward) that seems like an odd way to define "steer direction"- if you are looking for a direction towards the target, from the hovercraft, I would have used:
(target.transform.position - hovercraft.transform.position).Normalized()
Well I'm using an empty dummy GO as a target. The dummy's y position is always relative to the hovercrafts position. When I rotate the parent of the dummy GO with a mouse look scriptscript locked to the x axis, the hovercraft rotates like I want, but when it needs to align to any floor below it, it refuses to actually align when on any slope greater than about 47 degrees. I'll try your method in a bit.
EDIT: Using the above stated method gives an error exception - $$anonymous$$issing$$anonymous$$ethodException: UnityEngine.Vector3.Normalized
Simply put, I want only roll and pitch to match the floors normals, while only yaw steers.
oops, Normalized is not a function, it's a property - no parens. Normalize() is the function version (which modifies the vector it operates on).
y axis steers: I have not even complied the below (so it might have more typos), but it might be closer to what you are looking for.
steerDirection = target.transform.position -transform.position;
float angleOnZXplane = $$anonymous$$athf.ATan(steerDirection.x,steerDirection.z);
transform.rotation = Quaternion.AngleAxis(angleOnZXplane,surfaceNormal);