Search Unity

ARFoundation - Keeping GameObject within Plane Boundary

Discussion in 'AR' started by dwilliams82, Dec 22, 2018.

  1. dwilliams82

    dwilliams82

    Joined:
    Nov 4, 2018
    Posts:
    3
    Hey all,

    I am new to Unity and AR (and forum posting so apologies if formatting isn't correct). I am modifying the arfoundation-samples-master project to control the game object placed on the detected AR plane with on-screen joystick (free Virtual JoyStick Pack from the asset store). Unity version I started on was 2018.2.18f1 and am now running 2018.3.0f2. Build settings are set for Android and I am testing on a Pixel 2 XL.

    The placing of the object and moving it with the joystick works properly. However, I want to limit the game object's movement to the bounds of the plane it is on like what happens in the project by default when dragging the object by touch. I have looked through the ARSessionOrigin and ARPlaneManager code and can't figure out which code (or inspector settings?) is responsible for this behavior when dragging. I have also looked at the ARPlane API and tried iterating through all the planes in the current session's ARPlaneManager and using TryGetBoundary() on each one to test if the game object is within its boundary but it always came back false regardless of where the object is on the plane.

    I have the Joystick on a canvas and have an EventSystem in the scene. I have tried both making the canvas a child of ARSessionOrigin and not a child. The canvas is set to Screen Space - Overlay. I have a Canvas Scaler and GraphicRaycaster attached to the canvas although these may have been a result of previous testing, I can't remember offhand.

    The Vector3 created from the joystick movement is:
    Code (CSharp):
    1. Vector3 moveVector = (Vector3.right * joystick.Horizontal + Vector3.forward * joystick.Vertical);
    The object's rotation:
    Code (CSharp):
    1. spawnedObject.transform.rotation = Quaternion.LookRotation(moveVector);
    I have tried moving the object using this:
    Code (CSharp):
    1. requested_move = spawnedObject.transform.position +
    2.                     moveVector * moveSpeed * Time.deltaTime;
    3.                 spawnedObject.transform.position = requested_move;
    I have also tried moving using this:
    Code (CSharp):
    1. spawnedObject.transform.Translate(moveVector * moveSpeed * Time.deltaTime, Space.World);
    The code below is what I am trying to check with and is in PlaceOnPlane's Update(). The Added TouchPhase.Ended check was to keep the game object from being placed on the plane behind the on-screen joystick upon letting go of the screen while moving the joystick:

    Code (CSharp):
    1. if (m_SessionOrigin.Raycast(current_touch.position,
    2.                 s_Hits,
    3.                 TrackableType.PlaneWithinPolygon) &&
    4.                 current_touch.phase != TouchPhase.Ended)
    5.             {
    6.                 m_ARPlaneManager.GetAllPlanes(trackedPlanes);
    7.                 for (int x = 0; x < trackedPlanes.Count; x++)
    8.                 {                                       trackedPlanes[x].TryGetBoundary(currentPlanePoints, Space.World);
    9.                     for (int y = 0; y < currentPlanePoints.Count; y++)
    10.                     {
    11.                         if (currentPlanePoints[y] ==spawnedObject.transform.position)
    12.                         {
    13.                             Debug.Log("object inside plane");
    14.                         }
    15.                         else Debug.Log("object outside plane");
    16.                     }
    17.                 }
    18.             }
    When using the log to look at each of the positions of the planes vs the game object's the position, the game object's position is always very close to at least one of the points being checked.

    I feel like I am missing/misunderstanding some fundamental concept or something. World space vs Local Space. Utilizing a mesh somehow? This troubleshooting also prompted me to wonder what is the least intensive way of determining which plane in ARPlaneManager's list is being tracked. Just iterating through the list and checking TrackingState of each plane every Update()?

    Thanks
     
  2. tdmowrer

    tdmowrer

    Joined:
    Apr 21, 2017
    Posts:
    605
    You are checking whether the position of the spawned object exactly matches any of the planes' vertices. This will almost never happen; you want to know whether the object is inside or outside the plane, not if it is resting one of the corners.

    The full solution would be to perform 2D collision detection between the 2D silhouette of the placed object projected onto the plane with the convex polygon defined by the plane's vertices.

    An simpler approximation would be to raycast from several points on the placed object and check whether they hit the plane. The points you choose depends on how accurate you want the collision detection to be. If you just want to reproduce the behavior you get when moving the object with the PlaceOnPlane script, you can just use a single raycast from the placed object's position toward the plane.
     
  3. dwilliams82

    dwilliams82

    Joined:
    Nov 4, 2018
    Posts:
    3
    Hi tdmowrer, thanks for the reply.

    I had initially tried performing the check by doing a single raycast from the game object to the plane as you mentioned, in the following format:

    Code (CSharp):
    1. if (m_SessionOrigin.Raycast(spawnedObject.transform.position,
    2.                     s_Hits,
    3.                     TrackableType.PlaneWithinPolygon))
    4.                 {
    5.                     Debug.Log("within polygon");
    6.                 }  
    For me this seems to always return true whether the game object leaves the plane's polygon or not.
     
  4. tdmowrer

    tdmowrer

    Joined:
    Apr 21, 2017
    Posts:
    605
    There are 2 raycast methods: one takes a screen position and the other takes a ray. Passing a 3d position as the first argument is interpreted as a screen position and a ray is cast in the direction the camera is facing.

    Try constructing a ray instead. If the plane is horizontal, then it would be
    Code (CSharp):
    1. var ray = new  Ray(spawnedObject.transform.position, Vector3.down);
    2. var insidePlane = m_SessionOrigin.Raycast(ray, s_Hits, TrackableType.PlaneWithinPolygon);
    3.  
     
  5. dwilliams82

    dwilliams82

    Joined:
    Nov 4, 2018
    Posts:
    3
    That did it. I had tried both Raycast methods before as well but I had the Vector3 direction wrong.

    Many thanks!