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
6
Question by PeterB · Mar 06, 2011 at 12:47 PM · errorraycastcollidernormalsspherecast

RaycastHit.normal - what does it really return?

I'm testing for proximity of other objects by raycasting a sphere, considerably larger than the object itself (also a sphere). I then examine the array of RaycastHit structs, using the RaycastHit.normal vector to push away from the other object in proportion to the distance. So far, so good.

However, the normal returned isn't perpendicular to the surface of the object. In the case of a wall created from a cube, I would have expected the normal to point 90 from the surface, but it doesn't. In the following screen caps, the blue line is the vector returned by RaycastHit.normal:

alt text

The white line is the original direction of the sphere; the red line is the ray from the centre of the sphere to the collision point, for which the normal supposedly is computed. The documentation says the normal is "the normal of the surface of the ray hit". As you can see, the angle is not 90 against the surface.

Here's the same scene from a slightly different perspective:

alt text

As you can see, the normal points downwards, which drives the sphere, which will follow the yellow line, down into the ground. Not so good.

The plane beneath the other objects gives different results:

alt text alt text

In this case, the normal is always along the hit ray.

When moving the sphere around, I can also sometimes see how the hit point snaps to one of the vertices of the wall and stays there; sometimes the hit point is clearly not the closest point. In the case of the plane, the hit point always seems to be along a triangle vertex.

Am I misunderstanding how normals and/or colliders work here? (I should add that I'm not using physics to move the sphere, but translation. As the next step, I'll implement the same type of steering using rigidbodies and physics; I want to compare the pros and cons of the two different approaches.)

I'm using Unity Pro 3.3. Here's the code attached to the sphere:

var target : Transform; var speed : float = 1.0; var comfortDist : float = 10.0;

var move : boolean = false;
var skip : Transform;

private var r : float; private var repulsionExtent : float;

function Start() { r = transform.lossyScale.x; repulsionExtent = comfortDist - r; }

function Update() { if (!target) return; // Turn towards the target transform.LookAt(target);

 var pos : Vector3 = transform.position;
 var dist : float = Time.deltaTime * speed;
 var ray : Ray = new Ray(pos, target.position - pos);
 var dir : Vector3 = transform.TransformDirection(Vector3.forward);
 Debug.DrawRay(pos, dir * 10);

 var hits : RaycastHit[] = Physics.SphereCastAll(ray, comfortDist, dist);
 for (var h in hits) {
     if (h.transform != target && 
         h.collider != this.collider &&
         h.transform != skip) { 
         var realDist : float = Vector3.Distance(pos, h.point) - r;

         Debug.DrawLine(pos, h.point, Color.red);
         Debug.DrawRay(h.point, h.normal, Color.blue);

         var repulsion = (repulsionExtent - realDist) / repulsionExtent;

         var currentForward : Vector3 = transform.TransformDirection(Vector3.forward);
         var newdir : Vector3 = Vector3.Slerp(currentForward, h.normal, repulsion);
         transform.rotation = Quaternion.FromToRotation(currentForward, newdir) * transform.rotation;
     };
 };

 var resultdir : Vector3 = transform.TransformDirection(Vector3.forward);
 Debug.DrawRay(pos, resultdir * 10, Color.yellow);

 // Move forward
 if (move) transform.Translate(Vector3.forward * dist);

}

P.S.: If you put in more than one seeker sphere, you'll notice that they behave correctly in respect to each other, which seems to have to do with the fact that the sphere collision normals behave as expected. Or rather, as I would expect them to... ;-)

Comment
Add comment · Show 1
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 Matthew A · Mar 06, 2011 at 05:05 PM 0
Share

What happens if you then use a standard raycast towards the hit point of the sphere cast? Is the normal returned from that the correct normal? (Not ideal, but if it's correct that should be a simple work-around.)

4 Replies

· Add your reply
  • Sort: 
avatar image
6
Best Answer

Answer by Statement · Mar 06, 2011 at 02:14 PM

It seems that the normal returned from the SphereCastAll method I used represents the normal from the SphereCast, in reverse. This seems to happen if the sphere cast does not hit "ray on" the collider, such in the cases of hitting a edge with the "sides" of the casted sphere. Otherwise my tests show it seems to return the normal of the surface. I think this is some sort of "not really the surface normal but the expected surface bounce direction". Or you can view it as an slerped normal for edge cases.

It seems that:

  • If hitting straight on a collider, it return collider normal.
  • If hitting edge of collider, it return slerped collider normal.
  • Bigger radius make errors (I tried with radius 10 and it was completely unexpected).

Another way of looking at it is to imagine your colliders are spherically extruded, and you cast a ray on that:

alt text

That can perhaps make some sense in why the normal "bends off" in edge cases.

This is the script I used to get to this conclusion:

using UnityEngine; using System.Collections;

public class SphereCastTester : MonoBehaviour { public float radius = 1.0f; public float distance = 10.0f;

 void Update()
 {
     Ray forwardRay = new Ray(transform.position, transform.forward);
     var hits = Physics.SphereCastAll(forwardRay, radius, distance);

     // White = cast direction
     Debug.DrawRay(forwardRay.origin, forwardRay.direction * distance, Color.white);

     foreach (var hit in hits)
     {
         // Red = from origin to point
         Debug.DrawLine(forwardRay.origin, hit.point, Color.red);

         // Blue = normal on hit point.
         Debug.DrawRay(hit.point, hit.normal, Color.blue);
     }
 }

}

To try this out, just place it on some object, place a few colliders out, hit play and move the transform of the object around a bit.

Edit: I tried setting radius to 10, and some unexpected behavior was witnessed. It seems the normal bends off way too early. I can't explain this.

Comment
Add comment · Show 11 · 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 PeterB · Mar 06, 2011 at 02:31 PM 1
Share

Thanks for the script and your detailed answer. It's very interesting: using your script I get exactly the behaviour I would expect, the correct one, including the slerping on the edges. We also use the same function to do the raycasting. There is one difference, though: I only sweep the sphere as far as it would go during the frame, while you move it much farther (10 units). I wonder if that is affecting the outcome of the raycast, as everything else seem to be identical.

avatar image PeterB · Mar 06, 2011 at 02:35 PM 0
Share

If you try out my script and enable $$anonymous$$ove, you'll also see how the hit points "snap" to various fixed positions during the path towards the target. It's most easily seen on the floor plane. I would think that the hit point would always be slightly in front of the sphere and thus always nearly straight up, but the snapping is instant and the difference in angle can be dramatic.

avatar image Statement · Mar 06, 2011 at 02:36 PM 0
Share

Actually, try increasing radius to 10, and you'll be seeing strange stuff happen!

avatar image Statement · Mar 06, 2011 at 02:38 PM 0
Share

Yeah I noticed the same snapping in my trial. I know I have had issues with sphere and capsule cast in the past, but then it was about hitting triggers. They don't seem 100% tested, but I could be wrong.

avatar image PeterB · Mar 06, 2011 at 02:44 PM 0
Share

So we might have hit upon a Unity bug?

Show more comments
avatar image
1
Best Answer

Answer by Ashkan_gc · Mar 06, 2011 at 01:44 PM

maybe your raycasting code has some problems. posting the code might help. pictures don't display anymore to see. i did not have a problem like this never. using physics for special gameplay types don't work most of the times unless you use some tricks so this way is a better one. however the blurst.com guys might be able to implement anything using physx. :)

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 PeterB · Mar 06, 2011 at 02:07 PM 0
Share

I'll edit the question to include the code. Thanks. :)

avatar image
0

Answer by Bampf · Mar 06, 2011 at 01:22 PM

Double-check the collider of the wall, make sure it's what you expect.

Comment
Add comment · Show 4 · 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 PeterB · Mar 06, 2011 at 01:39 PM 0
Share

The wall collider is a standard box collider. Turning on trigger of course disables collision detection. Were you thinking of anything specific? The docs explicitly state that RaycastHit.normal is relative to the surface of the colliding object.

Also, in the case of the floor plane, I would expect the floor normal to point straight up, which clearly it doesn't.

avatar image Statement · Mar 06, 2011 at 01:57 PM 0
Share

I think Bampf meant you should check that the collider is aligned with the box and not rotated and such. I doubt it is rotated, especially if you just created a box using the primitives.

avatar image PeterB · Mar 06, 2011 at 02:06 PM 0
Share

Ah, ok, then I see. Well, the collider is aligned with the box, so the problem doesn't lie there.

avatar image Bampf · Mar 07, 2011 at 10:32 PM 0
Share

Ah well. Worth checking.

avatar image
0

Answer by Tabemasu Games · Jan 15, 2015 at 10:33 AM

I created a small function to fix the normal returned by a sphere or capsule cast:

 public static void FixNormal(Vector3 position, ref RaycastHit hit, int layermask) {
     RaycastHit rayHit;
     Physics.Raycast(position, hit.point - position, out rayHit, 2 * hit.distance, layermask);
     hit.normal = rayHit.normal;
 }

Basically, it casts a ray, and finds its normal.

Usage:

 RaycastHit hit;
 if (Physics.SphereCast(startPosition, radius, direction, out hit, distance, layermask)) {
     FixNormal(startPosition, ref hit, layermask);
     // hit.normal is now OK
 }
Comment
Add comment · 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

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

1 Person is following this question.

avatar image

Related Questions

SphereCastAll doesn't seem to be working 2 Answers

Can Raycast get multiple points from the same collider? 1 Answer

How to rotate an object 360 degrees around a sphere with collisions? 0 Answers

Getting treePrototype information from RayCasting collisions onto terrain? 2 Answers

Raycast problems unexpected token var etc. 2 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