Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 12 Next capture
2021 2022 2023
1 capture
12 Jun 22 - 12 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 LukeZaz · Aug 05, 2019 at 01:40 AM · physicsrigidbodyphysxoncollisionstaycapsulecastall

Ways to work around OnCollisionStay/Enter events firing one frame late?

I'm trying to make a rigidbody-based player movement script, and for the most part everything's worked well. Recently though, while trying to add code to avoid sliding down slopes below a walkable slope limit, I came across an issue with my ground check system; i.e., OnCollisionStay, which my ground check is reliant on, appears to fire one physics frame late from when it happens. Adding an OnCollisionEnter check has no effect, nor does any variant of the rigidbody collision detection setting.

Searching in general has turned up very few things, but one result of note was that it appears this behavior is intentional in PhysX:

"[...] frame is then getting lost because there is a general one frame delay from the moment a contact was discovered to the the moment an appropriate callback was called. This behaviour is by design in PhysX."

This is mind, I've been trying to find alternatives for finding ground for the rigidbody. The issue linked above suggests using an overlap function followed by a CapsuleCast. I've been trying something similar, but it always ends up reporting normals pointing directly up:

 Vector3 rotatedCapsuleHalfHeight = transform.up * (collider.height / 2);
 RaycastHit[] hits = Physics.CapsuleCastAll(transform.position - rotatedCapsuleHalfHeight,
     transform.position + rotatedCapsuleHalfHeight, collider.radius - 0.01f, -transform.up,
     0.0001f, collisionMask);
 
 if (hits.Length > 0)
 {
     Vector3 normal;
     // Find the contact normal that is closest to 'up', relative to the current direction of gravity; this signifies the most level ground
     if (hits.Length == 1) normal = hits[0].normal;
     else
     {
         float closestDot = 1;
 
         normal = hits[0].normal;
         foreach (RaycastHit hit in hits)
             if (Vector3.Dot(hit.normal, -gravity.normalized) < closestDot) normal = hit.normal;
     }
 
     float angle = Vector3.Angle(-gravity.normalized, normal);
     if (angle < slopeLimit - 0.01f) walkableGroundNormal = groundNormal = normal;
     else if (angle < 89.9f) groundNormal = normal;
 
     // IsGrounded is a shorthand property that returns true if groundNormal does not equal Vector3.zero
     // groundNormal is set to Vector3.zero at the end of every FixedUpdate.
     if (IsGrounded) lastGroundedTime = Time.time;
 }

It's worth noting though that I've been trying to tackle this problem for a few days now, so I'm absolutely open to any workarounds to this, not just the CapsuleCast one.

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

2 Replies

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

Answer by LukeZaz · Aug 05, 2019 at 07:09 AM

Fixed my code. Turns out, CapsuleCast and SphereCast give RaycastHits whose normals are the inverse normals of the cast, not the normal of the hit as expected. I was able to repair this using a modified function I found here. (Thanks a ton, lordofduct!)


Note that there's still a couple bugs with my implementation that I'm ironing out (namely, non-MeshCollider normals still seem to be a tad messy), but I'll edit this post with any fixes as I find them.

Hit normal repair code:

 // I use this as an extension to RaycastHit here, but obviously that's not necessary by any stretch.
 public static Vector3 GetRepairedNormal(this RaycastHit hit)
 {
     if (hit.collider is MeshCollider)
     {
         Mesh mesh = (hit.collider as MeshCollider).sharedMesh;
 
         // Get the vertices of the specific triangle that was hit
         Vector3 vertex0 = mesh.vertices[mesh.triangles[hit.triangleIndex * 3]];
         Vector3 vertex1 = mesh.vertices[mesh.triangles[hit.triangleIndex * 3 + 1]];
         Vector3 vertex2 = mesh.vertices[mesh.triangles[hit.triangleIndex * 3 + 2]];
 
         Vector3 normal = Vector3.Cross(vertex1 - vertex0, vertex2 - vertex1).normalized;
 
         return hit.transform.TransformDirection(normal);
     }
     else
     {
         Vector3 point = hit.point + hit.normal * 0.01f;
         hit.collider.Raycast(new Ray(point, -hit.normal), out hit, 0.011f);
         return hit.normal;
     }
 }

Note that I have changed the ground check code in my script to happen at the start of every FixedUpdate, instead of the end as I had before. I also only reset my ground normals right before checking collision; otherwise, IsGrounded would be false during Update calls.

Code for finding ground:

 Vector3 rotatedCapsuleHalfHeight = transform.up * (collider.height / 2 - collider.radius);
 // Note that the gravity variables used here are Vector3s that represent force AND direction.
 RaycastHit[] hits = Physics.CapsuleCastAll(transform.position - rotatedCapsuleHalfHeight,
     transform.position + rotatedCapsuleHalfHeight, collider.radius, baseGravity.normalized,
     currentGravity.magnitude * Time.fixedDeltaTime, collisionMask);
 
 if (hits.Length > 0)
 {
     Vector3 normal;
     if (hits.Length == 1) normal = hits[0].GetRepairedNormal();
     else
     {
         float closestDot = 1;
 
         normal = hits[0].GetRepairedNormal();
         foreach (Vector3 norm in hits.Select(x => x.GetRepairedNormal()))
             if (Vector3.Dot(norm, -gravity.normalized) < closestDot) normal = norm;
     }
 
     float angle = Vector3.Angle(-gravity.normalized, normal);
     if (angle < slopeLimit - 0.01f) walkableGroundNormal = groundNormal = normal;
     else if (angle < 89.9f) groundNormal = normal;
     // IsGrounded is a shorthand that returns true if groundNormal does not equal Vector3.zero
     // groundNormal and walkableGroundNormal are both reset right before this collision check, 
     // which works ideally at the beginning of FixedUpdate, before code relying on ground checks.
     if (IsGrounded) lastGroundedTime = Time.time;
 }


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
avatar image
1

Answer by xxmariofer · Aug 05, 2019 at 06:27 AM

well i think your issue is not exactly the same as reported there, Unity physics are completly independent from the main loop in unity, this means that per frame you can get multiple collision callbacks, or not a single. if yoou are not colliding frequently enought you can try using the property collision detection to Continues or continues dynamic and see if it helps, your rigidboy moves independant of the player thats being rendered. you can use the Window>Physics Debugger to see this, if you want to get collision messages before they really occur the only alternatives are using a child empty component with the same collider as the parent but triggered and a bit bigger so you get the trigger messages, or using raycast with a certain length

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 LukeZaz · Aug 05, 2019 at 07:14 AM 0
Share

I'm aware. I'd already tried all collision detection settings – even continuous speculative, just in case – and none helped. It also isn't a rendering issue, as the player doesn't even have a renderer right now. I ended up fixing my code though, which I've posted as an answer above.

That all said, thanks for mentioning the Physics Debugger, I didn't know that was there!

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

206 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 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 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 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 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 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 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 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 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 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 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 avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image

Related Questions

How to "discharge" the angular power of a configurable joint? 0 Answers

Using kinematic rigidbody on parent for disable enable mesh collider and primitive collider children? 1 Answer

What is the best way to prevent a physics object from going through thin colliders? 2 Answers

Physics.Simulate time increases exponentially relative to rigidbody count 1 Answer

How to create floating platform? 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