How to detect double click in _process()?

Intro

I know how to detect double click in _input()
event.is_double_click()
But now I need to detect double click in _process().
It works with InputEvent class, but it didn’t work with usual Event that could be used in _process().

What I tried

I tried to use InputEvent in process() but can’t figure out how to make it work.
Got error
Parse Error: Cannot call non-static function "is_action_pressed()" on the class "InputEvent" directly. Make an instance instead.

I tried to make double click event in InputMap by double clicking in “listening for input” menue. But It works as usual click.
изображение
I don’t wanna use detecting scheme with timer and want to find more elegant solution.

Question(s)

Is it possible to make work InputEvent in process?
Why I can create double click in InputMenu?
Wat other ways to detect double click?

Godot 4.2

InputEvent is first of all a class mainly used in _input (and its variants such as _unhandled_input…) function, and not a singleton (unlike Input mainly used in _process and _physics_process functions). Moreover, is_action_pressed is not a static function like the error says. Basically, it means that you need an instance of the class to execute the method (like event.is_action_pressed(...) in input functions) : you can’t write InputEvent.is_action_pressed(...) (since an instance is needed). This is why is_action_pressed is called “non_static”.

Since in the _process function, we do not have access to an InputEvent, we have to implement your algorithm using the Input singleton, by polling (i.e. constantly checking out the input each and every frame, instead of checking it each time it changes).

A way of implementing the double click is by listening to when the user clicks, and the delta time between every consecutive clicks. If the delta time is inferior to an amount of time (considered as the max duration between two clicks in a double click), then we can consider it as double clicked.

As you have surely noticed, it is a bit more tedious than using the _input function with the event argument, since we have to implement our own click and double-click detection (since there is only one method of the Input singleton that is related to the mouse : Input.is_mouse_button_pressed(...).

I have tried myself to code it for fun, here’s what I got. Change whatever is needed so that it fits your initial code.

class_name DoubleClickNode
extends Node


## Detects when user double-clicked


## Max duration between two clicks
const MAX_CLICK_DELTA := 0.15
## Max click duration
const MAX_CLICK_DURATION: float = 0.1
## The mouse button we're listening (change it if wanted)
const CLICK := MouseButton.MOUSE_BUTTON_LEFT


## Used to track down the delta time between two clicks
var click_delta: float = 0.0
## Used to measure the amount of time the button was pressed
var click_duration: float = 0.0
## Used to check if we consider that the user has clicked
var already_clicked := false


func _process(delta: float) -> void:
	detect_click(delta)
	

## Detects when we have considered that the user has clicked
func detect_click(delta: float) -> void:
	# User is pressing the mouse button
	if Input.is_mouse_button_pressed(CLICK):
		click_duration += delta
	
	# User released or is not pressing the mouse button
	elif not Input.is_mouse_button_pressed(CLICK):
		click_delta += delta
		if click_duration <= MAX_CLICK_DURATION and click_duration > 0.0:
			handle_click()
		click_duration = 0.0
	
	
	# Resets already_clicked when too much time has passed before another click occured
	if click_delta > MAX_CLICK_DELTA:
		already_clicked = false


## Handles each considered click occurence happens
func handle_click() -> void:
	# Not clicked? => clicked already, we're waiting for the next click now
	if not already_clicked:
		already_clicked = true
		
	# We're checking if not too much time has passed since the previous click
	# (Implies that already_clicked == true)
	elif click_delta <= MAX_CLICK_DELTA:
		handles_double_click()
		
	# Resets the variable each time a click occurs
	click_delta = 0.0


## Handles when user double-clicked
func handles_double_click() -> void:
	# Write code here
	print("DOUBLE CLICK")

I have tested this in Godot 4.2, it should work.

If you want to check another mouse button, simply change the constant’s CLICK value to another MouseButton value (cf enum @GlobalScope.MouseButton)

My answers to your question would be:

  1. No, since no InputEvent argument is passed along the _process(delta: float) -> void.
  2. You’re not “creating” a double click with the InputEvent, rather you’re using a built-in method in this class that detects for you the user’s double-clicks (instead of coding one like I did with the Input singleton).
  3. cf my previous reply where I put my solution to your problem

Yes, use the Input singleton

Input.is_action_just_pressed("double_click")

Never seen that “Left Mouse Button (Double Click)” before, seems like it registers with single clicks sadly. You could hold a variable from _input into _process, it might add a frame of delay, but asking for double clicks already adds delay inheritly.

var did_double_click: bool = false

func _input(event: InputEvent) -> void;
    if event is InputEventMouseButton:
        if event.is_action_pressed("double_click") and event.double_click:
            did_double_click = true

func _process(delta: float) -> void:
    if did_double_click:
        print("double clicky")
    did_double_click = false

thanks for hte answer! Your solution looks interesting, but for my arcitecture better solution is to work with _input() and _process() at the same time