Search code examples
touchspritegodotgdscript

Handle short and long clicks, drag and drop and sprites overlapping?


My game is going to be using touch controls and I'm having a lot of trouble implementing the following mechanics.

When I make a short click on a sprite, I want it to do a simple click event, in my code, it should highlight the sprite.

But when I make a long click on a sprite, it should at one point anchor to the cursor and be dragged around by it. When I release it, it should then stop following the cursor and stay in place.

Last but not least, I'm actually working with multiple sprites and I want to always influence the top-most sprite (z_index wise).

So right now, my code looks like this:

# Node2D, parent of my sprite
func _process(delta):
    # Checks for clicks and hold, calls function based on events
    if not owning_hand or not owning_hand.my_hand:
        is_holding = false
        return
    if Input.is_action_just_released("click"):
        owning_hand.drop_card()
        is_holding = false
    if not input_in_sprite:
        is_holding = false
        return
    if Input.is_action_just_pressed("click"):
        is_holding = true
    if Input.is_action_just_released("click"):
        if hold_time < HOLD_TARGET:
            owning_hand.clicked_cards.append(self)
        is_holding = false
    if is_holding:
        hold_time += delta
    else:
        hold_time = 0
    if hold_time >= HOLD_TARGET:
        owning_hand.held_cards.append(self)

func _input(event):
    # If the mouse is in the sprite, set input_in_sprite to true
    if not owning_hand or not owning_hand.my_hand:
        return
    if event is InputEventMouse and sprite.get_rect().has_point(to_local(event.position)) and not played:
        input_in_sprite = true
    else:
        input_in_sprite = false
# Position2D, represents the player's input and methods
func _process(delta): # Gets the top most card z_index wise
    # Checks for any clicked cards
    print(held_cards)
    if dragged_card:
        dragged_card.position = get_global_mouse_position()
    if clicked_cards:
        var top_card = clicked_cards[0]
        for Card in clicked_cards:
            if Card.z_index > top_card.z_index:
                top_card = Card
        clicked_cards.clear()
        highlight_card(top_card)
    if held_cards:
        var top_card = held_cards[0]
        for Card in held_cards:
            if Card.z_index > top_card.z_index:
                top_card = Card
        held_cards.clear()
        drag_card(top_card)

func drag_card(card):
    # Drags the card around
    dragged_card = card

func drop_card():
    # Drops the card
    dragged_card = null

func highlight_card(card):
    # Highlights the card
    card.move_rotate(card.position + transform.y * -HIGHLIGHT_HEIGHT, card.rotation, REORGANISE_TIME)

At the moment, the only issue is that dropping a sprite when there's another sprite under my cursor triggers the click event of the sprite isn't being dropped.

To be frank, the code is pretty much okay for what I'm doing. I'm asking here to see if anyone knows a better way to code those mechanics.


Solution

  • func _process(delta):
        # Checks for clicks and hold, calls function based on events
        if not owning_hand or not owning_hand.my_hand:
            is_holding = false
            return
        if Input.is_action_just_released("click"):
            if hold_time < HOLD_TARGET and input_in_sprite and is_holding:
                owning_hand.clicked_cards.append(self)
            if owning_hand.dragged_card:
                owning_hand.drop_card()
            is_holding = false
        if Input.is_action_just_pressed("click") and input_in_sprite:
            is_holding = true
        if is_holding:
            hold_time += delta
        else:
            hold_time = 0
        if hold_time >= HOLD_TARGET:
            owning_hand.held_cards.append(self)
    

    Coding it like this seems to make it work correctly. Basically, I used the is_holding bool to gather knowledge on if the sprite has been pressed earlier. If not, the is_action_just_released should completely ignore any action on the sprite.

    Feel free to suggest better ways to implement this