Visualiseur de manette
Sticks, gâchettes et boutons lus directement depuis Input et dessinés au fil de leurs mouvements. Sans manette branchée il le dit, et les flèches pilotent toujours le vecteur ui.
L'éditeur ci-dessus fait tourner ce projet. Changez une ligne et il se recharge.
Classes Godot utilisées
InputProgressBarPanelStyleBoxFlat
Le code
scripts/main.gd
extends Node
const STICK_RADIUS: float = 76.0
const KNOB_RADIUS: float = 20.0
const DEAD_ZONE: float = 0.18
const BUTTONS: Array[Dictionary] = [
{name = 'A', id = JOY_BUTTON_A},
{name = 'B', id = JOY_BUTTON_B},
{name = 'X', id = JOY_BUTTON_X},
{name = 'Y', id = JOY_BUTTON_Y},
{name = 'LB', id = JOY_BUTTON_LEFT_SHOULDER},
{name = 'RB', id = JOY_BUTTON_RIGHT_SHOULDER},
{name = 'L3', id = JOY_BUTTON_LEFT_STICK},
{name = 'R3', id = JOY_BUTTON_RIGHT_STICK},
{name = 'Back', id = JOY_BUTTON_BACK},
{name = 'Start', id = JOY_BUTTON_START},
{name = 'Up', id = JOY_BUTTON_DPAD_UP},
{name = 'Down', id = JOY_BUTTON_DPAD_DOWN},
{name = 'Left', id = JOY_BUTTON_DPAD_LEFT},
{name = 'Right', id = JOY_BUTTON_DPAD_RIGHT},
]
var device: int = -1
var status: Label
var readout: Label
var knobs: Array[Panel] = []
var pills: Array[StyleBoxFlat] = []
var triggers: Array[ProgressBar] = []
func _ready() -> void:
var column: VBoxContainer = _root()
var title: Label = Label.new()
title.text = 'Gamepad viewer'
title.add_theme_font_size_override('font_size', 28)
column.add_child(title)
status = Label.new()
column.add_child(status)
var decks: HBoxContainer = HBoxContainer.new()
decks.add_theme_constant_override('separation', 40)
decks.alignment = BoxContainer.ALIGNMENT_CENTER
column.add_child(decks)
decks.add_child(_stick('left stick'))
decks.add_child(_trigger_deck())
decks.add_child(_stick('right stick'))
var grid: GridContainer = GridContainer.new()
grid.columns = 7
grid.add_theme_constant_override('h_separation', 10)
grid.add_theme_constant_override('v_separation', 10)
column.add_child(grid)
for entry: Dictionary in BUTTONS:
grid.add_child(_pill(entry.name))
readout = Label.new()
readout.add_theme_color_override('font_color', Color('#8da5f3'))
column.add_child(readout)
Input.joy_connection_changed.connect(_on_connection_changed)
_refresh_device()
print('move the sticks and press the buttons, the arrow keys also drive the ui vector')
func _process(_delta: float) -> void:
var left: Vector2 = _stick_value(JOY_AXIS_LEFT_X, JOY_AXIS_LEFT_Y)
var right: Vector2 = _stick_value(JOY_AXIS_RIGHT_X, JOY_AXIS_RIGHT_Y)
_move_knob(knobs[0], left)
_move_knob(knobs[1], right)
triggers[0].value = _axis(JOY_AXIS_TRIGGER_LEFT)
triggers[1].value = _axis(JOY_AXIS_TRIGGER_RIGHT)
for i in BUTTONS.size():
var down: bool = device >= 0 and Input.is_joy_button_pressed(device, BUTTONS[i].id)
pills[i].bg_color = Color('#8eef97') if down else Color('#2a2f3d')
var digital: Vector2 = Input.get_vector(&'ui_left', &'ui_right', &'ui_up', &'ui_down')
readout.text = 'left %.2f, %.2f right %.2f, %.2f ui_* vector %.2f, %.2f' % [left.x, left.y, right.x, right.y, digital.x, digital.y]
func _on_connection_changed(_which: int, _connected: bool) -> void:
_refresh_device()
func _refresh_device() -> void:
var pads: Array[int] = Input.get_connected_joypads()
device = pads[0] if pads.size() > 0 else -1
if device < 0:
status.text = 'no gamepad connected, plug one in and press a button'
status.add_theme_color_override('font_color', Color('#fc7f7f'))
return
status.text = 'device %d: %s' % [device, Input.get_joy_name(device)]
status.add_theme_color_override('font_color', Color('#8eef97'))
func _axis(axis: JoyAxis) -> float:
return Input.get_joy_axis(device, axis) if device >= 0 else 0.0
func _stick_value(x_axis: JoyAxis, y_axis: JoyAxis) -> Vector2:
var raw: Vector2 = Vector2(_axis(x_axis), _axis(y_axis))
return Vector2.ZERO if raw.length() < DEAD_ZONE else raw
func _move_knob(knob: Panel, value: Vector2) -> void:
knob.position = Vector2(STICK_RADIUS, STICK_RADIUS) + value * (STICK_RADIUS - KNOB_RADIUS) - Vector2(KNOB_RADIUS, KNOB_RADIUS)
func _stick(caption: String) -> VBoxContainer:
var column: VBoxContainer = VBoxContainer.new()
column.add_theme_constant_override('separation', 10)
var disc: Panel = _disc(STICK_RADIUS, Color('#1a1e28'), Color('#2a2f3d'))
disc.size_flags_horizontal = Control.SIZE_SHRINK_CENTER
column.add_child(disc)
var knob: Panel = _disc(KNOB_RADIUS, Color('#8da5f3'), Color('#8da5f3'))
disc.add_child(knob)
knobs.append(knob)
_move_knob(knob, Vector2.ZERO)
var label: Label = Label.new()
label.text = caption
label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
label.add_theme_color_override('font_color', Color('#9aa3b5'))
column.add_child(label)
return column
func _trigger_deck() -> VBoxContainer:
var column: VBoxContainer = VBoxContainer.new()
column.add_theme_constant_override('separation', 10)
column.alignment = BoxContainer.ALIGNMENT_CENTER
for caption in ['LT', 'RT']:
var label: Label = Label.new()
label.text = caption
label.add_theme_color_override('font_color', Color('#9aa3b5'))
column.add_child(label)
var bar: ProgressBar = ProgressBar.new()
bar.custom_minimum_size = Vector2(170, 20)
bar.max_value = 1.0
bar.show_percentage = false
bar.add_theme_stylebox_override('background', _bar_style(Color('#1a1e28')))
bar.add_theme_stylebox_override('fill', _bar_style(Color('#f3d48d')))
column.add_child(bar)
triggers.append(bar)
return column
func _pill(caption: String) -> PanelContainer:
var style: StyleBoxFlat = StyleBoxFlat.new()
style.bg_color = Color('#2a2f3d')
style.set_corner_radius_all(8)
style.set_content_margin_all(10)
var box: PanelContainer = PanelContainer.new()
box.custom_minimum_size = Vector2(74, 0)
box.add_theme_stylebox_override('panel', style)
pills.append(style)
var label: Label = Label.new()
label.text = caption
label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
box.add_child(label)
return box
func _disc(radius: float, fill: Color, border: Color) -> Panel:
var style: StyleBoxFlat = StyleBoxFlat.new()
style.bg_color = fill
style.border_color = border
style.set_border_width_all(2)
style.set_corner_radius_all(int(radius))
var disc: Panel = Panel.new()
disc.custom_minimum_size = Vector2(radius * 2, radius * 2)
disc.size = Vector2(radius * 2, radius * 2)
disc.add_theme_stylebox_override('panel', style)
return disc
func _bar_style(color: Color) -> StyleBoxFlat:
var style: StyleBoxFlat = StyleBoxFlat.new()
style.bg_color = color
style.set_corner_radius_all(6)
return style
func _root() -> VBoxContainer:
var background: ColorRect = ColorRect.new()
background.color = Color('#12141a')
add_child(background)
background.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
var center: CenterContainer = CenterContainer.new()
add_child(center)
center.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
var column: VBoxContainer = VBoxContainer.new()
column.add_theme_constant_override('separation', 20)
center.add_child(column)
return column