• 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
Question by Moesh · Jun 11, 2015 at 05:25 AM · collisionraycastcolliderboxcolliderdiagonal

How can I make diagonal collisions work with raycasts from a boxcollider? (3D, C#)

First I'd like to mention that I'm very new to unity, C# and game development. I'm currently making my own player controller using raycasts to detect collisions, but I'm stuck at how to handle diagonal collisions.

What I have is a box with a boxcollider that can move in all directions within the x and z axis. The box itself doesn't rotate, but instead raycasts are being drawn towards one of the 4 axis (x, z, xz or zx). Collisions against one of the faces of the box work, but diagonal collisions against the edges of the box don't.

Here's a gif of the raycasts being drawn in the direction the player is moving: http://i.imgur.com/QseT1Ot.gifv

Here's a gif showing that the diagonal collisions aren't working: http://i.imgur.com/wYuSm8O.gifv

When that didn't work, I tried another method. Instead of having raycasts in 4 axis, I made the box rotate and shot raycasts only forward. Here's a gif of that: http://i.imgur.com/hYHahDA.gifv

That does work better, but still not perfect. Which one of the methods would work best and can you push me into the right direction how to fix the collision system?

Here's the part of the script that handles raycasts and collisions like in the first 2 gifs:


    void XCollisions(ref Vector3 velocity) {
         float directionX = Mathf.Sign(velocity.x);
         float rayLength = Mathf.Abs(velocity.x) + skinWidth;
 
         //horizontal rays
         for(int i = 0; i < xRayCount; i++) {
             Vector3 rayOrigin = (directionX == -1) ? raycastOrigins.bottomLeftBack : raycastOrigins.bottomRightBack;
             rayOrigin += Vector3.forward * (xRaySpacing * i);
                 
             //vertical multiplier
             for(int j = 0; j < xzRayCountY; j++) {
                 Vector3 xrayOriginY = rayOrigin;
                 xrayOriginY += Vector3.up * (xzRaySpacingY * j);
 
                 RaycastHit hitInfo;
                 bool hit = Physics.Raycast(xrayOriginY, Vector3.right * directionX, out hitInfo, rayLength, collisionMask);
                 Debug.DrawRay(xrayOriginY, Vector3.right * directionX * 3, Color.red);
 
                 if(hit) {
 
                     if(hitInfo.distance == 0) {
                         continue;
                     }
 
                     float slopeAngle = Vector3.Angle(hitInfo.normal, Vector3.up);
                     
                     if(i == 0 && slopeAngle <= maxClimbAngle) {
                         if(collisions.descendingSlope) {
                             collisions.descendingSlope = false;
                             velocity = collisions.velocityOld; 
                         }
                         float distanceToSlopeStart = 0;
                         if(slopeAngle != collisions.slopeAngleOld) {
                             distanceToSlopeStart = hitInfo.distance - skinWidth;
                             velocity.x -= distanceToSlopeStart * directionX;
                         }
                         ClimbSlopeX(ref velocity, slopeAngle);
                         velocity.x += distanceToSlopeStart * directionX;
                     }
                     
                     if(!collisions.climbingSlope || slopeAngle > maxClimbAngle) {
 
                         velocity.x = (hitInfo.distance - skinWidth) * directionX;
                         rayLength = hitInfo.distance;
 
                         //collision in x axis while climbing slope
                         if(collisions.climbingSlope) {
                             velocity.y = Mathf.Tan(collisions.slopeAngle * Mathf.Deg2Rad) * Mathf.Abs(velocity.x);
                         }
 
                         collisions.left = directionX == -1;
                         collisions.right = directionX == 1;
                     }
                 }
             }
         }
     }
 
     void ZCollisions(ref Vector3 velocity) {
         float directionZ = Mathf.Sign(velocity.z);
         float rayLength = Mathf.Abs(velocity.z) + skinWidth;
 
         //horizontal rays
         for(int i = 0; i < zRayCount; i++) {
             Vector3 rayOrigin = (directionZ == -1) ? raycastOrigins.bottomLeftBack : raycastOrigins.bottomLeftFront;
             rayOrigin += Vector3.right * (zRaySpacing * i);
 
             //vertical multiplier
             for(int j = 0; j < xzRayCountY; j++) {
                 Vector3 zrayOriginY = rayOrigin;
                 zrayOriginY += Vector3.up * (xzRaySpacingY * j);
 
                 RaycastHit hitInfo;
                 bool hit = Physics.Raycast(zrayOriginY, Vector3.forward * directionZ, out hitInfo, rayLength, collisionMask);
                 Debug.DrawRay(zrayOriginY, Vector3.forward * directionZ * 3, Color.blue);
 
                 if(hit) {
 
                     if(hitInfo.distance == 0) {
                         continue;
                     }
 
                     float slopeAngle = Vector3.Angle(hitInfo.normal, Vector3.up);
 
                     if(i == 0 && slopeAngle <= maxClimbAngle) {
                         if(collisions.descendingSlope) {
                             collisions.descendingSlope = false;
                             velocity = collisions.velocityOld;
                         }
                         float distanceToSlopeStart = 0;
                         if(slopeAngle != collisions.slopeAngleOld) {
                             distanceToSlopeStart = hitInfo.distance - skinWidth;
                             velocity.z -= distanceToSlopeStart * directionZ;
                         }
                         ClimbSlopeZ(ref velocity, slopeAngle);
                         velocity.z += distanceToSlopeStart * directionZ;
                     }
 
                     if(!collisions.climbingSlope || slopeAngle > maxClimbAngle) {
 
                         velocity.z = (hitInfo.distance - skinWidth) * directionZ;
                         rayLength = hitInfo.distance;
 
                         //collision in z axis while climbing slope
                         if(collisions.climbingSlope) {
                             velocity.y = Mathf.Tan(collisions.slopeAngle * Mathf.Deg2Rad) * Mathf.Abs(velocity.z);
                         }
 
                         collisions.back = directionZ == -1;
                         collisions.front = directionZ == 1;
                     }
                 }
             }
         }
     }
 
     void XZCollisions(ref Vector3 velocity) {
         float directionX = Mathf.Sign(velocity.x);
         float directionZ = Mathf.Sign(velocity.z);
         float directionXZ = 0;
         float directionZX = 0;
 
         if(directionX == -1) {
             directionXZ = (directionZ == 1) ? 1 : 0;
             directionZX = (directionZ == -1) ? -1 : 0;
         } else if(directionX == 1) {
             directionXZ = (directionZ == -1) ? -1 : 0;
             directionZX = (directionZ == 1) ? 1 : 0;
         }
 
         float rayLength = (Mathf.Abs(velocity.z) + Mathf.Abs(velocity.x)) / 2 + skinWidth;
         Vector3 playerDirection = new Vector3(velocity.x, 0, velocity.z);
         Vector3 playerDirectionXZ = new Vector3(-1, 0, 1);
         Vector3 playerDirectionZX = new Vector3(1, 0, 1);
 
         if(directionXZ != 0) {
             Vector3 rayOrigin = (directionXZ == -1) ? raycastOrigins.bottomRightBack : raycastOrigins.bottomLeftFront;
             for(int i = 0; i < xzRayCountY; i++) {
                 Vector3 rayOriginX = (directionXZ == -1) ? raycastOrigins.bottomRightBack : raycastOrigins.bottomLeftFront;
                 Vector3 rayOriginZ = (directionXZ == -1) ? raycastOrigins.bottomRightBack : raycastOrigins.bottomLeftFront;
                 rayOriginX += Vector3.right * directionXZ * (xRaySpacing * i);
                 rayOriginZ += Vector3.back * directionXZ * (zRaySpacing * i);
 
                 for(int j = 0; j < xzRayCountY; j++) {
                     Vector3 xzrayOriginYX = rayOriginX;
                     xzrayOriginYX += Vector3.up * (xzRaySpacingY * j);
                     RaycastHit hitInfoX;
                     bool hitX = Physics.Raycast(xzrayOriginYX, playerDirectionXZ * directionXZ, out hitInfoX, rayLength, collisionMask);
                     Debug.DrawRay(xzrayOriginYX, playerDirectionXZ * directionXZ * 3, Color.magenta);
 
                     if(hitX) {
                         Debug.Log("diagonal collision");
                         velocity.x = (hitInfoX.distance - skinWidth) * directionXZ;
                         velocity.z = (hitInfoX.distance - skinWidth) * directionXZ;
                         rayLength = hitInfoX.distance;
                     }
 
                     Vector3 xzrayOriginYZ = rayOriginZ;
                     xzrayOriginYZ += Vector3.up * (xzRaySpacingY * j);
                     RaycastHit hitInfoZ;
                     bool hitZ = Physics.Raycast(xzrayOriginYZ, playerDirectionXZ * directionXZ, out hitInfoZ, rayLength, collisionMask);
                     Debug.DrawRay(xzrayOriginYZ, playerDirectionXZ * directionXZ * 3, Color.magenta);
 
                     if(hitZ) {
                         Debug.Log("diagonal collision");
                         velocity.x = (hitInfoZ.distance - skinWidth) * directionXZ;
                         velocity.z = (hitInfoZ.distance - skinWidth) * directionXZ;
                         rayLength = hitInfoZ.distance;
                     }
                 }
             }
         }
 
         if(directionZX != 0) {
             Vector3 rayOrigin = (directionZX == -1) ? raycastOrigins.bottomLeftBack : raycastOrigins.bottomRightFront;
             for(int i = 0; i < xzRayCountY; i++) {
                 Vector3 rayOriginX = (directionZX == -1) ? raycastOrigins.bottomLeftBack : raycastOrigins.bottomRightFront;
                 Vector3 rayOriginZ = (directionZX == -1) ? raycastOrigins.bottomLeftBack : raycastOrigins.bottomRightFront;
                 rayOriginX += Vector3.back * directionZX * (xRaySpacing * i);
                 rayOriginZ += Vector3.left * directionZX * (zRaySpacing * i);
 
                 for(int j = 0; j < xzRayCountY; j++) {
                     Vector3 xzrayOriginYX = rayOriginX;
                     xzrayOriginYX += Vector3.up * (xzRaySpacingY * j);
                     RaycastHit hitInfoX;
                     bool hitX = Physics.Raycast(xzrayOriginYX, playerDirectionZX * directionZX, out hitInfoX, rayLength, collisionMask);
                     Debug.DrawRay(xzrayOriginYX, playerDirectionZX * directionZX * 3, Color.yellow);
 
                     if(hitX) {
                         Debug.Log("diagonal collision");
                         velocity.x = 0;
                         velocity.z = 0;
                         rayLength = hitInfoX.distance;
                     }
 
                     Vector3 xzrayOriginYZ = rayOriginZ;
                     xzrayOriginYZ += Vector3.up * (xzRaySpacingY * j);
                     RaycastHit hitInfoZ;
                     bool hitZ = Physics.Raycast(xzrayOriginYZ, playerDirectionZX * directionZX, out hitInfoZ, rayLength, collisionMask);
                     Debug.DrawRay(xzrayOriginYZ, playerDirectionZX * directionZX * 3, Color.yellow);
 
                     if(hitZ) {
                         Debug.Log("diagonal collision");
                         velocity.x = 0;
                         velocity.z = 0;
                         rayLength = hitInfoZ.distance;
                     }
                 }
             }
 
             //player direction
             RaycastHit hitInfoPD;
             bool hitPD = Physics.Raycast(rayOrigin, playerDirection * directionZ, out hitInfoPD, rayLength, collisionMask);
             Debug.DrawRay(rayOrigin, playerDirection * directionXZ * 3, Color.cyan);
 
             if(hitPD) {
                 Debug.Log("PlayerDirection collision");
                 velocity.x = 0;
                 velocity.z = 0;
                 rayLength = hitInfoPD.distance;
             }
         }
 
     }



If you need any more information, feel free to ask.

What am I doing wrong?

Comment

People who like this

0 Show 3
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 zach-r-d · Jun 11, 2015 at 08:37 AM 0
Share

Is there any particular reason you aren't using a RigidBody with kinematic set to true? Then whenever you wanted to move you could just call the .MovePosition method manually, and it would collide properly.

avatar image Moesh · Jun 11, 2015 at 10:19 AM 0
Share

I want to learn how to make a simple player controller, so I try not to use the standard unity player controllers or a rigidbody. The standard Player controller comes pretty close to what I want, but I'm trying to make my own so I can fine tune it more easily to my needs. With my own controller I'm having a few problems like edge collisions and moving on slopes.

avatar image zach-r-d · Jun 11, 2015 at 10:32 AM 0
Share

Ah, I see. Well, no matter how many raycasts you do, it will always be possible for geometry to intersect. What you really want to do instead of raycasting is "boxcasting", but Unity's Physics class only supports raycasting, linecasting, spherecasting, and capsulecasting (most player/character controllers are capsules).

Writing your own boxcasting algorithm would be extremely difficult but not impossible. If you attached a rigidbody, you could use .SweepTest, if that doesn't count as cheating for you.

0 Replies

· Add your reply
  • Sort: 

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

3 People are following this question.

avatar image avatar image avatar image

Related Questions

Unity collision detection only for raycasts 1 Answer

Problem with Vectrosity line colliders 0 Answers

Prevent shooting when gun is inside wall 1 Answer

Collider suddenly stops detecting collision? (bug?) 1 Answer

Anyway to tell which side of a collider is hit? 4 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