Search Unity

Is raycasting really the best way to do this?

Discussion in 'Scripting' started by Badoobicus, Sep 22, 2014.

  1. Badoobicus

    Badoobicus

    Joined:
    Jan 27, 2013
    Posts:
    10
    I have been working on a small FPS game, learning unity in the process. However I am not new to programming, so I know for the most part what I am doing. Anyways, here's my problem:

    In my game, I am trying to adding in player movement similar to that of Titanfall (if you haven't played Titanfall it's an FPS but you can wall run and double jump). I have implemented walking/running, as well as jumping/double jumping, but I have run into some issues with the wall running, mainly having the player detect walls. I have looked around for a while and there are seemingly no methods that can be used in my situation that will return if the player is touching a wall and what direction the wall is facing. So I came up with an idea in which the player casts 360 rays out, 1 every 1 degree, meaning that 360 raycasts would be done each frame. This would be quite accurate (would be able to detect thin poles and what not), but I am worried that this will be very taxing on performance. Is this a good way to go about doing this, or is there a better, more efficient way? Also, I don't want to put a rigid body on every object in the scene to get OnCollisionStay() to work.
     
  2. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,539
    Add a SphereCollider to the player and put it on a layer that will only interact with the layer your walls are in and set it as a trigger. Use OnTriggerEnter/Exit to register nearby walls and raycast only against the ClosestPointOnBounds of those colliders.
     
  3. Badoobicus

    Badoobicus

    Joined:
    Jan 27, 2013
    Posts:
    10
    The ClosestPointOnBounds is exactly what I was looking for. Thanks for your help.
     
  4. Badoobicus

    Badoobicus

    Joined:
    Jan 27, 2013
    Posts:
    10
    After doing a bit more work, I have found a flaw with this method. The method seems to treat every bounding box as axis-aligned, which makes this method pretty much useless for any walls that aren't at 90-degree angles. Does anyone know how to fix this?
     
  5. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,531
    bounding boxes are by definition axis aligned.

    As KelsoMRK pointed out though. You use the ClosestPointOnBounds to get get the nearby wall, then you do extra calculations to get more detailed information.

    When you have a complex collision to determine you always first start by ruling out the obvious. For instance if you had two complex mesh collisions, before testing each face of the mesh you'd first make sure that the meshes were even near each other in the first place. If they were on other sides of the world, you can quickly rule out their collision based on that alone.

    With your OP approach you're testing ALL directions at a discrete detail of 1 degree. You can rule out MOST of them though just by getting a general idea of what side you're to test on. If the collider comes back saying it's on the right hand side, you can rule out all left hand facing rays.
     
  6. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,531
    Personally, I do wall running as well.

    I use sphere cast for the calculations (I tried capsule as well, but it's shape made for odd behaviour moving from wall to ceiling... I have ceiling climbing too... so we simplified to a sphere to clean it up and also because it's a faster calculation).

    You COULD just then sphere cast in the direction of the ClosestPointOnBounds. Then wherever the sphere hit, you go.

    That's not how I go about it though. I consider the players current velocity and the last surface normal. I do 2 main sphere casts to determine data (and a few extras if the data that comes back doesn't make sense).

    1) cast in the direction we're moving (if we're moving) to make sure we're not running into something.

    2) cast in the opposite direction of the last normal to get the newest normal. Most surfaces don't undulate that greatly, so a movement of a fraction of a unit should result in the surface being roughly in the same direction that it was last time... and because we're sphere casting, if it's a sharp corner (90 degrees or greater), the sides of the sphere cast will hit said corner.

    NOTE - consider using a 'skin' on your casts. The sphere size you cast with is ever so slightly smaller than the sphere you want to treat as your bounds. This helps with the behaviour of sphere casting... if the sphere already intersects a surface in the 0 position of its cast, it won't register an intersection down the line... it's considered "behind" the sphere, even though it's inside the sphere. A 'skin width' pulls back the sphere every so much so that it hits this. A thicker skin allows for greater detail in the direction your casting, while reduces detail in the direction adjacent to the direction you're casting.
     
  7. Badoobicus

    Badoobicus

    Joined:
    Jan 27, 2013
    Posts:
    10
    I'm still a bit confused on how your method works... I'm not entirely sure how to test if the player is near enough to a wall to start wall running.
     
  8. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,531
    Oh, to start... well listen for the collision enter event, check if it's a wall (check the normal of the surface, and any other stuff you may do... like maybe you tag walls that are runnable), and there you know you're near enough. If you want a distance check... just make the collider that you test with a trigger and larger than your physics collider.