Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Question How can I use two directions in a single Raycast?

Discussion in 'Scripting' started by ResonantWilterUni, Jul 24, 2023.

  1. ResonantWilterUni

    ResonantWilterUni

    Joined:
    Jul 20, 2023
    Posts:
    41
    I am trying to shoot a Raycast both down and also backwards. I have code to handle the angle, but it's the Raycast if statement that is causing me confusion.

    Code (CSharp):
    1. if (Physics.Raycast(transform.position, Vector3.down, out hit, 5f))
    2. {
    3.     // Actions here
    4. }
    I'm not sure if this photo representation is good, but essentially I am trying to have two Raycast rays that are both active simultaneously and when they hit something, then my player should move in the direction of the red arrow. I have the movement part solved, but it's the initial Raycast that I can't figure out since I know from Unity docs that only one direction can be used in the declaration.
     
  2. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    5,769
    Just do two raycasts and check the results of both.

    Code (CSharp):
    1. bool result1 = Physics.RayCast(...);
    2. bool result2 = Physics.RayCast(...);
    3.  
    4. if (result1 && result2)
    5. {
    6.     // stuff
    7. }
     
    CodeSmile and Yuchen_Chang like this.
  3. ResonantWilterUni

    ResonantWilterUni

    Joined:
    Jul 20, 2023
    Posts:
    41
    thank you I will give this a shot. I have never seen a bool used with a Raycast like that before, is there any unity docs or other resources on that I can look through?
     
  4. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    5,769
    It's just standard C# stuff. Physics.Raycast and many other methods in the class return a boolean value. So you can just get the return value and do whatever you want with it.
     
  5. zulo3d

    zulo3d

    Joined:
    Feb 18, 2023
    Posts:
    510
    I'm sure Spiney knows this and he was just giving a basic advice, but if any new users are reading this, if doing something that depends on the success of two raycasts then it can be more efficient to do it this way:

    Code (CSharp):
    1.         if (Physics.Raycast(...))
    2.         {
    3.             if (Physics.Raycast(...))
    4.             {
    5.                 // do something
    6.             }
    7.         }
    And the first raycast should be one that's least likely to be successful. This way your script can often avoid having to bother doing the second raycast..
     
  6. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    5,769
    Slightly more performant, yeah. Though in situations where performance isn't a huge concern (which is most of the time, especially when learning) I would opt for readability over performant.

    With something like this:
    Code (CSharp):
    1. bool groundCheck = Physics.RayCast(...);
    2. bool slopeCheck = Physics.RayCast(...);
    3.  
    4. if (groundCheck && slopeCheck)
    5. {
    6.     // stuff
    7. }
    You can easily tell what the code is doing just by reading the member names. Saves a lot of time in the long run.
     
    Bunny83, halley and zulo3d like this.
  7. ResonantWilterUni

    ResonantWilterUni

    Joined:
    Jul 20, 2023
    Posts:
    41
    In that case, alongside the first Raycast using Vector3.down as the direction, the next direction based on the photo would be Vector3.back?

    But I was thinking and if I'm not mistaken, Vector3.back is world direction right? So if the photo above was flipped horizontally and the player was moving downwards but right to left instead of left to right like in the photo, would the Vector3.back not hit anything due to its fixed world position? But also upon testing -transform.forward, that is not suitable either due to it being based on my real time character rotation
     
  8. zulo3d

    zulo3d

    Joined:
    Feb 18, 2023
    Posts:
    510
    You don't need two raycasts. You can do a single raycast down and check the hit.normal to see if your character is on a slope.

    Code (CSharp):
    1.         if (Physics.Raycast(transform.position,Vector3.down,out hit,5f))
    2.         {
    3.             if (Vector3.Dot(hit.normal,Vector3.up)<0.95f)   // slope?
    4.             {
    5.                 // do a roly poly!
    6.             }
    7.         }
     
    ResonantWilterUni and spiney199 like this.
  9. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    5,769
    Directions are either world or local depending on context. So, if the context is a 3d game with a 3rd person camera,
    Vector3.back
    would be considered 'World'. You can use methods like
    Transform.InverseTransformDirection
    to make it local to a particular transform, though this is basically the same as
    -transform.forward
    .

    Though as above, the downwards raycast is enough to determine the slope the player is on. And from that you can determine things like which way to slide in, etc.
     
    ResonantWilterUni likes this.
  10. ResonantWilterUni

    ResonantWilterUni

    Joined:
    Jul 20, 2023
    Posts:
    41
    Thanks for the info. I'm going to use everything above, and the reason I was trying to use a second direction is because with Vector3.down, the player stops before they should because the Vector3.down Raycast is no longer detecting an angled object, yet half the player character is still technically standing on the angled object.

    So in my mind if I had Vector3.down + back for example, then even if the down Raycast stops a little too early, the back Raycast would make the player continue moving that last bit of distance.
     
  11. ResonantWilterUni

    ResonantWilterUni

    Joined:
    Jul 20, 2023
    Posts:
    41
    And then from there I can use the hit.normal of the Vector3.Dot to adjust speed based on player direction, whether they are ascending or descending the angled object?
     
  12. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    5,769
    It might be worth just doing multiple raycasts and averaging the results, or one of the other casts that has more 'volume', such as SphereCast.

    Just note that casts don't 'hit' objects they're already intersecting with when the cast starts.
     
    ResonantWilterUni likes this.
  13. zulo3d

    zulo3d

    Joined:
    Feb 18, 2023
    Posts:
    510
    I've seen some users consider the player's direction but I would just create a vector and add it to the player's velocity. They'll then naturally move slower or faster depending on whether they're going up or down the slope.

    And as Spiney suggests, Spherecast is a little more robust for getting the hit normal below the player.
     
    ResonantWilterUni likes this.
  14. ResonantWilterUni

    ResonantWilterUni

    Joined:
    Jul 20, 2023
    Posts:
    41
    Yea I saw some older posts on this forum and youtube where people describe Spherecast but I'm just not sure how many unintended problems will occur because its technically a sphere around the bottom of my player right? So that means it will be checking for angles all around the radius so what if its detecting an angle when there is not one, such as on a straight object like a square, rectangle or cube?

    Sorry im not too experienced with Spherecast. My Raycast will never detect an angle on top of an object like a cube, rectangle and so on but the spherecast will if I stand near the corn. Do I just use layer masks to ignore cube or flat objects?
     
  15. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    5,769
    It's not as complicated as you think.

    First look at the docs: https://docs.unity3d.com/ScriptReference/Physics.SphereCast.html

    It just returns a bool if it hits, and overloads that output the same RaycastHit info as all the other raycast methods.

    This RaycastHit info will be the result of the first point at which the imaginary sphere intersects with something. So if the player is standing right at the edge of a slope, you'll likely hit the slope first and can work off that information.

    You may have to combine multiple types of raycasts and weigh the results. It's not going to be a one line of code affair.
     
  16. ResonantWilterUni

    ResonantWilterUni

    Joined:
    Jul 20, 2023
    Posts:
    41
    Is using a layer mask here where my Raycast ignores certain objects or potentially even RaycastAll with a Foreach loop to go through object "tags" a good choice, or is it worth investing time into learning how to actually combine those several Raycasts and weigh their results?

    I'm not quite sure about implementation with that last part of your reply as I tried using Vector3.Angle, Vector3.SignedAngle and I was given a suggestion to use Vector3.Cross, but even when I use their angle checks in if statements to prevent the spherecast from hitting when it shouldn't, it would just ignore it.
     
  17. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    5,769
    A layermask is going to be necessary most of the time anyway as you may have surfaces you don't want the player to be able to jump/move on.

    I will reiterate something I said earlier:
    So if
    transform.position
    is at the floor of your player, then sphere casting from it will never work. You will need to cast from a higher point down to the ground to get an effective result.

    When I mean 'weigh the results' I really mean going through a series of checks and go with what's higher, smaller, etc, depending on context. You can start with 'best case' assumptions, such as a simple raycast down to the ground. If the result is insufficient, you can move onto more elaborate checks.

    Otherwise hard to say without seeing code to be able to tell if what you're doing is incorrect.
     
  18. ResonantWilterUni

    ResonantWilterUni

    Joined:
    Jul 20, 2023
    Posts:
    41
    Oh like the origin position of the ray.

    My code for this is basically just: https://pastebin.com/xDfMeMcE

    I took out a few checks that did nothing like if (angle != 0f). I even added a bool to check if I am on a non-flat surface and use that to prevent Spherecast from hitting the wrong objects (cubes, rectangles), etc, but they all get ignored.

    And the sliding is just one line of code with move += the hitPointNormal, x, -y, z, and then controller.Move().
     
  19. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    5,769
    Like I said if
    transform.position
    is bottom of the player, a SphereCast will miss the ground as its starting already intersecting with the ground. So basically none of this code is ever running.

    You need to cast from a higher point.
     
    ResonantWilterUni likes this.
  20. ResonantWilterUni

    ResonantWilterUni

    Joined:
    Jul 20, 2023
    Posts:
    41
    Yeah I'm in the process of doing this. I'm currently testing it out with Gizmos.DrawWireSphere using my SphereCast declarations to visualize it. But when replacing transform.position with a new Vector3 where I increase the origin point, even when I adjust the values of radius or hit distance, the Spherecast either never detects anything, or the same problem persists. I believe I'm doing it correctly based on the Unity docs you linked which shows a case of this where the origin is replaced with Vector3 p1 = transform.position + charCtrl.center; so I'm using that as a reference but still no change so far
     
    Last edited: Jul 24, 2023
  21. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    5,769
    I would just increase it by some measure greater than the sphere radius. If the radius is
    0.5f
    , for example, then you can just 'move up' by that amount plus a little more, eg:
    transform.position + (Vector3.up * radius) + 0.1f
    , while also making sure you're casting far enough down to actually reach the ground.

    Otherwise just a matter of debugging all the options as to why a raycast would fail.
     
    ResonantWilterUni likes this.
  22. ResonantWilterUni

    ResonantWilterUni

    Joined:
    Jul 20, 2023
    Posts:
    41
    Thanks for all the messages. I will try to continue it further and hopefully solve it. Is the + 0.1f meant to be * 0.1f though since there's a '+' operand error
     
  23. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    5,769
    Right, that probably should've been
    transform.position + (Vector3.up * (radius + 0.1f))
    or something along those lines. We don't want to multiply by that small value otherwise we we'll be back to where we started.
     
  24. ResonantWilterUni

    ResonantWilterUni

    Joined:
    Jul 20, 2023
    Posts:
    41
    Yeah thanks, I fixed the DrawWireSphere and now I'm testing that line you sent plus other values to see what works best and I will figure it out, so I appreciate all the help.

    Does that mean that the rest of my code generally speaking is OK? And it's just a matter of finding the right values with the transform.position origin?
     
  25. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    5,769
    Nothing stands out immediately as being wrong, but only testing and debugging will say for sure.
     
    ResonantWilterUni likes this.