手柄查看器
摇杆、扳机和按键直接从 Input 读取并实时绘制。没插手柄时会提示,方向键依然能驱动 ui 向量。
上方的编辑器正在运行这个项目。改一行代码,它就会重新加载。
用到的 Godot 类
InputProgressBarPanelStyleBoxFlat
代码
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