Visor de gamepad

Sticks, gatillos y botones leídos directo de Input y dibujados según se mueven. Sin nada conectado lo avisa, y las flechas siguen moviendo el vector de ui.

El editor de arriba está ejecutando este proyecto. Cambia una línea y se recarga.

Clases de Godot usadas

InputProgressBarPanelStyleBoxFlat

El código

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

Más ejemplos de Entrada