At the moment I’m using UI panel WITH Image component. Image is invisible.
Is it possible to remove image component but mark this object as Raycast target?
After reading over @Statement’s incredibly helpful answer, I decided to try making a non-drawing Graphic
subclass. The result was surprisingly simple, and in my testing has worked without problems (descendant-GO components still layout and draw as expected).
The NonDrawingGraphic
class:
using UnityEngine;
using UnityEngine.UI;
/// A concrete subclass of the Unity UI `Graphic` class that just skips drawing.
/// Useful for providing a raycast target without actually drawing anything.
public class NonDrawingGraphic : Graphic
{
public override void SetMaterialDirty() { return; }
public override void SetVerticesDirty() { return; }
/// Probably not necessary since the chain of calls `Rebuild()`->`UpdateGeometry()`->`DoMeshGeneration()`->`OnPopulateMesh()` won't happen; so here really just as a fail-safe.
protected override void OnPopulateMesh(VertexHelper vh) {
vh.Clear();
return;
}
}
An optional NonDrawingGraphicEditor
class, put in an Editor/
folder in your project, will make sure Unity doesn’t show the “Color” and “Material” fields in the Inspector, which we’d rather not see since they don’t actually affect anything.
using UnityEngine;
using UnityEditor;
using UnityEditor.UI;
[CanEditMultipleObjects, CustomEditor(typeof(NonDrawingGraphic), false)]
public class NonDrawingGraphicEditor : GraphicEditor
{
public override void OnInspectorGUI ()
{
base.serializedObject.Update();
EditorGUILayout.PropertyField(base.m_Script, new GUILayoutOption[0]);
// skipping AppearanceControlsGUI
base.RaycastControlsGUI();
base.serializedObject.ApplyModifiedProperties();
}
}
Usage couldn’t be simpler:
So the GraphicRaycaster work on Graphic objects (see EventSystem, Raycasters).
5 is a MaskableGraphic which is a Graphic, so this is why Image acts a raycast blocker.
Theres Canvas Group (scripting reference) which according to the docs sound like it would do what you ask, but I was not able to get click events without a Graphic attached. For reference, here’s the script I used to test if it worked, perhaps I did something wrong etc. It didnt appear to get input without a Graphic anyway. As soon as I add an image etc, it works.
using UnityEngine;
using UnityEngine.EventSystems;
public class PrintOnPointerClick : MonoBehaviour, IPointerClickHandler
{
void IPointerClickHandler.OnPointerClick(PointerEventData eventData)
{
print("Clicked " + name);
}
}
Either you have to use any Component that subclasses from Graphic (such as Image, or create your own), or you have to provide a new Raycaster, such as a Physics2DRaycaster or a custom raycaster that extend from BaseRaycaster.
Unfortunately, setting alpha to zero will still cause the quad to be drawn, causing alpha blending which could impact performance on mobile devices. The method I used to try this out was to slide alpha all the way to zero and then use a material which doesn’t use the alpha channel, but output a solid color instead. Therefore we can assume that the quad will be rendered, unless the system has a default mode for standard (blank) materials with zero alpha.
However for the GraphicRaycaster to block, the bare minimum is to have a Graphic, and graphics will be rendered. If you subclass Graphic, you’ll notice it’ll draw a quad on the transform so there seems to be no way out without rendering something, using GraphicRaycaster alone…
Raycasters, then…
Any custom built Raycasters will automatically work with Unitys EventSystem component. All you have to do is to implement the abstract method and property to get going, and possibly override any of the few virtual members. You can also take a look at Unity 5.2 UI open source code to see how the systems interact with each other. If you use a secondary Raycaster that detects your object, then you don’t need a Graphic to get the pointer click or block the ray.
public abstract Camera eventCamera { get; }
public abstract void Raycast(PointerEventData eventData, List<RaycastResult> resultAppendList);
Finally this works well: create “Text” uGUI component inside Canvas, clear its “Text” value and set “Raycast Target” to “true”. No extra draw call but clicks are intercepted. I’m not the author for the solution, I’ve just seen it somewhere in the Internet. Thank you, the author!
This is the only thing you actually need!
using UnityEngine.UI;
public class RaycastTarget : Graphic
{
public override void SetMaterialDirty() { return; }
public override void SetVerticesDirty() { return; }
}
Tested in Unity 2020.3.18f1. The image with alpha set to 0 will not be drawn.
Guys, no need to create extra component.
Just set Color’s alpha channel to 0 of the Image component and check “Cull Transparent Mesh” option on in Canvas Renderer component on the same GameObject.
Additional draw call in Frame Debugger is gone.