Skip to content

Instantly share code, notes, and snippets.

@figgleforth
Last active September 18, 2020 07:01
Show Gist options
  • Save figgleforth/c4fd7cc605e3570d8f53ab694cf3fc74 to your computer and use it in GitHub Desktop.
Save figgleforth/c4fd7cc605e3570d8f53ab694cf3fc74 to your computer and use it in GitHub Desktop.
# Based on https://gamedev.stackexchange.com/questions/182534/comparing-analog-stick-movements-to-predefined-flick-shapes
extends Node2D
signal gesture_began(recognizer, gesture)
signal gesture_ended(recognizer, gesture)
signal gesture_recognized(recognizer, gesture)
export(bool) var draw_gizmos = true
export(int) var gizmo_line_resolution = 100
export(float) var gizmo_analog_diagram_line_width = 2.0
export(float) var gizmo_analog_trail_line_width = 3.0
export(int) var gizmo_analog_diagram_radius = 40
export(Color) var gizmo_analog_diagram_color = Color.black
export(Color) var gizmo_analog_trail_color = Color.orangered
export(Color) var gizmo_analog_knob_color = Color.orange
export(float) var gizmo_analog_knob_size = 7.0
export(float, 0.1, 0.9)var gesture_activation_radius: float = 0.7
export(int) var frame_count_before_gesture_reset = 2
export(int) var gesture_bucket_count: int = 8
var _input: Vector2
var _gesture_duration = 0.0
var _last_bucket: int
var _frames_in_deadzone: int
var _ready_to_begin_gesture: bool
var _current_gesture := ""
var _gesture_history = []
var _gesture_history_size = 32
var _registered_gestures = []
onready var _offset: float = gizmo_analog_diagram_radius - gizmo_analog_knob_size
func _ready():
_gesture_history.resize(_gesture_history_size)
for i in _gesture_history_size:
_gesture_history[i] = Vector2()
func register_gesture(gesture: String) -> void:
if not _registered_gestures.has(gesture):
_registered_gestures.append(gesture)
func input(analog_input: Vector2) -> void:
_input = analog_input
func _process(delta):
if not draw_gizmos:
return
_gesture_history[get_tree().get_frame() & (_gesture_history_size - 1)] = _input
var magnitude = _input.length()
if magnitude < (1.0 - gesture_activation_radius):
if _gesture_duration > 0 && _frames_in_deadzone > frame_count_before_gesture_reset:
_end_gesture()
_frames_in_deadzone += 1
_ready_to_begin_gesture = true
else:
_frames_in_deadzone = 0
if _ready_to_begin_gesture && magnitude > gesture_activation_radius:
if _gesture_duration == 0:
_begin_gesture()
_gesture_duration += delta
_process_gesture()
update()
func _begin_gesture():
_last_bucket = -1
_current_gesture = ""
func _process_gesture() -> void:
var bucket = _current_bucket()
if bucket != _last_bucket:
_current_gesture += str(bucket)
_recognize_gesture()
_last_bucket = bucket
func _end_gesture():
emit_signal("gesture_ended", self, _current_gesture)
_gesture_duration = 0.0
_ready_to_begin_gesture = false
func _recognize_gesture() -> void:
if _current_gesture.length() == 1:
emit_signal("gesture_began", self, _current_gesture)
if _registered_gestures.has(_current_gesture):
emit_signal("gesture_recognized", self, _current_gesture)
func _current_bucket() -> int:
var buckets_per_radian = gesture_bucket_count / (2 * PI)
var angle = atan2(-_input.x, -_input.y)
return int(round(angle * buckets_per_radian + gesture_bucket_count / 2.0)) % gesture_bucket_count
func _gesture_at_index(index: int = 1) -> Vector2:
return _gesture_history[(get_tree().get_frame() + index) & (_gesture_history_size - 1)]
func _draw():
if not draw_gizmos:
return
var buckets_per_radian = gesture_bucket_count / (2 * PI)
var radians_per_bucket = (2 * PI) / gesture_bucket_count
var activation_radius = gesture_activation_radius * gizmo_analog_diagram_radius
var angle_offset = 0.5 * radians_per_bucket
for i in gesture_bucket_count:
var start_angle = i * radians_per_bucket + angle_offset
var end_angle = (i + 1) * radians_per_bucket + angle_offset
# Outer arc
draw_arc(Vector2(),
gizmo_analog_diagram_radius,
start_angle,
end_angle,
gizmo_line_resolution,
gizmo_analog_diagram_color,
gizmo_analog_diagram_line_width)
# Inner arc
draw_arc(Vector2(),
activation_radius,
start_angle,
end_angle,
gizmo_line_resolution,
gizmo_analog_diagram_color,
gizmo_analog_diagram_line_width)
# Vertical line separating buckets
var bucket_separator = Vector2(sin(start_angle), cos(start_angle))
draw_line(bucket_separator * activation_radius,
bucket_separator * gizmo_analog_diagram_radius,
gizmo_analog_diagram_color,
gizmo_analog_diagram_line_width)
# Gesture trail
var last_gesture: Vector2 = _gesture_at_index()
for i in range(1, _gesture_history_size + 1):
var next_gesture = _gesture_at_index(i)
draw_line(last_gesture * _offset,
next_gesture * _offset,
gizmo_analog_trail_color,
gizmo_analog_trail_line_width)
last_gesture = next_gesture
# Gesture knob
draw_circle(last_gesture * _offset,
gizmo_analog_knob_size,
gizmo_analog_knob_color)
extends Node2D
# Add a Node2D as a child of this node and attach the AnalogGestureRecognizer.gd script to it. Then reference it in this node.
onready var gesture_recognizer = $AnalogGestureRecognizer
# Register a gesture for the recognizer to look for and connect to the signals.
func _ready():
gesture_recognizer.register_gesture('01234')
gesture_recognizer.connect("gesture_began", self, "gesture_began")
gesture_recognizer.connect("gesture_ended", self, "gesture_ended")
gesture_recognizer.connect("gesture_recognized", self, "gesture_recognized")
func _process(delta):
var right_stick = Vector2.ZERO
var rx = Input.get_joy_axis(0, JOY_AXIS_2)
var ry = Input.get_joy_axis(0 ,JOY_AXIS_3)
if abs(rx) > 0.1: # deadzone
right_stick.x = rx
if abs(ry) > 0.1: # deadzone
right_stick.y = ry
gesture_recognizer.input(right_stick)
func gesture_began(recognizer, gesture):
print(str('began ', gesture))
func gesture_ended(recognizer, gesture):
print(str('ended ', gesture))
func gesture_recognized(recognizer, gesture):
print(str('recognized ', gesture))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment