Anti gravity racing: the sequel

OK, I think this is sufficiently different to start a new thread.
How would one make an anti-gravity racing craft?
I’ve been trying (many) different ideas, and so far none of them work. My current concept was aligning the craft to the face normal directly under it with Quaternion.FromToRotation. Only it aligned it perfectly with the track, making turning impossible. Any idea how to do that or am I barking up the wrong tree again? :roll:

Thanks in advance…

Could you align it along only the axises you need?

And what about this idea. Have a gameObject with a collider on it sitting on the ground, rolling along. Its invisible. Now attach your antigravity object to that one with some sort of spring joint, so that the craft moves with physics, and is off the ground? You could do quite well with that, as it would allow you to have the craft’s rotation respond to inertia, if you wanted to.

Use a variable to keep track of what the rotation should be, and use it to add another rotation on top of the one that you get from Quaternion.FromToRotation.

–Eric

That was the first thing I thought of, but when transform.up is something like (0.6,0,0.4) that idea doesn’t work so well.

I was thinking about that, similar to what was mentioned above. Maybe it’s just the 100 degree heat, but I haven’t been able to figure out how to do that yet. The problem, as mentioned above, is figuring out how to control rotation on one of its axes when it’s doing all sorts of weird things. It is anti gravity, after all. Is there a way to convert a global Euler rotation into a local, object-based Euler rotation that I’m overlooking? Should I just scrap this and go back to a more primitive approach?
Sigh… this shouldn’t be rocket science… I can’t think when it’s hot!

Send me some of your excess heat plz. In return I will send you some unseasonable cold kthx.

private var myRotation : float = 0;
var rotateSpeed : float = 50;

function Update () {
	// Wacky quaternion rotation stuff here
	myRotation += rotateSpeed * Time.deltaTime * Input.GetAxis("Horizontal");
	transform.Rotate(Vector3.up * myRotation);
}

Something like.

–Eric

I wish I could. Trust me, I wish. We all do :lol:

Thanks for the script, but one of us doesn’t get it. Either I’m out of it, or Vector3.up would be global. Would it still work turned on its side? Flipped? While going through a corkscrew? It needs to.

But it did give me an idea. The surface normal isn’t doing it, it’s the Quaternion function. That means it should be a global orientation. With that, I could store the… uh, “turn” globally, then re-override the Quat every frame. Still feels flawed, though.

Must be the heat. :wink: Vector3.up is not global in this context.

transform.Rotate(Vector3.up * myRotation);

is the same as

transform.Rotate(Vector3.up * myRotation, Space.Self);

If you want Vector3.up to be global (which you don’t in this case, but if you did for some reason), then you’d use

transform.Rotate(Vector3.up * myRotation, Space.World);

–Eric

Nope. Still nothing.

The problem seems to be just that the Quaternion.FromToRotation controls the entire rotation, making the craft face in a certain way. There should be a way to remove it, but I don’t even know what some of these functions even do, let alone how they work.
For what it’s worth, I used a modified version of this example code

transform.rotation = Quaternion.FromToRotation (Vector3.up, surfaceNormal);

The object faces correctly, but it keeps resetting the yaw to 0. It’s the “wacky quaternion stuff” that’s giving me the problems, not the simple vectors. (and now that you mention it, I remember that transform.Rotate works differently from transform.rotation)

Question, how real are you trying to make the physics of the anti-grav race vehicle? If that’s not critical, then why not have your vehicle follow your track (rotation and all) much the same way the smooth follow camera script works? ie, try and maintain a static distance, but allow for some degree of floating.

Thanks,

Galen

I don’t think that’s practical…
Think of “Wipeout”, that’s what I’m aiming for.
Another question, how could one get an object to only turn relative to itself while taking input from a global rotation? Basically, only set the pitch/roll, but the surface normal I need is global. I’m thinking of rotating it, using the global stuff to mess with it, then rotate it back and apply it to the transform.
Hmmm…

I’m working on a F-Zero type racer. My cars hover over the ground.

I used the FSP walker and tweaked it a bit.
Instead of “move” I used velocity ++
A character controller. Making the “green orb” bigger so the car looks like its 2-3 above the ground.

My cars “look like” there hovering. As for the AI making the waypoints 2-3 above the terrain gives “the elution of hovering”

I read some of your threads (Omega) on the player standings. Ever managed to solve that issue. If so can you point the way? :twisted:

I just checked my email and BAM! a reply to a year old thread. Ain’t forums great? :wink:

Now, to actually address the topic, that’s an interesting idea with the character controllers. I’m not sure how it would handle what I was doing, though. I wanted a realistic bouncyness/buoyancy to the vehicles (a la Wipeout) that I thought was best done, or at least most simply done, with raycasts. But I’ve always had trouble with the counter-intutive design that game engines like.

I never managed to solve the place issue in code, as it was a project for school that was completed (enough) and, well, you know the rest. I did, however, figure out how to do it in general. Don’t quite remember, but it went something like this:

there is an array of racer positions (or a way to get one)
    racer data needs [lap, waypoint, distanceToTheNextWaypoint] (as well as some way to figure out who's who)
using a simple sorting method, sort the racers based on lap
    if there are any duplicates, then sort the duplicates by their next waypoint
        if there are any duplicates there, then sort those duplicates by the distances to the next waypoint (calculated by some vector math that I don't remember. But avoid simply comparing distances from the waypoint, as how far they are to the side is irrelevant)

Well, there you are. I think the duplicate resorting was the most important part, though, so I’m glad I remembered it. Hope it helps.

There is some code for an anti-gravity vehicle in this thread, which uses the physics engine. That was also for a Wipeout/F-Zero type of thing.

Hey Omega, glad to see another WipEout fan around here! I am currently working on the exact same as you were however I haven’t gotten as far as z-axis corkscrews yet. I will give andeeee’s link a look but I’d still like to ask if there is a way to get the RaycastHit.collider.gameObject’s face/surface height (Y-value)? You know, for hills and banks…

It’s been a while, but everything I needed came from the RaycastHit. I think what you’re looking for is RaycastHit.point.y

Hope it helps

I’ve tried that already but thanks anyway. :slight_smile:

Hmmm… I had a feeling that that was too obvious to be right, but the only thing I can think of for “surface height” is the y position of where the raycast hit the surface. What are you using it for?

I have a race track that has an incline and an upper level and I want to consistently maintain a distance above the track (i.e. hover).

OK, what I did was use RaycastHit.distance to get the distance from the track, then repel based on the distance. The direction to move in is the normal of the surface beneath the vehicle (RaycastHit.normal) although you might want to do some interpolation with surrounding raycasts to avoid abrupt jerks (the track’s geometry is nowhere as smooth as it looks).

Hope it helps. This would be sooooo much clearer with pictures…

PS: I have a feeling that RaycastHit.distance uses Vector3.magnitude which uses a square root. If you’re paranoid about performance, the Vector3.sqrMagnitude trick could shave off a few microseconds.

I’m just trying to get the darn thing to work, I’ll worry about performance later. I do have my own thread but here is some code I have been toying with:

public class Antigrav : MonoBehaviour {
	public float agField, floatHeight;
	public Vector3 buoyancyCentreOffset1, buoyancyCentreOffset2, buoyancyCentreOffset3, buoyancyCentreOffset4;
	public float bounceDamp; 
	public float AGfactor = 2;

	public RaycastHit hit;

	void Update(){

		if (Physics.Raycast(transform.position, -Vector3.up, out hit, /*floatHeight*/Mathf.Infinity)) {

			Debug.Log(hit.collider.tag + " within " + hit.distance + " meters.");
			
			if (hit.collider.tag == "Track"){
	
				agField = (float)hit.distance + (float)hit.transform.localPosition.y;
			
				Vector3 actionPoint1 = transform.position + transform.TransformDirection(buoyancyCentreOffset1);
				Vector3 actionPoint2 = transform.position + transform.TransformDirection(buoyancyCentreOffset2);
				Vector3 actionPoint3 = transform.position + transform.TransformDirection(buoyancyCentreOffset3);
				Vector3 actionPoint4 = transform.position + transform.TransformDirection(buoyancyCentreOffset4);
				float forceFactor1 = AGfactor - ((actionPoint1.y - agField) / floatHeight) / (float)hit.distance;
				float forceFactor2 = AGfactor - ((actionPoint2.y - agField) / floatHeight) / (float)hit.distance;
				float forceFactor3 = AGfactor - ((actionPoint3.y - agField) / floatHeight) / (float)hit.distance;
				float forceFactor4 = AGfactor - ((actionPoint4.y - agField) / floatHeight) / (float)hit.distance;
				
				if (forceFactor1 > 0f) {
					Vector3 uplift1 = -Physics.gravity * (forceFactor1 - rigidbody./*angularV*/velocity.x * bounceDamp);
					rigidbody.AddForceAtPosition(uplift1, actionPoint1);
				}
				if (forceFactor2 > 0f){
					Vector3 uplift2 = -Physics.gravity * (forceFactor2 - rigidbody.velocity.x * bounceDamp);
					rigidbody.AddForceAtPosition(uplift2, actionPoint2);
				}
				if (forceFactor3 > 0f){
					Vector3 uplift3 = -Physics.gravity * (forceFactor3 - rigidbody./*angularV*/velocity.x * bounceDamp);
					rigidbody.AddForceAtPosition(uplift3, actionPoint3);
				}
				if (forceFactor4 > 0f){
					Vector3 uplift4 = -Physics.gravity * (forceFactor4 - rigidbody./*angularV*/velocity.x * bounceDamp);
					rigidbody.AddForceAtPosition(uplift4, actionPoint4);
				}
			}
		}
	}
}

I still think this, although sloppy, is the better way to go. I get a consistent hovering effect but it seems to occur at one point - WipEout 1 - 3 physics. I am aiming for WipEout PurE or HD -like floaty physics. I had a revelation in the shower (where all great ideas come from :P) yesterday after reading this thread and XYZ’s - I need to place multiple raycasts on my craft. My code supports this so I decided to incorporate that idea as well as dramatically clean up my code - this is what I’ve got:

public class Antigrav : MonoBehaviour {
	// Publics
	public float agField, floatHeight;
	public Vector3 buoyancyCentreOffset1, buoyancyCentreOffset2, buoyancyCentreOffset3, buoyancyCentreOffset4;
	public float bounceDamp; // should be between 0 and 1 
	public float AGfactor = 1;
	public RaycastHit hit1;
	public RaycastHit hit2;
	public RaycastHit hit3;
	public RaycastHit hit4;
	
	// Privates
	private bool ray1;
	private bool ray2;
	private bool ray3;
	private bool ray4;
	
	private float forceFactor1;  
	private float forceFactor2; 
	private float forceFactor3; 
	private float forceFactor4; //= AGfactor - ((actionPoint4.y - agField) / floatHeight) / (float)hit.distance;
	
	//others
	Vector3 actionPoint1;
	Vector3 actionPoint2; 
	Vector3 actionPoint3; 
	Vector3 actionPoint4; 
				

	void Update(){
		
		actionPoint1  = transform.position + transform.TransformDirection(buoyancyCentreOffset1);
		actionPoint2  = transform.position + transform.TransformDirection(buoyancyCentreOffset1);
		actionPoint3  = transform.position + transform.TransformDirection(buoyancyCentreOffset1);
		actionPoint4  = transform.position + transform.TransformDirection(buoyancyCentreOffset1);
		
		ray1 = Physics.Raycast(actionPoint1, -Vector3.up, out hit1, Mathf.Infinity);
		ray2 = Physics.Raycast(actionPoint2, -Vector3.up, out hit2, Mathf.Infinity);
		ray3 = Physics.Raycast(actionPoint3, -Vector3.up, out hit3, Mathf.Infinity);
		ray4 = Physics.Raycast(actionPoint4, -Vector3.up, out hit4, Mathf.Infinity);
		
		// Determine object below and distance from said object relative to each ray source/action point.
		// Set agField and calculate forceFactor.
		if (ray1) {
			Debug.Log(hit1.collider.tag + " within " + hit1.distance + " meters of actionPoint1.");
			
			if (hit1.collider.tag == "Track"){
				agField = (float)hit1.distance * floatHeight;
				forceFactor1 = AGfactor - ((actionPoint1.y - agField) / floatHeight);
			}
		}
		if (ray2) {
			Debug.Log(hit2.collider.tag + " within " + hit2.distance + " meters of actionPoint2.");
			
			if (hit2.collider.tag == "Track"){
				agField = (float)hit2.distance;
				forceFactor2 = AGfactor - ((actionPoint2.y - agField) / floatHeight);
			}
		}
		if (ray3) {
			Debug.Log(hit3.collider.tag + " within " + hit3.distance + " meters of actionPoint3.");
			
			if (hit3.collider.tag == "Track"){
				agField = (float)hit3.distance;
				forceFactor3 = AGfactor - ((actionPoint3.y - agField) / floatHeight);
			}
		}
		if (ray4) {
			Debug.Log(hit4.collider.tag + " within " + hit4.distance + " meters of actionPoint4.");
			
			if (hit4.collider.tag == "Track"){
				agField = (float)hit4.distance;
				forceFactor4 = AGfactor - ((actionPoint4.y - agField) / floatHeight) / (float)hit4.distance;
			}
		}
		
		// Apply force
		if (forceFactor1 > 0f) {
			Vector3 uplift1 = -Physics.gravity * (forceFactor1 - rigidbody./*angularV*/velocity.y * bounceDamp);
			rigidbody.AddForceAtPosition(uplift1, actionPoint1);
		}
		if (forceFactor2 > 0f){
			Vector3 uplift2 = -Physics.gravity * (forceFactor2 - rigidbody.velocity.y * bounceDamp);
			rigidbody.AddForceAtPosition(uplift2, actionPoint2);
		}
		if (forceFactor3 > 0f){
			Vector3 uplift3 = -Physics.gravity * (forceFactor3 - rigidbody./*angularV*/velocity.y * bounceDamp);
			rigidbody.AddForceAtPosition(uplift3, actionPoint3);
		}
		if (forceFactor4 > 0f){
			Vector3 uplift4 = -Physics.gravity * (forceFactor4 - rigidbody./*angularV*/velocity.y * bounceDamp);
			rigidbody.AddForceAtPosition(uplift4, actionPoint4);
		}
		
	}
}

This code, surprisingly, doesn’t work the way I had hoped. It never applies enough uplift and when I manually drag it off the track, it dips it’s nose down and does a face/nose plant into the track - I am confused with this one… :?