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

How to make Physic Raycaster Take Precedence over Graphic Raycaster?

Discussion in 'Scripting' started by Indie_Dev, Jan 13, 2018.

  1. Indie_Dev

    Indie_Dev

    Joined:
    Dec 29, 2017
    Posts:
    35
    Essentially, I've got a drag and drop functionality that modifies my player object's velocity toward the cursor on drag movements. I've even got a check so that if your mouse leaves the screen (or finger on mobile) that it continues to move toward the last known good point.

    Unfortunately, I still cannot get it to move around my UI objects.

    The linked video demonstrates the issue.



    Drag Controls
    Code (csharp):
    1.  
    2. public class playerMovementController : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler{
    3.  
    4.     private RaycastResult lastGoodRaycast;
    5.  
    6.     public void OnBeginDrag(PointerEventData data)
    7.     {
    8.         PlayerController.Singleton.StartAdjustingVelocity ();
    9.         lastGoodRaycast = data.pointerCurrentRaycast;
    10.     }
    11.  
    12.     public void OnDrag(PointerEventData data)
    13.     {
    14.         // Send last good raycast from data.  Only update if current raycast is good
    15.         if (data.pointerCurrentRaycast.isValid) {
    16.             lastGoodRaycast = data.pointerCurrentRaycast;
    17.         }
    18.             Vector3 velocityData = new Vector3 (lastGoodRaycast.worldPosition.x, 0.0f, lastGoodRaycast.worldPosition.z);
    19.             PlayerController.Singleton.SetVelocityDataFromDrag (velocityData);
    20.     }
    21.  
    22.     public void OnEndDrag(PointerEventData data)
    23.     {
    24.         PlayerController.Singleton.StopAdjustingVelocity ();
    25.     }
    26. }
    27.  
    Player Controls (pertinent code)
    Code (csharp):
    1.  
    2.     void FixedUpdate () {
    3.         if (updateVelocity && (Vector3.Distance(velocityDataFromDrag, rb.position) > touchMargin)) {    // Only update if touch distance is sufficient distance... prevents stuttering
    4.             Vector3 moveTowardValue = velocityDataFromDrag - rb.position;
    5.             moveTowardValue.y = 0.0f;
    6.             moveTowardValue.Normalize ();
    7.  
    8.             rb.velocity = moveTowardValue * Time.deltaTime * movementSpeed;
    9.  }
    10.  
     
  2. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    Not sure about precedence, but are you asking (or would it satisfy your question) , if your drag ignored UI raycasts?
     
  3. Indie_Dev

    Indie_Dev

    Joined:
    Dec 29, 2017
    Posts:
    35
    It would. But the top left " . . ." is actually a button, so I don't think I can jut remove it as a raycast target, or set it to the Ignore Raycast layer...
     
  4. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    What I meant was if the pointerCurrentRaycast 's layer is the UI, then you do not set it as a valid target? (ie: last good raycast)
    Does that sound like it could work?
    I'm not talking about making it invalid for everything, just your script :)
     
    Indie_Dev likes this.
  5. Indie_Dev

    Indie_Dev

    Joined:
    Dec 29, 2017
    Posts:
    35
    Absolutely what I need! :)
     
  6. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    Which object, out of curiousity, is this script on?
     
  7. Indie_Dev

    Indie_Dev

    Joined:
    Dec 29, 2017
    Posts:
    35
    An empty transform with a "Quad" mesh collider, scaled up to fit my game screen. Nothing attached to it but the collider and the playerMovementControl code to detect dragging.
     
  8. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    So, I did a bit of testing (had some issue with getting a sprite to register my interfaces lol -- forgot the collider!)

    Anyways, so this is what I was thinking:
    Code (csharp):
    1.  
    2. Vector3 validPos = Vector3.zero;
    3.     public void OnDrag(PointerEventData data)
    4.     {
    5.         // Send last good raycast from data.  Only update if current raycast is good
    6.         if (data.pointerCurrentRaycast.isValid)
    7.         {
    8.             if (data.pointerCurrentRaycast.gameObject.layer == LayerMask.NameToLayer("UI"))
    9.             {
    10.                 validPos = data.position;
    11.                 print("valid pos = " + validPos);
    12.             }
    13.             else
    14.             {
    15.                 print("Was valid: " + data.position);
    16.                 lastGoodRaycast = data.pointerCurrentRaycast;
    17.             }
    18.         }
    19.         else print("Wasn't valid?");
    20.  
    I didn't add back in your specific things (obviously I didn't have access to your methods), but does that kind of give you an idea? For me, when I go over a button and I'm dragging, I still get the world position.
    If I'm dragging near the very top left corner and not on the button, I might have position x = 20, then I move slightly and I'm over the button, and I get '22'.
    However, I then realize that it doesn't even matter about the UI layer! hahaha.

    I think you were using a deprecated 'worldPosition' property on the raycast result. I just used the position of the pointer event, instead.
    So, you could safely just remove that UI portion, but if you ever wanted some UI layer aware code, that might be one way to do it! lol
    :)
     
  9. Indie_Dev

    Indie_Dev

    Joined:
    Dec 29, 2017
    Posts:
    35
    data.position is a Vector2 screen position, whereas I'm doing everything by Vector3 world position. All of the Camera.main functions to convert to world space require a Vector3, so how would I go about translating?
     
  10. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    Sorry... you're right. Well, instead of talking through those conversions (which is an option), I there's another one:
    Code (csharp):
    1.  
    2. if (data.pointerCurrentRaycast.gameObject.layer == LayerMask.NameToLayer("UI"))
    3. {
    4.      // this doesn't have to be made 'new' if you move it.
    5.    List<RaycastResult> rcr = new List<RaycastResult>(10);
    6.    EventSystem.current.RaycastAll(data, rcr);
    7.    bool found = false;
    8.    for(int i = 0; i < rcr.Count; ++i)
    9.    {
    10.       if (rcr[i].gameObject.layer == LayerMask.NameToLayer("UI")) continue;
    11.       validPos = rcr[i].worldPosition;
    12.        found = true; break;
    13.      }
    14.     if (found)
    15.     {
    16.         print("valid pos = " + validPos); // this was set in the loop.
    17.     }
    18.    else print("No valid hits."); // don't even think you can get here lol
    19.  
    Something like that? Only do the additional raycast (all) fetch, if you're over the UI ?
     
    Indie_Dev likes this.
  11. Indie_Dev

    Indie_Dev

    Joined:
    Dec 29, 2017
    Posts:
    35
    Ah! True, didn't even think about RaycastAll. Touche!
     
  12. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    :) Hope it works well for ya.
     
  13. fogsight

    fogsight

    Joined:
    Apr 6, 2010
    Posts:
    65
    Probably a bit too late, but you can also capture drag events on UI elements from GraphicRaycaster and relay them directly to the script capturing drag events using Physics2DRaycaster. This will effectively ignore UI.