I’m trying to get my head around the EventSystem in Unity but I’m having a hard time, the documentation is hard to find and uses terms like “This does what you expect it to” which is about as unhelpful as it gets. Had no luck finding decent tutorials either, probably using the wrong key words. The only stuff that came close was about building a custom event manager and dated to Unity version 5.x
Anyway, as a test, I want to log when I click the mouse somewhere on the screen. I have the script
public class ClickerImplementation : MonoBehaviour, IPointerDownHandler
{
public void Start()
{
Debug.Log("ClickerImplementation started");
}
public void OnPointerDown(PointerEventData eventData)
{
Debug.Log(name + " Game Object Clicked!" + eventData);
}
}
when I add this to UI element in the canvas it works as intended.
I then added the same script to my Tilemap object (on which I’d painted a bunch of tiles), and also added a Physics2DRaycaster on the (only) camera in the game. It doesn’t register any clicks on any of the tiles at all. I tried moving things around to Grid rather than tilemap and using a 3D Graphics raycaster but it didn’t work. I even tried adding a Canvas to the tilemap (in worldspace) with a Graphics raycaster, but no luck either.
I’m pretty sure there’s something super basic I’m doing wrong, but for the life of me I have no idea even where to start reading up on this. I’ve tried Namespace UnityEditor.EventSystems | Unity UI | 1.0.0 and Event System | Unity UI | 1.0.0
but while they might be good as references for those who already know what they’re doing, they’re no use as an introduction.
I am a fool. Thanks for the help @Kurt-Dekker , but I got there before reading your reply.
In the best tradition of rubber ducking, a problem that had me completely perplexed for hours was immediately solved upon writing the problem. The thing is to add a Tilemap Collider 2D to the tilemap, as well as a Physics 2D Raycaster to the relevant camera. This makes sense, as well as a thing that “emits” rays, you need that registers their hits. The final code is
public class ClickerImplementation : MonoBehaviour, IPointerDownHandler
{
private Tilemap Tilemap;
public void Start()
{
Tilemap = GetComponent<Tilemap>();
}
public void OnPointerDown(PointerEventData eventData)
{
Vector2 screenPos = eventData.position;
Vector3 worldPos = eventData.pointerCurrentRaycast.worldPosition;
Vector3Int coord = Tilemap.WorldToCell(worldPos);
Debug.Log($"Clicked at {screenPos} on screen, landing on {worldPos} on the tile map which is tile {coord}");
}
}
Note that the behaviour is very similar to a more common solution to the question of how to click on tile map that I could hack together from old forum posts
public class ClickerImplementation : MonoBehaviour
{
private Tilemap tilemap;
private Plane tilemapPlane;
private void Start()
{
tilemap = GetComponent<Tilemap>();
tilemapPlane = new Plane(Vector3.forward, tilemap.transform.position);
}
private void Update()
{
if (Input.GetMouseButtonDown(0) && Camera.main != null)
{
Vector2 screenPos = Input.mousePosition;
Ray ray = Camera.main.ScreenPointToRay(screenPos);
tilemapPlane.Raycast(ray, out float enter);
Vector3 worldPos = ray.GetPoint(enter);
Vector3Int coord = tilemap.WorldToCell(worldPos);
Debug.Log($"Clicked at {screenPos} on screen, landing on {worldPos} on the tile map which is tile {coord}");
}
}
}
Except for two things. Firstly, it doesn’t run on Update() which (presumably) makes it faster? Much tidier anyway. But more importantly, it’s aware of other game objects that might block the ray. For example, it will behave nicely if you click on some part of the UI that hides the Tilemap. Am I correct in thinking that the former implementation is generally to be preferred over the latter?
Anyway, I’ll leave this here as a monument to my mistakes, and hopefully to save others from my agony haha
It’s just kinda different layers entirely. Input is the entire input stream, unmodified by anything going on. In contrast, the pointer down stuff is “what has made it through all the raycast targets to this point here.”
So the former would be good if you just want “click anywhere” to work regardless.
The latter would be good if you cared that clicking the menu button should NOT also trigger a “clicked anywhere” event, eg., you are playing in a larger UI context.
Ok, thanks, the IPointerDownHandler is definitely what I want and it feels like the more general use case.
As a follow up, is there a way to catch keypresses (on a keyboard) in the same way? That is, with an EventSystem Interface, rather than by checking on an object in Update(). In this case there is no ray that goes through the mouse pointer, but it would still be possible for objects with this OnButtonPress() method to be hidden behind the UI when they shouldn’t respond. If several objects with this method are all visible then they should all have their OnButtonPress() called.
I think you would do your own raycast with EventSystem.RaycastAll, based on where the mouse is, then take whatever action you want when you detect a key with Input.GetKeyDown().
Input.GetKeyDown() is only valid in Update()… so I think it would be a little bit like:
if (keydown)
{
// see what I hit with EventSystem.RaycastAll
// see if those things have an IPointerDownHandler interface
// (or perhaps another interface you make; use GetComponent<T>())
// tell it "hey I key-mouse-touched you" via that interface
}
Yes, I see how that would work. I was hoping to avoid doing anything in update though. I’d much rather do it by listening to Input Module events. Whether it hits all the subscribers that are visible, or the closest one to the camera, or the most recently added doesn’t matter so much, I know how to customise that. It’s the whole framework for handling keyboard presses via EventSystem that I can’t find.
It says here“The built in Input Modules are designed to support common game configurations such as touch input, controller input, keyboard input, and mouse input.” Yet I can’t find any Interface for keyboard buttons listed here. What am I missing? Or do I have to write my own events, interface, and InputModule for keyboard events?