Search Unity

Handling Event Input Offscreen

Discussion in 'UGUI & TextMesh Pro' started by BortStudios, Jan 8, 2016.

  1. BortStudios

    BortStudios

    Joined:
    May 1, 2012
    Posts:
    48
    I am working on a game where the player can manipulate an in game computer with a mouse. The way I am doing this is by having a canvas offscreen from the rendering camera which has its own camera that draws to a render texture that the primary camera then sees. What I want to do is use the event system exactly as it is, but on this "offscreen" canvas. It seems that the standalone input module only casts against elements that are on the screen, and so the only UI element it detects is the rendertexture.

    The off screen canvas has its own cursor, too. How do I handle using the event system from that cursor? I tried to make my own input module but I want all of the standalone input functionality exactly except using this graphical canvas cursor instead of the real one. How can I do this?
     
  2. Banemus

    Banemus

    Joined:
    Jul 11, 2013
    Posts:
    9
    Well, I have found solution but it is quite complicated.

    First you do this on your RawImage where you render the render target:

    Code (CSharp):
    1. public class RAW_IMAGE_RENDER_TEXTURE_INPUT_COMPONENT
    2.     : MonoBehaviour
    3. {
    4.     // -- PUBLIC
    5.  
    6.     // .. OPERATIONS
    7.  
    8.     public void Update()
    9.     {
    10.         RectTransform
    11.             rect_transform;
    12.  
    13.         rect_transform = GetComponent<RectTransform>();
    14.  
    15.         if ( RectTransformUtility.RectangleContainsScreenPoint( rect_transform, Input.mousePosition, null ) )
    16.         {
    17.              Vector2
    18.                  point;
    19.  
    20.              point = rect_transform.InverseTransformPoint( Input.mousePosition );
    21.              point.x += rect_transform.rect.width * 0.5f;
    22.              point.y += rect_transform.rect.height;
    23.  
    24.             RawImageInputModule.UpdateCursorPosition( Camera.ScreenToWorldPoint( point ) );
    25.         }
    26.     }
    27.  
    28.     // ~~
    29.  
    30.     public void UpdateCamera(
    31.         Camera camera
    32.         )
    33.     {
    34.         GetComponent<RawImage>().texture = camera.targetTexture;
    35.         Camera = camera;
    36.     }
    37.  
    38.     // -- PRIVATE
    39.  
    40.     // .. ATTRIBUTES
    41.  
    42.     [ SerializeField ] Camera
    43.         Camera = null;
    44.     [ SerializeField ] RawImageInputModule
    45.         RawImageInputModule = null;
    46.     [ SerializeField ] Canvas
    47.         Canvas = null;
    48. }
    49.  
    Then you add this to your EventManager:

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEngine.EventSystems;
    5. public class RAW_IMAGE_INPUT_MODULE
    6.     : StandaloneInputModule
    7. {
    8.     // -- PUBLIC
    9.  
    10.     // .. OPERATIONS
    11.  
    12.     public void UpdateCursorPosition(
    13.         Vector2 new_position
    14.         )
    15.     {
    16.         MousePosition = new_position;
    17.     }
    18.  
    19.     // ~~
    20.  
    21.     protected override MouseState GetMousePointerEventData(
    22.         int id = 0
    23.         )
    24.     {
    25.         // Populate the left button...
    26.         PointerEventData
    27.             leftData;
    28.         bool
    29.             is_created = GetPointerData( kMouseLeftId, out leftData, true );
    30.  
    31.         leftData.Reset();
    32.  
    33.         if ( is_created )
    34.         {
    35.             leftData.position = MousePosition;
    36.         }
    37.      
    38.         Vector2
    39.             pos = MousePosition;
    40.  
    41.         MousePosition = Input.mousePosition;
    42.  
    43.         leftData.delta = pos - leftData.position;
    44.         leftData.position = pos;
    45.         leftData.scrollDelta = Input.mouseScrollDelta;
    46.  
    47.         leftData.button = PointerEventData.InputButton.Left;
    48.  
    49.         eventSystem.RaycastAll( leftData, m_RaycastResultCache );
    50.      
    51.         var
    52.             raycast = FindFirstRaycast( m_RaycastResultCache );
    53.  
    54.         leftData.pointerCurrentRaycast = raycast;
    55.         m_RaycastResultCache.Clear();
    56.  
    57.         // copy the apropriate data into right and middle slots
    58.         PointerEventData
    59.             rightData;
    60.  
    61.         GetPointerData( kMouseRightId, out rightData, true );
    62.         CopyFromTo( leftData, rightData );
    63.         rightData.button = PointerEventData.InputButton.Right;
    64.  
    65.         PointerEventData
    66.             middleData;
    67.  
    68.         GetPointerData( kMouseMiddleId, out middleData, true );
    69.         CopyFromTo( leftData, middleData );
    70.         middleData.button = PointerEventData.InputButton.Middle;
    71.      
    72.         MouseState.SetButtonState( PointerEventData.InputButton.Left, StateForMouseButton( 0 ), leftData );
    73.         MouseState.SetButtonState( PointerEventData.InputButton.Right, StateForMouseButton( 1 ), rightData );
    74.         MouseState.SetButtonState( PointerEventData.InputButton.Middle, StateForMouseButton( 2 ), middleData );
    75.  
    76.         return MouseState;
    77.     }
    78.  
    79.     // -- PRIVATE
    80.  
    81.     // .. ATTRIBUTES
    82.  
    83.     Vector2
    84.         MousePosition;
    85.     MouseState
    86.         MouseState = new MouseState();
    87. }
    88.  
    Then on each offscreen canvas you add this:

    Code (CSharp):
    1. using System;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEngine.UI;
    5. using UnityEngine.EventSystems;
    6. public class OFF_SCREEN_GRAPHIC_RAY_CASTER
    7.     : GraphicRaycaster
    8. {
    9.     Canvas
    10.         myCanvas;
    11.  
    12.     Canvas MyCanvas
    13.     {
    14.         get
    15.         {
    16.             if ( myCanvas != null )
    17.             {
    18.                 return myCanvas;
    19.             }
    20.  
    21.             myCanvas = GetComponent<Canvas>();
    22.             return myCanvas;
    23.         }
    24.     }
    25.  
    26.     [NonSerialized]
    27.     private List<Graphic> m_RaycastResults = new List<Graphic>();
    28.     public override void Raycast(
    29.         PointerEventData eventData,
    30.         List<RaycastResult> resultAppendList
    31.         )
    32.     {
    33.         if ( MyCanvas == null )
    34.         {
    35.             return;
    36.         }
    37.  
    38.         // Convert to view space
    39.         Vector2
    40.             pos;
    41.  
    42.         if ( eventCamera == null )
    43.         {
    44.             pos = new Vector2( eventData.position.x / Screen.width, eventData.position.y / Screen.height );
    45.         }
    46.         else
    47.         {
    48.             pos = eventCamera.WorldToViewportPoint( eventData.position );
    49.         }
    50.  
    51.         // If it's outside the camera's viewport, do nothing
    52.         if ( pos.x < 0f || pos.x > 1f || pos.y < 0f || pos.y > 1f )
    53.         {
    54.             return;
    55.         }
    56.      
    57.         float
    58.             hitDistance = float.MaxValue;
    59.  
    60.         if ( MyCanvas.renderMode != RenderMode.ScreenSpaceOverlay && blockingObjects != BlockingObjects.None )
    61.         {
    62.             var ray = eventCamera.ViewportPointToRay( pos );
    63.             float dist = eventCamera.farClipPlane - eventCamera.nearClipPlane;
    64.  
    65.             if ( blockingObjects == BlockingObjects.ThreeD || blockingObjects == BlockingObjects.All )
    66.             {
    67.                 var hits = Physics.RaycastAll( ray, dist, m_BlockingMask );
    68.  
    69.                 if ( hits.Length > 0 && hits[ 0 ].distance < hitDistance )
    70.                 {
    71.                     hitDistance = hits[ 0 ].distance;
    72.                 }
    73.             }
    74.  
    75.             if ( blockingObjects == BlockingObjects.TwoD || blockingObjects == BlockingObjects.All )
    76.             {
    77.                 var hits = Physics2D.GetRayIntersectionAll( ray, dist, m_BlockingMask );
    78.  
    79.                 if ( hits.Length > 0 && hits[ 0 ].fraction * dist < hitDistance )
    80.                 {
    81.                     hitDistance = hits[ 0 ].fraction * dist;
    82.                 }
    83.             }
    84.         }
    85.  
    86.         m_RaycastResults.Clear();
    87.         Raycast( MyCanvas, eventCamera, eventData.position, m_RaycastResults );
    88.  
    89.         for ( var index = 0; index < m_RaycastResults.Count; index++ )
    90.         {
    91.             var go = m_RaycastResults[ index ].gameObject;
    92.             bool appendGraphic = true;
    93.  
    94.             if ( ignoreReversedGraphics )
    95.             {
    96.                 if ( eventCamera == null )
    97.                 {
    98.                     // If we dont have a camera we know that we should always be facing forward
    99.                     var dir = go.transform.rotation * Vector3.forward;
    100.                     appendGraphic = Vector3.Dot( Vector3.forward, dir ) > 0;
    101.                 }
    102.                 else
    103.                 {
    104.                     // If we have a camera compare the direction against the cameras forward.
    105.                     var cameraFoward = eventCamera.transform.rotation * Vector3.forward;
    106.                     var dir = go.transform.rotation * Vector3.forward;
    107.                     appendGraphic = Vector3.Dot( cameraFoward, dir ) > 0;
    108.                 }
    109.             }
    110.  
    111.             if ( appendGraphic )
    112.             {
    113.                 float distance = ( eventCamera == null || MyCanvas.renderMode == RenderMode.ScreenSpaceOverlay ) ? 0 : Vector3.Distance( eventCamera.transform.position, MyCanvas.transform.position );
    114.  
    115.                 if ( distance >= hitDistance )
    116.                     continue;
    117.  
    118.                 var castResult = new RaycastResult
    119.                 {
    120.                     gameObject = go,
    121.                     module = this,
    122.                     distance = distance,
    123.                     index = resultAppendList.Count,
    124.                     depth = m_RaycastResults[ index ].depth
    125.                 };
    126.                 resultAppendList.Add( castResult );
    127.             }
    128.         }
    129.     }
    130.  
    131.     /// <summary>
    132.     /// Perform a raycast into the screen and collect all graphics underneath it.
    133.     /// </summary>
    134.     [NonSerialized]
    135.     static readonly List<Graphic> s_SortedGraphics = new List<Graphic>();
    136.     private static void Raycast( Canvas canvas, Camera eventCamera, Vector2 pointerPosition, List<Graphic> results )
    137.     {
    138.         // Debug.Log("ttt" + pointerPoision + ":::" + camera);
    139.         // Necessary for the event system
    140.         var foundGraphics = GraphicRegistry.GetGraphicsForCanvas( canvas );
    141.         s_SortedGraphics.Clear();
    142.         for ( int i = 0; i < foundGraphics.Count; ++i )
    143.         {
    144.             Graphic graphic = foundGraphics[ i ];
    145.  
    146.             // -1 means it hasn't been processed by the canvas, which means it isn't actually drawn
    147.             if ( graphic.depth == -1 )
    148.                 continue;
    149.  
    150.             Vector3
    151.                 screenpoint = canvas.transform.worldToLocalMatrix.MultiplyPoint( pointerPosition ) + new Vector3( canvas.GetComponent<RectTransform>().rect.width * 0.5f, canvas.GetComponent<RectTransform>().rect.height * 0.5f );
    152.  
    153.             if ( !RectTransformUtility.RectangleContainsScreenPoint( graphic.rectTransform, screenpoint, eventCamera ) )
    154.                 continue;
    155.  
    156.             if ( graphic.Raycast( pointerPosition, eventCamera ) )
    157.             {
    158.                 s_SortedGraphics.Add( graphic );
    159.             }
    160.         }
    161.  
    162.         s_SortedGraphics.Sort( ( g1, g2 ) => g2.depth.CompareTo( g1.depth ) );
    163.         //        StringBuilder cast = new StringBuilder();
    164.         for ( int i = 0; i < s_SortedGraphics.Count; ++i )
    165.             results.Add( s_SortedGraphics[ i ] );
    166.         //        Debug.Log (cast.ToString());
    167.     }
    168. }
    169.  
    There might be a cleaner way to do it, but it works.

    Ow and don't forget to remove the standaloneinputmodule from your event manager and graphic ray caster from your offscreen canvases