• Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
  • Asset Store
  • Get Unity

UNITY ACCOUNT

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account
  • Blog
  • Forums
  • Answers
  • Evangelists
  • User Groups
  • Beta Program
  • Advisory Panel

Navigation

  • Home
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
    • Blog
    • Forums
    • Answers
    • Evangelists
    • User Groups
    • Beta Program
    • Advisory Panel

Unity account

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account

Language

  • Chinese
  • Spanish
  • Japanese
  • Korean
  • Portuguese
  • Ask a question
  • Spaces
    • Default
    • Help Room
    • META
    • Moderators
    • Topics
    • Questions
    • Users
    • Badges
  • Home /
avatar image
8
Question by SirGive · Sep 19, 2011 at 03:11 AM · rotationnormalalignment

Orient vehicle to ground normal (terrain hugging)

So the problem I'm having is trying to orient a vehicle to the ground based on a raycast. The raycast works, the movement works, and aligning the normal of the ground to the transform.up works. However, together these don't work. It won't rotate.

alt text

I think the problem is that aligning the transform.up = hit.normal overrides any rotation. My question would be, how exactly do I form an algorithm that will translate rotation into the normal. I figure if I can rotate the normal before I set it to the up vector that rotation should be possible. And I don't want it to rotate and snap back, which I found out how to do.

Here is my code (C#):

 if (Physics.Raycast(transform.position, -transform.up, out hit))
     {
         Quaternion grndTilt = Quaternion.FromToRotation(transform.up, hit.normal);
         transform.rotation = Quaternion.Euler(0, Input.GetAxis("Horizontal") * turnSpeed * 100 * Time.deltaTime, 0) * grndTilt;
         //transform.up = hit.normal;    
     }
     Vector3 movDir;
     //transform.Rotate(0, Input.GetAxis("Horizontal") * turnSpeed * Time.deltaTime,0);
     movDir = transform.forward*Input.GetAxis("Vertical")*speed;
     // moves the character in horizontal direction
     controller.Move(movDir*Time.deltaTime-Vector3.up*0.1f);

I've been all over the internet, and nothing seems to be working!

EDIT:

Removed the code because it was incorrect. The solution:

Two parts - a composite vector from each of the corners to give you the direction to orient (my original issue) - and separating the mesh to a sub gameobject. Keep your controller as the root (or even another sub object). Very clean movement. Forgive me for no code example. :)

instruction-1.png (11.9 kB)
Comment
Add comment · Show 2
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image picobots · Apr 05, 2012 at 07:17 PM 0
Share

What is "back" in the code you ended up using?

avatar image SirGive · Apr 06, 2012 at 02:47 PM 0
Share

the back and front are offset variables to move the raycasts towards the front of the tank and the back. These are float data types.

An easy solution is in two parts: - a composite vector from each of the corners to give you the direction to orient (my original issue) - and separating the mesh to a sub gameobject. $$anonymous$$eep your controller as the root (or even another sub object)

Was a very clean movement

7 Replies

· Add your reply
  • Sort: 
avatar image
1

Answer by Owen-Reynolds · Sep 20, 2011 at 12:53 AM

For the rotation, it snaps back because your code says the rotation is the current value of the arrow keys. Of course when you let go it snaps to 0. Use the arrows to add to rotation (the answer above.)

For raycast wiggling, use -Vector3.up as the aim dir instead of -transform.up. The latter is down from our tilt. So, tilting changes the raycast dir, which hits the ground somewhere else, which gets a diff normal, which makes you tilt a different dir... . You'll wiggle any time you stand still on a curved surface. The effect is worse as your (0,0,0) gets further off the ground. -Vector3.up is always straight down, which won't change as we tilt.

Setting transform.up wants to override any previous tilt you have. It's actually doing a bit of math to figure out how to get up to point that way. Using *grndTilt (if it stands for the tilt from "no change" to "ground normal" changes your up while keeping everything the same.

To apply the grndTilt, the order multiplying quaternions matters, and it's usually backwards to how you think. Try flipping *grndtilt to in front instead of in back (that's in the answer above, but is easy to miss, since we are so used to thinking a*b and b*a are the same.) Or, you can think of doing the tilt as: transform.rotation = tilt*transform.rotation;

Comment
Add comment · Show 1 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image SirGive · Sep 20, 2011 at 01:54 AM 0
Share

I didn't even think about using Vector3.up! I noticed that it was co$$anonymous$$g down from the tilt, but it never cross my $$anonymous$$d that it could be a problem. Thanks. And I have forgotten about the order of rotations. facepalm I need to brush up on my math. I did get it working, but I used 2 raycasts to be much more simple. Thanks for that.

avatar image
17

Answer by aldonaletto · Sep 19, 2011 at 11:11 AM

I had the same problem: assigning transform.up seems to be equivalent to transform.rotation = Quaternion.FromToRotation(Vector3.up, newNormal). The solution for me was to keep a "compass" variable which showed the current rotation angle from forward: thus I "rotated" this angle and applied to the object using Euler:

float curDir = 0f; // compass indicating direction
float vertSpeed = 0f; // vertical speed (see note)
...
void Update(){
    float turn = Input.GetAxis("Horizontal") * turnSpeed * 100 * Time.deltaTime,
    curDir = (curDir + turn) % 360; // rotate angle modulo 360 according to input
    if (Physics.Raycast(transform.position, -transform.up, out hit))
    {
        Quaternion grndTilt = Quaternion.FromToRotation(transform.up, hit.normal);
        transform.rotation = grndTilt * Quaternion.Euler(0, curDir, 0);
    }
    Vector3 movDir;
    movDir = transform.forward*Input.GetAxis("Vertical")*speed;
    // moves the character in horizontal direction (gravity changed!)
    if (controller.isGrounded) vertSpeed = 0; // zero v speed when grounded
    vertSpeed -= 9.8f * Time.deltaTime; // apply gravity
    movDir.y = vertSpeed; // keep the current vert speed
    controller.Move(movDir*Time.deltaTime);
    ...
An alternative way to do that is using the OnControllerColliderHit event to get the normal - but only if its Y coordinate is > 0.3 (or some other suitable value) or else the car can stick to walls or to other vehicles during lateral collisions!
NOTE: I changed the way gravity is applied - it gives a better behaviour when "flying" after a hill. Discard these changes if you don't want them - the rotation thing is at the beginning, and has nothing to do with gravity.

EDITED: There was an error in the script above: the rotation was being calculated from transform.up to the normal (it should be Vector3.up to normal) what was causing the crazy instability.
In my tests, I also noticed that the vehicle was following the terrain normal immediately, what was producing a strange behaviour. I added a new variable - curNormal - which smoothly followed the terrain normal using Lerp, and also was used in the Raycast to avoid other instabilities. The result is very convincent - hope you like it too:

 float curDir = 0f; // compass indicating direction
 float vertSpeed = 0f; // vertical speed (see note)
 Vector3 curNormal = Vector3.up; // smoothed terrain normal
 
 void Update(){
     float turn = Input.GetAxis("Horizontal") * turnSpeed * 100 * Time.deltaTime;
     curDir = (curDir + turn) % 360; // rotate angle modulo 360 according to input
     RaycastHit hit;
     if (Physics.Raycast(transform.position, -curNormal, out hit)){
         curNormal = Vector3.Lerp(curNormal, hit.normal, 4*Time.deltaTime);
         Quaternion grndTilt = Quaternion.FromToRotation(Vector3.up, curNormal);
         transform.rotation = grndTilt * Quaternion.Euler(0, curDir, 0);
     }
     Vector3 movDir;
     movDir = transform.forward*Input.GetAxis("Vertical")*speed;
     // moves the character in horizontal direction (gravity changed!)
     if (controller.isGrounded) vertSpeed = 0; // zero v speed when grounded
     vertSpeed -= 9.8f * Time.deltaTime; // apply gravity
     movDir.y = vertSpeed; // keep the current vert speed
     controller.Move(movDir*Time.deltaTime);
 }

JAVASCRIPT VERSION:

 private var curDir: float = 0f; // compass indicating direction
 private var vertSpeed: float = 0f; // vertical speed (see note)
 private var curNormal = Vector3.up; // smoothed terrain normal
 
 function Update(){
     var turn = Input.GetAxis("Horizontal") * turnSpeed * 100 * Time.deltaTime;
     curDir = (curDir + turn) % 360; // rotate angle modulo 360 according to input
     var hit: RaycastHit;
     if (Physics.Raycast(transform.position, -curNormal, hit)){
         curNormal = Vector3.Lerp(curNormal, hit.normal, 4*Time.deltaTime);
         var grndTilt = Quaternion.FromToRotation(Vector3.up, curNormal);
         transform.rotation = grndTilt * Quaternion.Euler(0, curDir, 0);
     }
     var movDir = transform.forward*Input.GetAxis("Vertical")*speed;
     // moves the character in horizontal direction (gravity changed!)
     if (controller.isGrounded) vertSpeed = 0; // zero v speed when grounded
     vertSpeed -= 9.8f * Time.deltaTime; // apply gravity
     movDir.y = vertSpeed; // keep the current vert speed
     controller.Move(movDir*Time.deltaTime);
 }
Comment
Add comment · Show 14 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image SirGive · Sep 19, 2011 at 11:54 PM 0
Share

Hmm... not working for me. $$anonymous$$y vehicle just rotates and then snaps back. Also rotation is really if-y. When rotating it tends to freak out.

I think i'm more so looking for an algorithm to set transfom.up ins$$anonymous$$d of the whole rotation. Could be wrong though.

avatar image aldonaletto · Sep 20, 2011 at 12:20 AM 0
Share

I'll check this and return asap.

avatar image aldonaletto · Sep 20, 2011 at 01:14 AM 0
Share

I found the error and changed the script - now it's tested and following the terrain without any instability. Give a try to the new version above.

avatar image SirGive · Sep 20, 2011 at 01:56 AM 3
Share

That works extremely well. I was actually able to achieve the exact same thing with two raycasts. Funny thing is, it required me to use transform.position and transform.forward. But yours does the charm. I'm not sure which has less overhead, so I'll make sure to check. Here is what I did:

RaycastHit hitA;

 if (Physics.Raycast(transform.position + transform.forward * front, -transform.up, out hitA))
 {
     RaycastHit hitB;
     if (Physics.Raycast(transform.position - transform.forward * back, -transform.up, out hitB))
         transform.forward = hitA.point - hitB.point; // check A to B vector and align forward
avatar image tool55 · Apr 18, 2013 at 05:52 AM 1
Share

Posted it as a new question with a link. Thanks, SirGive.

Show more comments
  • ‹
  • 1
  • 2

Your answer

Hint: You can notify a user about this post by typing @username

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Welcome to Unity Answers

If you’re new to Unity Answers, please check our User Guide to help you navigate through our website and refer to our FAQ for more information.

Before posting, make sure to check out our Knowledge Base for commonly asked Unity questions.

Check our Moderator Guidelines if you’re a new moderator and want to work together in an effort to improve Unity Answers and support our users.

Follow this Question

Answers Answers and Comments

18 People are following this question.

avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image

Related Questions

Aligning Y-Axis rotation of stretched gameobject to specific angles while retaining initial position 0 Answers

Make cube look at player relative to a defined rotation. 0 Answers

Cant understand why object rotates when setting transform.up to normal 2 Answers

How can I obtain the overall rotation of multiple selected gameobjects in the unity editor? 2 Answers

How to handler rotation of a Gameobject while aligning it with normals 1 Answer


Enterprise
Social Q&A

Social
Subscribe on YouTube social-youtube Follow on LinkedIn social-linkedin Follow on Twitter social-twitter Follow on Facebook social-facebook Follow on Instagram social-instagram

Footer

  • Purchase
    • Products
    • Subscription
    • Asset Store
    • Unity Gear
    • Resellers
  • Education
    • Students
    • Educators
    • Certification
    • Learn
    • Center of Excellence
  • Download
    • Unity
    • Beta Program
  • Unity Labs
    • Labs
    • Publications
  • Resources
    • Learn platform
    • Community
    • Documentation
    • Unity QA
    • FAQ
    • Services Status
    • Connect
  • About Unity
    • About Us
    • Blog
    • Events
    • Careers
    • Contact
    • Press
    • Partners
    • Affiliates
    • Security
Copyright © 2020 Unity Technologies
  • Legal
  • Privacy Policy
  • Cookies
  • Do Not Sell My Personal Information
  • Cookies Settings
"Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere (more info here). Other names or brands are trademarks of their respective owners.
  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Spaces
  • Default
  • Help Room
  • META
  • Moderators
  • Explore
  • Topics
  • Questions
  • Users
  • Badges