0 votes

I have a grid of tiles that change sprite textures when clicked on (Black or white for marked or not marked). I am trying to get it to the point where if you then drag the clicked mouse over other tiles, they will also change. The code is like this:

export (Texture) var Blank
export (Texture) var Filled

var filled = false

func _on_Tile_input_event( ev ):
    if(ev.type==InputEvent.Mouse_Button and ev.pressed):
        if (ev.button_index==BUTTON_LEFT):
            if(filled)
                get_node("Sprite").set_texture(Blank)
                filled = false
            else:
                get_node("Sprite").set_texture(Filled)
                filled = true

func _on_Tile_mouse_enter():
    if Input.is_mouse_button_pressed(BUTTON_LEFT):
        if(filled)
            get_node("Sprite").set_texture(Blank)
            filled = false
        else:
            get_node("Sprite").set_texture(Filled)
            filled = true

If I click outside of any tiles and then drag onto tiles, they will fill in as the mouse goes over them, just like I expect. If I click on a single tile, that one will change, just as I expect. However, if I click on a tile and then drag, only that first tile will change, and the _on_Tile_mouse_enter function will not be called. Its like the _on_Tile_input_event function has captured the event system and won't allow anything else through until I release the mouse button. Is there a way to tell it it can go on its way after registering the mouse down, and no need to wait around for the mouse up?

in Engine by (30 points)
edited by

You should put a print() statement at the front of these enter events to confirm that.

Great idea- When I do so, I see what I described above as well. If I am not clicking on a tile first, every time I mouse over a tile I see my print statement, as expected. But if I have clicked on a tile first and drag away from it, no print statements are made for the mouse overs. Its like it can't handle other mouse inputs until I let up the mouse.

What's that code look like? I haven't been able to reproduce what you're describing.

Here is a minimal example I created that exhibits the problem. https://github.com/JymWythawhy/MinimalExample

Took a look. That does seem unusual. There was a brief discussion here, but I didn't really see anything else: https://github.com/godotengine/godot/issues/6362

2 Answers

+1 vote
Best answer

I see, it may be a designed that way for drag and drop reasons.

If you want to circumvent that behavior I have two suggestions. The first is a bit hacky and may create other undesired effects, so it may need other flags. It would be to code a mouse button release event.

func _on_Tile_input_event( ev ):
    if(ev.type==InputEvent.MOUSE_BUTTON and ev.pressed):
        if (ev.button_index==BUTTON_LEFT):
            if(filled):
                get_node("Sprite").set_texture(Blank)
                filled = false
            else:
                get_node("Sprite").set_texture(Filled)
                filled = true

            # Release button
            var event = InputEvent()
            event.type = InputEvent.MOUSE_BUTTON
            event.button_index=BUTTON_LEFT
            event.pressed = false
            get_tree().input_event(event)

The second solution, which may be more robust, but will take a little extra effort to implement, is to create your own mouse over detection. You would poll the mouse coordinates, and use rectangle collision detection to know if the cursor is in bounds of any given tile.

Many nodes can return their rectangle as Rect2, and inside that is an collision method where you can send the mouse coordinates as a Vector2.

http://docs.godotengine.org/en/stable/classes/class_rect2.html#class-rect2-has-point

There might be some other option for input handling that escapes me, but it may be because it's obscure.

by (4,944 points)
selected by

Thanks! I will try both of these.

You're welcome. ^_^

Ran into the same problem and the has point method worked for me inside the button up event:

get_rect().has_point(get_global_mouse_position())

get_rect() didn't quite work for me. Since it checks as if the rect always starts in the top-left corner of the screen.
I used this get_global_rect() instead:

get_global_rect().has_point(get_global_mouse_position())

Thanks a lot for the help though guys.

+1 vote

I've also struggled with this issue and I found that you can apply drop event on any Control node. This way mouse_entered event will continue firing while starting pressed event on same node.

func get_drag_data(position):
  print("start drag")
  return true

func can_drop_data(pos, data):
  print("dragging")
  return true

func drop_data(pos, data):
  print("drop")

Tested in Godot 3.1.1


Edit:
There is better dnd solution using the input event, and its good to be used as a guiinput signal from a Control node.

var dragging = false

func _input(event):
  if event is InputEventMouseButton:
    if dragging:
      dragging = false
      print("drop")
    else:
      dragging = true
      print("start drag")

  elif event is InputEventMouseMotion:
    if dragging:
      print("dragging")
by (20 points)
edited by
Welcome to Godot Engine Q&A, where you can ask questions and receive answers from other members of the community.

Please make sure to read How to use this Q&A? before posting your first questions.
Social login is currently unavailable. If you've previously logged in with a Facebook or GitHub account, use the I forgot my password link in the login box to set a password for your account. If you still can't access your account, send an email to webmaster@godotengine.org with your username.

Categories