Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

How in the world were the physics in F-zero X done?

Discussion in 'Scripting' started by Warrior1424, Oct 12, 2013.

  1. Warrior1424

    Warrior1424

    Joined:
    Sep 30, 2010
    Posts:
    984
    I was playing F-Zero X today and it boggles my mind how they achieved the car physics.
    The game was released in 1998 on the N64.
    I tried raycasting down from empty objects at different points on the vehicle and applying force as necessary, but it wasn't smooth and you could fly straight through the track even at lower speeds since it relied on rigidbodies.

    I'm trying to figure out how to re-create that magnetic car effect in Unity WITHOUT using the physics engine, as I don't think it would work well at high speeds and wouldn't be needed if the same effect was made on a N64.

    If you've never played it, here's a good example of the kinds of tracks in it


    I have this weird thing where I have to know how stuff works, and this is really bugging me.
    Any ideas?
     
  2. Jacksendary

    Jacksendary

    Joined:
    Jan 31, 2012
    Posts:
    408
    I have this weird Idea that it may work as a magnet, so certain objects are attracted to the game object, in this case the racing track and the cars/gliders are attracted. for the tracks that is only one sided I think they either use "Normal" physics or "directional" physics (only attracts objects from one direction, eg normal physics is from up to down.)

    Bear in mind this may be COMPLETELY wrong, but It's my Idea how it may work :)
     
  3. Nevak

    Nevak

    Joined:
    Sep 27, 2012
    Posts:
    20
    I tried this once just to see what I was able to come up with.

    My first approach was pretty much like the one you said, however it didn't turn out nicely so I decided to forget about physics engine stuff aswell.

    What I tried next was to use a single raycast from the center of the ship and in the direction of its local down axis. With the info from this raycast you can know the distance to the track and also the normal of the track's surface at your ship's location. Then I smoothly lerped the ship's rotation so that its up axis matched the direction of the track's normal.
    As for keeping the ship hovering you can try a similar approach: you check your current distance to the ground (given by the raycast) and compare it to your desired hovering height to adjust your height accordingly (probably using a lerp aswell so that it looks smooth).

    I will try to find my code to post it here to make things clearer.:)
     
  4. hpjohn

    hpjohn

    Joined:
    Aug 14, 2012
    Posts:
    2,190
    If you wanted a perfectly round track, the position of the car could probably be expressed as a distance away and angle relative to the spline that defines the road, to move the car forward, just sample the spline some distance further along, at the same relative angle
     
  5. Warrior1424

    Warrior1424

    Joined:
    Sep 30, 2010
    Posts:
    984
    I was thinking of that, but totally forgot about lerping it to make it smoother lol
    I'm gonna try to whip up some code using that idea.

    EDIT:
    "Then I smoothly lerped the ship's rotation so that its up axis matched the direction of the track's normal. "
    wat.
    How does one do such a thing?
     
    Last edited: Oct 12, 2013
  6. Nevak

    Nevak

    Joined:
    Sep 27, 2012
    Posts:
    20
    Ok, I managed to put some new code together since I couldn't find my old script. I rewrote it by heart so maybe it doesn't work 100% and might need some tweaking. Here it is:

    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4.  
    5. public class ShipTest : MonoBehaviour
    6. {
    7.     /*Ship handling parameters*/
    8.     public float fwd_accel = 100f;
    9.     public float fwd_max_speed = 200f;
    10.     public float brake_speed = 200f;
    11.     public float turn_speed = 50f;
    12.    
    13.     /*Auto adjust to track surface parameters*/
    14.     public float hover_height = 3f;     //Distance to keep from the ground
    15.     public float height_smooth = 10f;   //How fast the ship will readjust to "hover_height"
    16.     public float pitch_smooth = 5f;     //How fast the ship will adjust its rotation to match track normal
    17.    
    18.     /*We will use all this stuff later*/
    19.     private Vector3 prev_up;
    20.     public float yaw;
    21.     private float smooth_y;
    22.     private float current_speed;
    23.    
    24.  
    25.     void Update ()
    26.     {
    27.         /*Here we get user input to calculate the speed the ship will get*/
    28.         if (Input.GetKey(KeyCode.W))
    29.         {
    30.             /*Increase our current speed only if it is not greater than fwd_max_speed*/
    31.             current_speed += (current_speed >= fwd_max_speed) ? 0f : fwd_accel * Time.deltaTime;
    32.         }
    33.         else
    34.         {
    35.             if (current_speed > 0)
    36.             {
    37.                 /*The ship will slow down by itself if we dont accelerate*/
    38.                 current_speed -= brake_speed * Time.deltaTime ;
    39.             }
    40.             else
    41.             {
    42.                 current_speed = 0f;
    43.             }
    44.         }
    45.        
    46.         /*We get the user input and modifiy the direction the ship will face towards*/
    47.         yaw += turn_speed * Time.deltaTime * Input.GetAxis ("Horizontal");
    48.         /*We want to save our current transform.up vector so we can smoothly change it later*/
    49.         prev_up = transform.up;
    50.         /*Now we set all angles to zero except for the Y which corresponds to the Yaw*/
    51.         transform.rotation = Quaternion.Euler(0, yaw, 0);
    52.    
    53.         RaycastHit hit;
    54.         if (Physics.Raycast(transform.position, -prev_up, out hit))
    55.         {  
    56.             Debug.DrawLine (transform.position, hit.point);
    57.            
    58.             /*Here are the meat and potatoes: first we calculate the new up vector for the ship using lerp so that it is smoothed*/
    59.             Vector3 desired_up = Vector3.Lerp (prev_up, hit.normal, Time.deltaTime * pitch_smooth);
    60.             /*Then we get the angle that we have to rotate in quaternion format*/
    61.             Quaternion tilt = Quaternion.FromToRotation(transform.up, desired_up);
    62.             /*Now we apply it to the ship with the quaternion product property*/
    63.             transform.rotation = tilt * transform.rotation;
    64.            
    65.             /*Smoothly adjust our height*/
    66.             smooth_y = Mathf.Lerp (smooth_y, hover_height - hit.distance, Time.deltaTime * height_smooth);
    67.             transform.localPosition += prev_up * smooth_y;
    68.         }
    69.        
    70.         /*Finally we move the ship forward according to the speed we calculated before*/
    71.         transform.position += transform.forward * (current_speed * Time.deltaTime);
    72.     }
    73. }
    74.  
    You can attach it to a sphere or something to test it first. Try changing the parameters to get it the way you want.
     
  7. Warrior1424

    Warrior1424

    Joined:
    Sep 30, 2010
    Posts:
    984
    That works quite well, although it is important to note that all loops/twists must not be too sharp or the smoothing will cause the vehicle to fail to keep up.
    I'm gonna make a new test track that isn't so sharp and see how that works out.
    Other than that sick job dude!
     
  8. Nevak

    Nevak

    Joined:
    Sep 27, 2012
    Posts:
    20
    Yea, you are right. The track geometry needs to be smooth enough.

    I actually had a look at wip3out game for the PSX (quite similar to FZero) in an emulator so I could see it in wireframe mode. I found out that they used some kind of "Level of detail" trick so that only the section of the track you are in is actually smooth while the rest is really low poly. However I don't know how that could affect the AI ships since the geometry they "see" would not be smooth at all...

    It is amazing how simple these games look at first sight and how complex they can get in the inside. That's why I loved the idea :D
     
  9. Warrior1424

    Warrior1424

    Joined:
    Sep 30, 2010
    Posts:
    984
    I made a decent sized track, now there's the next issue:
    Barriers.
    How would the programming be done for the side collisions if the vehicle uses no colliders?
    Raycasts I guess?

    EDIT:
    New Issue,
    Vehicle doesn't function well upside down.
    Example:
    I have a tube track where you drive on the outside, and if I go down the side too far it falls off and snaps to the ground way down below.

    EDIT2:
    Also, if you watch the video in the original post, you can see that the vehicle is unaffected by the low poly tube it drives across. There has to be another way to do this.

    BTW Thanks Nevak for all the input :)
     
    Last edited: Oct 12, 2013
  10. Marsupilami

    Marsupilami

    Joined:
    Sep 22, 2013
    Posts:
    21
    http://answers.unity3d.com/questions/545844/make-player-character-stick-to-the-level-mesh-and.html

    See my video and code at the bottom of that url. Pretty much the same concept. Use Sphere cast of appropriate sizes 50-100 % radii of vehicle for single cast 25-50 % for multiple casts. You can also code a backup rotation position in case all of the casts fail.


    I would use colliders personally. Put barriers in a different layer or tag them and use the colliders or raycast to the sides and check the tag or layer and code the response accordingly.

    Handle your own gravity and have a sufficiently large downforce. Look at the link above to see a discussion and my example video and code for help. Using a larger sphere radius than the vehicle for a sphere cast can eliminate fall off issues.

    Using multiple sphere cast at locations similar to wheels and averaging them together then Lerp or Slerp with appropriate damping would handle just about any surface.

    You gotta remember that games like that took many, many hours of tweaking and trying to get things to work that way. I hope I helped :p



    Look at my video and see how using a sphere cast can make even angles equal to or sharper than -90° and 90° still work.
     
    Last edited: Oct 12, 2013
  11. Marsupilami

    Marsupilami

    Joined:
    Sep 22, 2013
    Posts:
    21
    Not really an F-Zero example but some hovercraft code I had laying around using 4 raycast at the corners used as hover thrusters. Then smoothing the rotation to the ground normal below the craft. One could combine the Magic Boots and hover code to create an F-Zero like game.

    http://www.youtube.com/watch?v=Rcz-3OkvNTA&feature=youtu.be


    Code (csharp):
    1.  
    2.         for(int i = 0; i < stabilizers.Length; i++)
    3.         {
    4.             if (Physics.Raycast(stabilizers[i].position, -stabilizers[i].up, out hit))
    5.             {
    6.                 thrustRatio = Mathf.Clamp(hit.distance, 0.1f, 5.0f);
    7.                 rigidbody.AddForceAtPosition(stabilizers[i].up * ((hoverForce + thrust) / thrustRatio), stabilizers[i].position);
    8.             }
    9.         }
    10.        
    11.         if (Physics.Raycast(transform.position, -transform.up, out hit))
    12.         {
    13.             thrustRatio = Mathf.Clamp(hit.distance, 0.1f, 5.0f);
    14.             rigidbody.AddForce(transform.up * ((hoverForce + thrust) / thrustRatio));
    15.             if (hit.distance < 2.0f)
    16.             {
    17.                 rotationVector = Quaternion.Lerp(transform.rotation, Quaternion.LookRotation(Vector3.Cross(transform.right, hit.normal), hit.normal), Time.deltaTime * 7.5f);
    18.             }
    19.             else
    20.             {
    21.                 rotationVector = Quaternion.Lerp(transform.rotation, Quaternion.LookRotation(Vector3.Cross(transform.right, hit.normal), hit.normal), Time.deltaTime * 3.5f);
    22.             }
    23.             transform.rotation = rotationVector;
    24.         }
    25.        
    26.         transform.rotation = new Quaternion(Mathf.Clamp(transform.rotation.x, -0.35f, 0.35f), transform.rotation.y, Mathf.Clamp(transform.rotation.z, -0.35f, 0.35f), transform.rotation.w);
    27.        
    28.         transform.rigidbody.velocity *= 0.99f;
    29.         transform.rigidbody.angularVelocity *= 0.99f;
    30.  
     
  12. Warrior1424

    Warrior1424

    Joined:
    Sep 30, 2010
    Posts:
    984
    So after playing F-Zero some more, I noticed that the front of the vehicle clipped through the track when starting a small loop.
    That gave me the idea of having an invisible cube running the code by Nevak without any rotation smoothing, and having the actual vehicle set it's position to the cube's and lerp the rotation.
    It almost worked.
    I put this code on the vehicle
    Code (csharp):
    1. #pragma strict
    2. var target : Transform;
    3. var speed = 15;
    4.  
    5.  
    6. function Update () {
    7. transform.position = target.transform.position;
    8. var anglex : float = Mathf.MoveTowardsAngle (transform.eulerAngles.x, target.eulerAngles.x, speed * Time.deltaTime);
    9. var angley : float = Mathf.MoveTowardsAngle (transform.eulerAngles.y, target.eulerAngles.y, speed * Time.deltaTime);
    10. var anglez : float = Mathf.MoveTowardsAngle (transform.eulerAngles.z, target.eulerAngles.z, speed * Time.deltaTime);
    11.  
    12. transform.eulerAngles = Vector3(anglex, angley, anglez);
    13. }
    I'm not too surprised it didn't work, as most things I code usually don't lol.

    I wanna use raycasting on the edges like in my initial attempt, but if you're going too fast and in one frame the vehicle is on the track, and in the next it is a little bit inside it, everything would goof up.
     
  13. rubixcube6

    rubixcube6

    Joined:
    Apr 17, 2013
    Posts:
    4
    I have tried so many different things trying to get this to work I'm convinced it neither uses unity physics or raycasts. It probably uses splines to keep the car on the track. I noticed that on tube track, it has 6 sides. I replicated that in unity, and used raycasting to stay on track, and the angles were way too sharp to work. Even when I was going at a slow enough speed to stay on, it was not a smooth effect going around it no matter how much I mess with the tilt/pitch smoothing. In F-Zero X, the car always stays at the same distance around the track so it has to be rotating around a central point. and for such a point to exist, to move along a track would mean it needs some sort of path or curve to follow.

    Movement

    If I were to move an object straight forward, it would travel straight along the z axis. If I wanted to move it along the spline then I would treat the distance of my point on the spline the same as I would the z axis. for normal track, your point on the spline also has a rotation so you would just use the x direction of your point as the ships local space x axis. For tube track, you would just rotate your ship around the spline.

    Walls and falling
    You could also set a min and max x distance for normal track. Then if you go beyond those limits, your ship would detach from the spline and fall. You could also simulate walls by not letting it go beyond your limits. No need for that on tube track. You could also set zones along the spline so that if you are going at a high enough speed you would detach from the spline like going off a ramp on normal track or a sharp curve on tube track. You could also re-attach to a spline by getting close enough to the track. you would just need to create an invisible mesh around your track to act as an attach zone.

    I haven't been able to test this theory yet, but I will soon.

    Here's how to make splines: http://catlikecoding.com/unity/tutorials/curves-and-splines/
     
    Last edited: Oct 4, 2015
  14. Polymorphik

    Polymorphik

    Joined:
    Jul 25, 2014
    Posts:
    599
    I used to play this game a lot...and by a lot I mean a lot....I am interested in giving this a try...
     
  15. tetriser016

    tetriser016

    Joined:
    Jan 3, 2014
    Posts:
    100
    Me also...

    Hoping to have a solution for this.
     
  16. Fajlworks

    Fajlworks

    Joined:
    Sep 8, 2014
    Posts:
    344
    Perhaps F-Zero X wasn't made using the same effect, but you can achieve similar thing with Curved World asset.
    https://www.assetstore.unity3d.com/en/#!/content/26165

    If I understood correctly, it is a shader, as it is stated in description "per mesh material shader effect". It basically curves your camera output, meaning your level is flat and you don't have to worry about those problems like curvature.

    I haven't tried it, but it seems the reviews are great. And it looks quite promising. Hope this helps!
     
  17. hippocoder

    hippocoder

    Digital Ape

    Joined:
    Apr 11, 2010
    Posts:
    29,723
    i'm pretty sure fzero keeps it simple with gravity always in the direction of the line segment, and distance to line segment + offset = floor height (for the tube bits).

    I really don't think it bothers with any form of raycasting on such limited hardware.
     
  18. bigmisterb

    bigmisterb

    Joined:
    Nov 6, 2010
    Posts:
    4,221
    I did this entire thing before based on another test game in unity. As a matter of fact it operated almost exactly like this. I did 3 rays from the base of the ship and calculated down on whichever rays collided with the floor. if none of them did, then I assumed you were out in space so I applied gravity. other than that gravity was the direction of the calculation of rays. It looked exactly like what you have there.
     
    Rujash likes this.
  19. tetriser016

    tetriser016

    Joined:
    Jan 3, 2014
    Posts:
    100
    Sorry for reviving this dead topic, but are there any appropriate solutions yet?
     
  20. Akli-LT

    Akli-LT

    Joined:
    May 29, 2017
    Posts:
    1
    I might be late to this. I am currently working on an anti gravity racer and figuring stuff out

    By interpolating between vertex normals you can get the movement pretty smooth.


    Now I am working on getting a constant height above the track

    Contstant_heigt.png
     
    Last edited: Feb 2, 2018
    LordPickaxe, tagergo, KOemxe and 5 others like this.
  21. tetriser016

    tetriser016

    Joined:
    Jan 3, 2014
    Posts:
    100
    It has been over a year, are there any snippets or examples?
     
  22. ahsan9898

    ahsan9898

    Joined:
    Oct 8, 2019
    Posts:
    6
    @Akli-LT
    Can you please share the script for "By interpolating between vertex normals you can get the movement pretty smooth."
    I'm trying to get the effect you have shown in the video but i'm unable to do that. I really appreciate it.
     
  23. Pattrigue

    Pattrigue

    Joined:
    Nov 24, 2018
    Posts:
    65
    After a week of trial and error, I finally figured it out.


    As mentioned by Akli-LT in his video's description, you have to use barycentric coordinate interpolation. Thankfully, Unity's RaycastHit.barycentricCoordinate documentation demonstrates how to do this: https://docs.unity3d.com/ScriptReference/RaycastHit-barycentricCoordinate.html

    I ended up using that to create the result seen in the video. I first encapsulated the code in a static class:
    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public static class BarycentricCoordinateInterpolator
    4. {
    5.     public static Vector3 GetInterpolatedNormal(RaycastHit hit)
    6.     {
    7.         MeshCollider meshCollider = hit.collider as MeshCollider;
    8.  
    9.         if (!meshCollider || !meshCollider.sharedMesh)
    10.         {
    11.             Debug.LogWarning("No MeshCollider attached to to the mesh!", hit.collider);
    12.             return Vector3.up;
    13.         }
    14.  
    15.         Mesh mesh = meshCollider.sharedMesh;
    16.         Vector3 normal = CalculateInterpolatedNormal(mesh, hit);
    17.      
    18.         return normal;
    19.     }
    20.  
    21.     private static Vector3 CalculateInterpolatedNormal(Mesh mesh, RaycastHit hit)
    22.     {
    23.         Vector3[] normals = mesh.normals;
    24.         int[] triangles = mesh.triangles;
    25.  
    26.         // Extract local space normals of the triangle we hit
    27.         Vector3 n0 = normals[triangles[hit.triangleIndex * 3 + 0]];
    28.         Vector3 n1 = normals[triangles[hit.triangleIndex * 3 + 1]];
    29.         Vector3 n2 = normals[triangles[hit.triangleIndex * 3 + 2]];
    30.  
    31.         // interpolate using the barycentric coordinate of the hitpoint
    32.         Vector3 baryCenter = hit.barycentricCoordinate;
    33.  
    34.         // Use barycentric coordinate to interpolate normal
    35.         Vector3 interpolatedNormal = n0 * baryCenter.x + n1 * baryCenter.y + n2 * baryCenter.z;
    36.         // normalize the interpolated normal
    37.         interpolatedNormal = interpolatedNormal.normalized;
    38.  
    39.         // Transform local space normals to world space
    40.         Transform hitTransform = hit.collider.transform;
    41.         interpolatedNormal = hitTransform.TransformDirection(interpolatedNormal);
    42.  
    43.         // Display with Debug.DrawLine
    44.         Debug.DrawRay(hit.point, interpolatedNormal);
    45.  
    46.         return interpolatedNormal;
    47.     }
    48. }
    Then I simply used this class' GetInterpolatedNormal method with my RaycastHit as input parameter, and converted the interpolated normal into a rotation quanternion like so:
    Code (CSharp):
    1. Vector3 interpolatedNormal = BarycentricCoordinateInterpolator.GetInterpolatedNormal(hit);
    2.  
    3. rigidbody.MoveRotation(Quaternion.FromToRotation(transform.up, interpolatedNormal) * rigidbody.rotation);
    And voila, it worked. I hope this is useful to someone out there.

    There's a few pitfalls along the way you should look out for:
    • Your road mesh must have a MeshCollider (obviously) and must have read/write enabled.
    • Your road mesh must be shaded smooth, not flat, otherwise the barycentric coordinate interpolation will not be as smooth as seen in the video.
    • You should use a rigidbody with gravity disabled and set its position based on the interpolated normal. Alternatively, you can use AddForce(), but I found that just made it much more complicated.
    • The rigidbody of your vehicle should have rotation frozen on the X and Z axis.
    If anyone's got any questions, feel free to ask and I'll try to get back to you.
     
    Last edited: Dec 7, 2020
    wmadwand, Fep310, LordPickaxe and 2 others like this.
  24. Heat_Advisory

    Heat_Advisory

    Joined:
    Oct 16, 2018
    Posts:
    2
    I had given up on this thread, and it's been saved by brilliance. Just to be clear is that two separate scripts and what do I attach them to? and how do you move your ship? I am a student learning how things work, patience is appreciated! :)
     
  25. Pattrigue

    Pattrigue

    Joined:
    Nov 24, 2018
    Posts:
    65
    The BarycentricCoordinateInterpolator class is just a static class with a method you call in your ship's movement class. BarycentricCoordinateInterpolator is not a MonoBehaviour and cannot be attached anywhere.

    The second code snippet I posted would be part of your ship's movement logic. I use a Rigidbody and set its velocity to its forward direction mulitplied by some speed value every frame in the video. I also raycast in the ship transform's downwards direction every frame and use the resulting RaycastHit in the BarycentricCoordinateInterpolator. You probably want to use the RaycastHit to set the position of the ship to a set distance above the ground as well.

    The movement implementation is up to you and depends on your specific game.
     
    Kurt-Dekker and Heat_Advisory like this.
  26. Heat_Advisory

    Heat_Advisory

    Joined:
    Oct 16, 2018
    Posts:
    2
    I have a movement script from a earlier post, can you show me where and how to add the second code snippet?
    Code (CSharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4. public class ShipTest : MonoBehaviour
    5. {
    6.     /*Ship handling parameters*/
    7.     public float fwd_accel = 100f;
    8.     public float fwd_max_speed = 200f;
    9.     public float brake_speed = 200f;
    10.     public float turn_speed = 50f;
    11.  
    12.     /*Auto adjust to track surface parameters*/
    13.     public float hover_height = 3f;     //Distance to keep from the ground
    14.     public float height_smooth = 10f;   //How fast the ship will readjust to "hover_height"
    15.     public float pitch_smooth = 5f;     //How fast the ship will adjust its rotation to match track normal
    16.  
    17.     /*We will use all this stuff later*/
    18.     private Vector3 prev_up;
    19.     public float yaw;
    20.     private float smooth_y;
    21.     private float current_speed;
    22.  
    23.     void Update ()
    24.     {
    25.         /*Here we get user input to calculate the speed the ship will get*/
    26.         if (Input.GetButton("Fire1"))
    27.         {
    28.             /*Increase our current speed only if it is not greater than fwd_max_speed*/
    29.             current_speed += (current_speed >= fwd_max_speed) ? 0f : fwd_accel * Time.deltaTime;
    30.         }
    31.         else
    32.         {
    33.             if (current_speed > 0)
    34.             {
    35.                 /*The ship will slow down by itself if we dont accelerate*/
    36.                 current_speed -= brake_speed * Time.deltaTime ;
    37.             }
    38.             else
    39.             {
    40.                 current_speed = 0f;
    41.             }
    42.         }
    43.      
    44.         /*We get the user input and modifiy the direction the ship will face towards*/
    45.         yaw += turn_speed * Time.deltaTime * Input.GetAxis ("Horizontal");
    46.         /*We want to save our current transform.up vector so we can smoothly change it later*/
    47.         prev_up = transform.up;
    48.         /*Now we set all angles to zero except for the Y which corresponds to the Yaw*/
    49.         transform.rotation = Quaternion.Euler(0, yaw, 0);
    50.  
    51.         RaycastHit hit;
    52.         if (Physics.Raycast(transform.position, -prev_up, out hit))
    53.         {
    54.             Debug.DrawLine (transform.position, hit.point);
    55.          
    56.             /*Here are the meat and potatoes: first we calculate the new up vector for the ship using lerp so that it is smoothed*/
    57.             Vector3 desired_up = Vector3.Lerp (prev_up, hit.normal, Time.deltaTime * pitch_smooth);
    58.             /*Then we get the angle that we have to rotate in quaternion format*/
    59.             Quaternion tilt = Quaternion.FromToRotation(transform.up, desired_up);
    60.             /*Now we apply it to the ship with the quaternion product property*/
    61.             transform.rotation = tilt * transform.rotation;
    62.          
    63.             /*Smoothly adjust our height*/
    64.             smooth_y = Mathf.Lerp (smooth_y, hover_height - hit.distance, Time.deltaTime * height_smooth);
    65.             transform.localPosition += prev_up * smooth_y;
    66.         }
    67.      
    68.         /*Finally we move the ship forward according to the speed we calculated before*/
    69.         transform.position += transform.forward * (current_speed * Time.deltaTime);
    70.     }
    71. }
    72.  
     
  27. Neto_Kokku

    Neto_Kokku

    Joined:
    Feb 15, 2018
    Posts:
    1,751
    Just be aware that when you access Mesh.vertices or Mesh.normals (or any other built-in Unity property which returns arrays) it allocates a copy of the array, which will quickly cause garbage collection spikes.
     
    Pattrigue likes this.
  28. LordPickaxe

    LordPickaxe

    Joined:
    Oct 10, 2020
    Posts:
    2
    Not wanting to be too late to a party, I've similarly been working on a carting/F-zero hybrid style game. So far this is what I've come up with to have a similar effect, although minus the hovering (they're cart-players after all):


    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class MagneticScript : MonoBehaviour
    6. {
    7.     public GameObject Car;  
    8.     public float magnetDistance = 100f;     //Maximum distance that a vehicle can be pulled by the track
    9.     public float magnetForce = 50f;     //Magnitude of the magnet force
    10.  
    11.     private int LayerTrack;
    12.  
    13.     void Update()
    14.     {
    15.         RaycastHit hit;
    16.  
    17.         LayerTrack = LayerMask.NameToLayer("GravTrack");
    18.  
    19.         Rigidbody rb = Car.GetComponent<Rigidbody>();
    20.  
    21.         if (Physics.Raycast(Car.transform.position, Car.transform.TransformDirection(Vector3.down), out hit, magnetDistance))
    22.         {
    23.             if (hit.transform.gameObject.layer == LayerTrack)
    24.                 {
    25.                 Debug.Log("YES");
    26.                 rb.useGravity = false;
    27.                 rb.AddForce(magnetForce * Car.transform.TransformDirection(Vector3.down));
    28.             }
    29.             else
    30.             {
    31.                 Debug.Log("Nah");
    32.                 rb.useGravity = true;
    33.             }
    34.         }
    35.    
    36.     }
    37.  
    38. }
    39.  
    40.  
    Note, the "Track" in question is an object with a layer named "GravTrack". A mesh collider works best.

    You could probably get the "hover" style with this script by simply moving the mesh out from the gameobject surface, then you could play with the rigidbody masses etc to get the movement and speed just right. It'll also allow for you to fall off and "die", a-la F-Zero X, if you go too fast.
     
    Last edited: Apr 9, 2021
  29. Vaani-del

    Vaani-del

    Joined:
    Oct 6, 2021
    Posts:
    1
    I know this is old, but this is how I did it

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. public class ShipTest : MonoBehaviour
    4. {
    5.     /*Ship handling parameters*/
    6.     public float fwd_accel = 100f;
    7.     public float fwd_max_speed = 200f;
    8.     public float brake_speed = 200f;
    9.     public float turn_speed = 50f;
    10.     /*Auto adjust to track surface parameters*/
    11.     public float hover_height = 3f;     //Distance to keep from the ground
    12.     public float height_smooth = 10f;   //How fast the ship will readjust to "hover_height"
    13.     public float pitch_smooth = 5f;     //How fast the ship will adjust its rotation to match track normal
    14.     /*We will use all this stuff later*/
    15.     private Vector3 prev_up;
    16.     public float yaw;
    17.     private float smooth_y;
    18.     private float current_speed;
    19.     void Update ()
    20.     {
    21.         /*Here we get user input to calculate the speed the ship will get*/
    22.         if (Input.GetButton("Fire1"))
    23.         {
    24.             /*Increase our current speed only if it is not greater than fwd_max_speed*/
    25.             current_speed += (current_speed >= fwd_max_speed) ? 0f : fwd_accel * Time.deltaTime;
    26.         }
    27.         else
    28.         {
    29.             if (current_speed > 0)
    30.             {
    31.                 /*The ship will slow down by itself if we dont accelerate*/
    32.                 current_speed -= brake_speed * Time.deltaTime ;
    33.             }
    34.             else
    35.             {
    36.                 current_speed = 0f;
    37.             }
    38.         }
    39.    
    40.         /*We get the user input and modifiy the direction the ship will face towards*/
    41.         yaw += turn_speed * Time.deltaTime * Input.GetAxis ("Horizontal");
    42.         /*We want to save our current transform.up vector so we can smoothly change it later*/
    43.         prev_up = transform.up;
    44.         /*Now we set all angles to zero except for the Y which corresponds to the Yaw*/
    45.         transform.rotation = Quaternion.Euler(0, yaw, 0);
    46.         RaycastHit hit;
    47.         if (Physics.Raycast(transform.position, -prev_up, out hit))
    48.         {
    49.             Debug.DrawLine (transform.position, hit.point);
    50.        
    51.             /*Here are the meat and potatoes: first we calculate the new up vector for the ship using lerp so that it is smoothed*/
    52.             Vector3 desired_up = Vector3.Lerp (prev_up, hit.normal, Time.deltaTime * pitch_smooth);
    53.             /*Then we get the angle that we have to rotate in quaternion format*/
    54.             Quaternion tilt = Quaternion.FromToRotation(transform.up, desired_up);
    55.             /*Now we apply it to the ship with the quaternion product property*/
    56.             transform.rotation = tilt * transform.rotation;
    57.  
    58.            Vector3 interpolatedNormal = BarycentricCoordinateInterpolator.GetInterpolatedNormal(hit);
    59.  
    60.            // Rotate to the track's normal
    61.            GetComponent<Rigidbody>().MoveRotation(Quaternion.FromToRotation(transform.up, interpolatedNormal) * GetComponent<Rigidbody>().rotation);
    62.        
    63.             /*Smoothly adjust our height*/
    64.             smooth_y = Mathf.Lerp (smooth_y, hover_height - hit.distance, Time.deltaTime * height_smooth);
    65.             transform.localPosition += prev_up * smooth_y;
    66.         }
    67.    
    68.         /*Finally we move the ship forward according to the speed we calculated before*/
    69.         transform.position += transform.forward * (current_speed * Time.deltaTime);
    70.     }
    71. }
     
  30. singlearrowgames

    singlearrowgames

    Joined:
    Oct 14, 2018
    Posts:
    5
    @Pattrigue,

    I tried using the script provided by Vanni-del. However the behavior was quite unexpected. I imported a torus model from blender and then added the shiptest script to a sphere in unity. But when I tried to move the sphere, the sphere had jittery movement and also moved away from the torus and kept going in the x direction.