Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 13 Next capture
2021 2022 2023
1 capture
13 Jun 22 - 13 Jun 22
sparklines
Close Help
  • 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
0
Question by AXXE78 · Jul 16, 2013 at 08:16 PM · raycastcollidermesh

Raycast to align character on MeshCollider normals

Hello, I'm trying to move an object (let's say a character) on the surface of another object. I know that this subject has been dealt with in a lot of other posts, but I still couldn't find a fully working solution.

I've seen different approaches and tried them all with some good results, but I can't really be satisfied because there are still some problems and glitches.

So I've decided to break the whole thing into smaller tasks, hoping to find the final solution to this.

Here's what I have so far:

 void Update () 
 {
     // Input translation
     float inputTranslation = Input.GetAxis("Vertical") * moveSpeed * Time.deltaTime;
     transform.position += transform.forward * inputTranslation;
 
     RaycastHit hit;
 
     if (Physics.Raycast (transform.position, -transform.up, out hit, Mathf.Infinity)) 
     {
         // Stick on surface
         transform.position = hit.point;
 
         // Align to surface normal
         Quaternion fro = Quaternion.FromToRotation(transform.up, hit.normal) * transform.rotation;
         transform.rotation = Quaternion.Lerp(transform.rotation, fro, Time.deltaTime * 5.0f);
     }
 
     // Inpput heading rotation
     float headingDeltaAngle = Input.GetAxis("Horizontal") * Time.deltaTime * headingRotSpeed;
     Quaternion headingDelta = Quaternion.AngleAxis(headingDeltaAngle, transform.up);
 
     transform.rotation = headingDelta * transform.rotation;
 }
 

First task: placing the object (character) onto another object's surface.

Here it looks like pretty much easy stuff: cast a ray downwards from the character, get the hit point and update the character's position.

Also, I need the caracter to move through user input.

This kinda works, but it depends on the surface of the object: sometimes the characters get stuck and won't move anymore; other times it will just go through the object's mesh and pass on the other side (because the raycast gets another hit.point) or even better, it will keep travelling in the outer space...Off course this is not good: I need to find a more generic solution that will work (hopefully) with any arbitrary mesh.

So the first question is: is this a right approach to make the character stick on the object's surface, while also allowing user input to move it? Is it right to set the position of the character to hit.point or does it conflict with the input translation?

Also, how can I add a little offset between the character's transform and the object's surface? I already put the pivot below the character, but I guess I also need to offset the Y position a bit.

Second task: orient the character to the surface normal

This is kinda tricky for me, my vector and quaternion math is lame (to say the least). Anyway, from what I can see and read on Unity docs, Quaternion.FromToRotation rotates the transfom from “fromDirection” to “toDirection”. In this case, it rotates the transform from it's up direction to the direction of the hit.normal. Seems legit to me.

Then all this stuff is being interpolated with Quaternion.Lerp, going from the current object's rotation to the desired one (according to the surface normal) in a given amount of time.

I've seen different examples to do this (at least 4 different ways), but this one seems to me the one which is working better. Again, it's not perfect. Is there a way to improve it, or maybe is better to accomplish this task in a different way?

Third task: changing character's direction with right/left arrow keys

I don't know if this is the right approach; it seems to work fine but I'm not sure if it is causing any of the problems stated above.

Comment
Add comment
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

1 Reply

· Add your reply
  • Sort: 
avatar image
0

Answer by robertbu · Jul 17, 2013 at 08:51 AM

I don't have a lot of experience moving characters over terrain, but in visualizing what is going on here, I have a few thoughts.

You are moving the character along the forward vector of the character (transform.forward), and you are Raycasting the character's down (-transform.up). I can visualize a number of situations where this combo will cause you trouble. For example at a sharp transition, the raycast will not hit the ground and therefore walk through the air, or it will drive the character into the ground and the Raycast() will fail because it starts below the surface of the terrain. I think you want to project the forward onto the XZ plane and use that for forward movement, and for Raycasting() you want to use Vector3.down from somewhere above the character.

    float inputTranslation = Input.GetAxis("Vertical") * moveSpeed * Time.deltaTime;
    Vector3 v3 = transform.forward;
    v3.y = 0.0f;
    transform.position += v3.normalized * inputTranslation;

And for the Raycast():

 if (Physics.Raycast (transform.position + Vector3.up * some_dist, Vector3.down, out hit, Mathf.Infinity)) 

Note that unless you need to raycast against all the objects in the scene, you can use Collider.Raycast() instead of Physics.Raycast().

If you want to position the character above the terrain a bit more, then use the normal:

 transform.position = hit.point + hit.normal * some_distance;

As for aligning with the normal, you might want to try Quaternion.RotateTowards() in place of the Lerp() for a different kind of movement.

Comment
Add comment · Show 13 · 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 AXXE78 · Jul 17, 2013 at 12:09 PM 0
Share

Thank you for your help. I'm not trying to move the character over a terrain or XZ plane; I need to move him all around arbitrary meshes (they can be different shapes). Think about Super $$anonymous$$ario Galaxy or something like that.

avatar image robertbu · Jul 17, 2013 at 02:26 PM 0
Share

If you want to handle arbitrary meshes, then you are going to to have to figure out how to handle the transitions. Note the scale of these 'zigs' don't have to be big nor does it have to be super sharp to cause your code issues:

alt text

At the top of the zig, your Raycast will miss the mesh and your character will head off into space. In the bottom, your Raycast() point can be inside the mesh.

You might be able to solve this issue by using a central attraction point either all the time or when your current Raycast fails. Your movement might need to use a vector tangent to the attraction vector rather than the forward vector.

zig.png (12.9 kB)
avatar image AXXE78 · Jul 17, 2013 at 08:34 PM 0
Share

this helped a lot:

 transform.position = hit.point + hit.normal * some_distance;

and it's exactly what I was looking for. It's kinda working, though not perfect. Here's a video of how it's working:

Youtube video

As you can see, it's not flying around anymore, nor it's falling into the mesh, but it's jittering when it's near the edge of two faces with different inclination, and it gets quite "jumpy" when it's near a peak. Look like the situaion you described in your post.

Also, I've noticed that in this line:

 transform.rotation = Quaternion.Lerp(transform.rotation, fro, Time.deltaTime * 2.0f);

the 2.0f value (the speed at which the character rotates from its previous rotation to the desired one) highly influences the overall behaviour: if it's too small or too high, the character keeps flying in the space or getting inside the mesh.

Any suggestions to fix the jittering and how to handle the Lerp thing? I tried with Quaterion.RotateTowards(), but it didn't work, the characters was flying or entering into the mesh.

avatar image robertbu · Jul 17, 2013 at 09:33 PM 0
Share

You can see why the jitter is occurring. As you make the angle change from one surface to another, you rotate the cube. As you rotate the cube, the direction -transform.up gets angled back so that it is again on the previous face. The next thing I'd try is to change the Raycasting so that it was towards pivot point of the terrain object. Something like:

 Vector3 v3 = transform.position - planet.position;
 if (Physics.Raycast (transform.position + v3 * someDist, -v3, out hit, $$anonymous$$athf.Infinity)) {

...where 'planet' is the transform of the terrain you are driving around on, and 'someDist' is a float value to bring the raycast up a bit. The '+v3*someDist' might not be necessary.

You may also have to change the vector you use for forward movement.

avatar image AXXE78 · Jul 17, 2013 at 09:45 PM 0
Share

hmm...not working; the character starts moving up (outside the mesh) as soon as I hit play. Here is the full Update() method:

 void Update () 
     {
         // Input translation
         float inputTranslation = Input.GetAxis("Vertical") * moveSpeed * Time.deltaTime;
         transform.position += transform.forward * inputTranslation;
 
         RaycastHit hit;
         
         Vector3 v3 = transform.position - planet.transform.position;
         
         if (Physics.Raycast (transform.position + v3 * 0.2f, -v3, out hit, $$anonymous$$athf.Infinity)) 
         {
             // Stick to surface
             transform.position = hit.point + (hit.normal * 0.2f);
                 
             // Align to surface normal
             Quaternion fro = Quaternion.FromToRotation(transform.up, hit.normal) * transform.rotation;
             transform.rotation = Quaternion.Slerp(transform.rotation, fro, Time.deltaTime * 2.0f);
         
         }
         else
         {
             Debug.Log("Lost contact...");
         }
         
         // Inpput heading rotation
         float headingDeltaAngle = Input.GetAxis("Horizontal") * Time.deltaTime * headingRotSpeed;
         Quaternion headingDelta = Quaternion.AngleAxis(headingDeltaAngle, transform.up);
     
         transform.rotation = headingDelta * transform.rotation;
     }

I also tried without "+v3 * 0.2f", but with no luck, the character is sliding on the surface (without user input) towards some point.

Show more comments

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.

Follow this Question

Answers Answers and Comments

15 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

Related Questions

Detecting whole gameobject, not just its collider 1 Answer

RaycastHit not working for Mesh Collider 0 Answers

Physics.Raycast not hitting anything 1 Answer

How to Detect All Mesh Triangles Within a Given Area 2 Answers

Creating a working meshcollider in code 3 Answers


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