Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 12 Next capture
2021 2022 2023
1 capture
12 Jun 22 - 12 Jun 22
sparklines
Close Help
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
  • Asset Store
  • Get Unity

UNITY ACCOUNT

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account
  • Blog
  • Forums
  • Answers
  • Evangelists
  • User Groups
  • Beta Program
  • Advisory Panel

Navigation

  • Home
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
    • Blog
    • Forums
    • Answers
    • Evangelists
    • User Groups
    • Beta Program
    • Advisory Panel

Unity account

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account

Language

  • Chinese
  • Spanish
  • Japanese
  • Korean
  • Portuguese
  • Ask a question
  • Spaces
    • Default
    • Help Room
    • META
    • Moderators
    • Topics
    • Questions
    • Users
    • Badges
  • Home /
  • Help Room /
avatar image
0
Question by Alex__ · Dec 05, 2019 at 05:18 PM · raycastingeventsystemcustom

Problems with EventSystem when implementing a custom raycaster

Context: I'm rendering a 3D scene to a RenderTexture and showing it on a UI RawImage. In order to implement correct mousebehaviour, I decided to make my own implementation of a Raycaster, by rendering a second RenderTexture which uses a replacement shader. This shader gets a color value at object instantiation which corresponds to a unique ID. The ID<>GameObject references are then stored in a dictionary for easy retrieval.


I based this "SelectionTexture" idea on the following article: https://web.archive.org/web/20170828092436/http://blog.celestial-static-games.de/2017/08/25/pixel-perfect-selection-using-shaders/


And decided that it'd be great to have simple eventsystem handlers on my selectable gameobjects (ie IPointerClickHandler etc) that are being rendered on the texture. Realized I could do this for non-UI gameobjects when stumbling upon this: https://answers.unity.com/questions/1161275/can-i-make-a-non-ui-gameobject-draggable-by-implem.html


So I made a CustomRaycaster which inherits from BaseRaycaster and implemented both Raycast() and eventCamera. Inside raycast it'll check if the pointer is over the corresponding UI texture and if yes, it'll do some calculations to get the corresponding pixel's color from the texture. It then uses that color to get the corresponding gameobject from the dictionary. rgba(0,0,0,0) meaning no selection.


So far so good! This is actually working quite well and gives us pixel-perfect selection without the use of any colliders or some type of mesh-raycasting. Furthermore, we can have multiple cameras w their own RenderTextures to show multiple viewpoints of the scene (I'm making a type of editor, as you might have guessed).


Where it goes wrong: I put a cube in the scene, which is being correctly found by my custom raycaster. I then added a monobehaviour with IPointerClickHandler and IPointerEnterHandler. The weird thing is that OnPointerEnter and OnPointerExit are being called EVERY frame that my mouse is over the object. And OnPointerClick, is only being called once every 5-10 clicks. So it kinda works, but not as intended.


So I'm guessing I'm missing some important detail about how the EventSystem interprets the raycastresult. Perhaps the values in my result are incorrect? Here's the code:

 public class CustomRaycaster : BaseRaycaster
     {
 
         public override int sortOrderPriority
         {
             get
             {
                 return base.sortOrderPriority;
             }
         }
 
         public override int renderOrderPriority
         {
             get
             {
                 return base.renderOrderPriority;
             }
         }
 
         protected CustomRaycaster()
         { }
 
 
         private IRenderSelectionTextureContainer m_SceneView;
         /// <summary>
         /// SceneView is the View-behaviour attached to the actual UI-Image which displays the RenderTexture
         /// It holds references to:
         /// - The actual 3D scene with object map
         /// - The dynamic panel util -> panel
         /// - The RenderTexture
         /// - The SelectionTexture -> texture
         /// </summary>
         private IRenderSelectionTextureContainer sceneView
         {
             get
             {
                 if (m_SceneView != null)
                     return m_SceneView;
 
                 m_SceneView = GetComponent<IRenderSelectionTextureContainer>();
                 if (m_SceneView == null)
                     Debug.LogError("Failed to find IRenderSelectTextureContainer as a component");
                 return m_SceneView;
             }
         }
 
         private ISelectionTextureObjectMapContainer m_objMapContainer;
         private ISelectionTextureObjectMapContainer objMapContainer
         {
             get
             {
                 if (m_objMapContainer != null)
                     return m_objMapContainer;
 
                 m_objMapContainer = sceneView.GetObjMapContainer();
                 return m_objMapContainer;
             }
         }
 
         private PanelUtil m_Panel;
         /// <summary>
         /// Easy access to info about the dynamic panel to find
         /// - center
         /// - corners
         /// - width/height, etc
         /// </summary>
         private PanelUtil panel
         {
             get
             {
                 if (m_Panel != null)
                     return m_Panel;
 
                 m_Panel = sceneView.GetPanelUtil();
                 return m_Panel;
             }
         }
 
         private RenderTexture m_Texture;
         /// <summary>
         /// The SceneView's SelectionTexture
         /// </summary>
         private RenderTexture texture
         {
             get
             {
                 if (m_Texture != null)
                     return m_Texture;
 
                 m_Texture = sceneView.GetSelectionTexture();
                 return m_Texture;
             }
         }
 
         private RectTransform m_RectTransform;
         /// <summary>
         /// The SceneView's RectTransform
         /// </summary>
         private RectTransform rectTransform
         {
             get
             {
                 if (m_RectTransform != null)
                     return m_RectTransform;
 
                 m_RectTransform = transform as RectTransform;
                 return m_RectTransform;
             }
         }
 
         // calculation stuff
         Vector2 localPos; //MousePos
         Vector3[] localCorners; //LocalCorners of the transfo
         float imgWidth, imgHeight;
         Vector2 positionNormalizedForTexCoords;
         Texture2D texture2D, tempTex; //Temp "screenShot" of the rendertext
         Color32 selectCol;
 
         // This is the generalized raycast function (called like any other type of raycaster)
         public override void Raycast(PointerEventData eventData, List<RaycastResult> resultAppendList)
         {
             if (texture == null || !sceneView.IsMouseOver)
                 return;
 
             if(texture2D == null || tempTex == null || localCorners == null)
             {
                 localCorners = new Vector3[4];
                 texture2D = new Texture2D(texture.width, texture.height, TextureFormat.ARGB32, false);
                 tempTex = new Texture2D(texture.width, texture.height);
             }
 
             RectTransformUtility.ScreenPointToLocalPointInRectangle(rectTransform, eventData.position, eventCamera, out localPos);
 
             rectTransform.GetLocalCorners(localCorners);
             imgWidth = localCorners[3].x - localCorners[0].x;
             imgHeight = localCorners[1].y - localCorners[0].y;
 
             positionNormalizedForTexCoords.x = localPos.x / imgWidth + 0.5f;
             positionNormalizedForTexCoords.y = localPos.y / imgHeight + 0.5f;
 
             RenderTexture.active = texture;
             texture2D = GetRTPixels(texture);
 
             selectCol = texture2D.GetPixel((int)(positionNormalizedForTexCoords.x * (float)texture.width), (int)(positionNormalizedForTexCoords.y * (float)texture.height));// .GetPixelBilinear(positionNormalizedForTexCoords.x, positionNormalizedForTexCoords.y);
             //selectCol = texture2D.GetPixelBilinear(positionNormalizedForTexCoords.x, positionNormalizedForTexCoords.y);
             RenderTexture.active = null;

             // If color is (0,0,0,0) it means no selection
             if (objMapContainer.ColorToUInt(selectCol) == 0)
                 return;
 
             //Debug.Log("Color is: " + selectCol + " texcoord: " + positionNormalizedForTexCoords + " localpos mouse: "+localPos);
 
             GameObject go = objMapContainer.GetGameObjectByColor(selectCol);
             //Debug.Log("Found object: " + go.name);
 
             if (go == null)
             {
                 Debug.LogWarning("Got a null GO from the map? why?");
                 return;
             }
 
             // Can there ever be more than 1 result?? I don't think so
 
             var castResult = new RaycastResult
             {
                 gameObject = go,
                 module = this,
                 distance = 0,
                 index = resultAppendList.Count,
                 depth = 100
             };
             resultAppendList.Add(castResult);
 
         }
 
         public override Camera eventCamera
         {
             get
             {
                 return sceneView.Cam2D;
             }
         }
 
 
 // TODO- this code is weird, needs to be refactored
         public Texture2D GetRTPixels(RenderTexture rt)
         {
             RenderTexture currentActiveRT = RenderTexture.active;
 
             RenderTexture.active = rt;
 
             // Create a new Texture2D and read the RenderTexture image into it
             //Texture2D tex = new Texture2D(rt.width, rt.height);
 
             tempTex.ReadPixels(new Rect(0, 0, tempTex.width, tempTex.height), 0, 0);
 
             RenderTexture.active = currentActiveRT;
             //  Destroy(tex, );
 
             return tempTex;
         }
 
 
     }
Comment
Add comment
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users

0 Replies

· Add your reply
  • Sort: 

Your answer

Hint: You can notify a user about this post by typing @username

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Follow this Question

Answers Answers and Comments

195 People are following this question.

avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image

Related Questions

EventSystem raycasting on World Canvas gameobject always returning worldPosition of zero 2 Answers

need to block raycast with UI 2 Answers

Raycast detect in object (problem) 0 Answers

Object referance not set to an instance of an object? 1 Answer

My RayCast isn't facing forward 1 Answer


Enterprise
Social Q&A

Social
Subscribe on YouTube social-youtube Follow on LinkedIn social-linkedin Follow on Twitter social-twitter Follow on Facebook social-facebook Follow on Instagram social-instagram

Footer

  • Purchase
    • Products
    • Subscription
    • Asset Store
    • Unity Gear
    • Resellers
  • Education
    • Students
    • Educators
    • Certification
    • Learn
    • Center of Excellence
  • Download
    • Unity
    • Beta Program
  • Unity Labs
    • Labs
    • Publications
  • Resources
    • Learn platform
    • Community
    • Documentation
    • Unity QA
    • FAQ
    • Services Status
    • Connect
  • About Unity
    • About Us
    • Blog
    • Events
    • Careers
    • Contact
    • Press
    • Partners
    • Affiliates
    • Security
Copyright © 2020 Unity Technologies
  • Legal
  • Privacy Policy
  • Cookies
  • Do Not Sell My Personal Information
  • Cookies Settings
"Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere (more info here). Other names or brands are trademarks of their respective owners.
  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Spaces
  • Default
  • Help Room
  • META
  • Moderators
  • Explore
  • Topics
  • Questions
  • Users
  • Badges