Canvas Layer and Input Events

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By Stevo
:warning: Old Version Published before Godot 3 was released.

I’ve add a CanvasLayer and added a Sprite, Area2D and CollisionShape2D as child nodes.

I cannot get any of the children in the CanvasLayer to receive mouse input events. Only the root node is receiving input events.

Why is this?

does your area2d have the “pickable” property enabled ?

MrMonk | 2017-04-17 17:38

I have the same issue as him and in my case I have “pickable” property enabled, in fact, if I move the Sprite>>Area2D >>Collision block outside the CanvasLayer it works perfect. But I’m not able to make it works inside the CanvasLayer

AlvaroAV | 2018-05-03 20:24

:bust_in_silhouette: Reply From: NerdP

I have the same problem. Does anybody have any clue?
I tried to explore the c++ sources but didnt succeed

:bust_in_silhouette: Reply From: AlvaroAV

I don’t think this is the best solution but this is the workaround I made to detect clicks on a joystick node that is child of a CanvasLayer. I have a structure like this:

  • mainNode
  • World
    • Player
      • Camera2D
 - Monsters
  • UI (this is the CanvasLayer)

  • health

  • menu

  • joystick (I want to manage click here)

    • joystickBtn

What I did was:

  • Add a script on the “joystick” node
  • Manually detect clicks by position
  • If click position belongs to the area I want, I call a function from the Player node

Sample code for joystick node (Godot 3):

extends Node2D
onready var joystickBtn = get_node("joystickBtn")
var btnPos = Vector2(0,0)
var btnSize = Vector2(0,0)
var btnRect2 = Rect2(Vector2(0,0), Vector2(0, 0))

func _ready():
    set_process_input(true)

    # Generate a Rect2 on the place we want to manage click
    # For this example the are will be obtained from a button
    btnPos = joystickBtn.get_global_position()
    btnSize = joystickBtn.get_size()
    btnRect2 = Rect2(btnPos, btnSize)     

func _input(event):
    var player = get_node("/root/mainNode/World/Player")
    if event is InputEventMouseButton: # If event is mouse click
        # Get click position
        var clickPos = Vector2(event.position.x, event.position.y)

        # If event is pressed and position belongs to btnRect2
        if event.pressed and btnRect2.has_point(clickPos):
            player.my_custom_function("arg1", "arg2")
            
:bust_in_silhouette: Reply From: Jaris

For this I used ColorRect node as background and changed the atribute in “Mouse” - “Filter” from “Stop” to “Ignore”. And my input events start to work like a charm.

I think its usable for every node in Control family as they have Mouse/filter parameter

The problem in this case is that the CanvasLayer where the object is located blocks the input_event. Your solution works when it is a Control node that blocks the object.

luislodosm | 2021-03-02 21:23

:bust_in_silhouette: Reply From: luislodosm

The only solution I have found is to have all objects that make use of input events in nodes that inherit from CanvasItem like Node2D or Control, but never inside a CanvasLayer node, which inherits from Node and blocks all input events.