Wired
ss-gameforge-wired es un sistema completo de gestión de input sobre el manejo de input nativo de Godot.
Añade aislamiento de input por jugador, rebinding en tiempo de ejecución con persistencia, detección automática de dispositivos y soporte “press to join” para co-op local.
Instalación
Sección titulada «Instalación»- Descarga
ss-gameforge-wireddesde la página de releases. - Copia
addons/ss-gameforge-wired/dentro deres://addons/. - Activa ss-gameforge-singleton y ss-gameforge-wired en Project Settings → Plugins.
Panel de editor
Sección titulada «Panel de editor»Al activar el plugin aparece una pestaña GodotWired en la pantalla principal del editor de Godot (junto a 2D/3D/Script).
Desde ahí puedes:
- Configurar el modo de jugador (
SINGLE,LOCAL_COOP,ONLINE) y el máximo de jugadores locales. - Añadir, renombrar y eliminar acciones.
- Asignar a cada acción su nombre de display, descripción, categoría, tipo (Button o Axis) y si permite rebinding.
- Guardar la configuración como un recurso
GWConfig(ruta por defecto:res://gw_config.tres).
Los bindings por defecto se añaden desde código con define_action(), o directamente sobre el recurso GWConfig en el inspector.
Clases principales
Sección titulada «Clases principales»| Clase | Descripción |
|---|---|
GWInputManager | Coordinador central — punto de entrada singleton para todas las operaciones de input. |
GWConfig | Recurso de configuración del proyecto (modo de jugador, máx. jugadores, lista de acciones). |
GWActionConfig | Plantilla para definir una acción (nombre, bindings, categoría, tipo). |
GWPlayer | Jugador lógico con sus propios dispositivos y copias independientes de todas las acciones. |
GWAction | Instancia de acción en tiempo de ejecución por jugador (rebindeable independientemente). |
GWBinding | Un mapeo de input físico individual (tecla, botón, eje, botón de ratón). |
GWDevice | Abstracción sobre un dispositivo físico (teclado/ratón o gamepad). |
GWProfileManager | Utilidad estática para guardar y cargar perfiles de binding con nombre. |
GWConfig
Sección titulada «GWConfig»Crea via el panel de editor, o con New Resource → GWConfig en el inspector.
enum PlayerMode { SINGLE, # Un jugador — teclado + cualquier gamepad LOCAL_COOP, # N jugadores — cada uno recibe un dispositivo exclusivo ONLINE, # Un jugador local, el resto se gestiona por la capa de red}
@export var player_mode: PlayerMode = SINGLE@export_range(2, 8) var max_local_players: int = 2@export var actions: Array[GWActionConfig] = []Carga la configuración al iniciar:
var config := preload("res://gw_config.tres") as GWConfigGWInputManager.i.load_config(config)GWActionConfig — campos de acción
Sección titulada «GWActionConfig — campos de acción»| Campo | Tipo | Descripción |
|---|---|---|
action_name | String | Identificador interno usado en el código de gameplay (ej. "saltar"). |
display_name | String | Etiqueta mostrada en la UI de rebinding (ej. "Saltar"). |
description | String | Tooltip opcional en la pantalla de rebinding. |
category | String | Agrupa acciones en la UI (ej. "Movimiento", "Combate"). |
max_bindings | int | Máximo de slots de binding por jugador. 0 = ilimitado. |
action_type | ActionType | BUTTON o AXIS. |
positive_name | String | Solo Axis — etiqueta para la dirección +1.0 (ej. "derecha"). |
negative_name | String | Solo Axis — etiqueta para la dirección -1.0 (ej. "izquierda"). |
allow_rebind | bool | Si es false, la acción no aparece como editable en la UI de rebinding. |
default_bindings | Array[GWBinding] | Bindings clonados en cada jugador al llamar a load_config(). |
GWBinding — mapeo de input
Sección titulada «GWBinding — mapeo de input»Usa los métodos factory estáticos para crear bindings:
GWBinding.from_key(KEY_SPACE)GWBinding.from_key(KEY_W)GWBinding.from_joy_button(JOY_BUTTON_A)GWBinding.from_joy_axis(JOY_AXIS_LEFT_Y, -1.0) # Analógico arribaGWBinding.from_mouse_button(MOUSE_BUTTON_LEFT)Usa get_display_name() para obtener una etiqueta legible de cualquier binding:
var b: GWBinding = action.bindings[0]label.text = b.get_display_name() # "Space", "A", "LX +", "Mouse L", etc.API de GWInputManager
Sección titulada «API de GWInputManager»Inicializar
Sección titulada «Inicializar»func _enter_tree() -> void: GWInputManager.ensure(get_tree().root)Jugadores
Sección titulada «Jugadores»var p1 := GWInputManager.i.create_player(0, "Jugador 1")var p2 := GWInputManager.i.create_player(1, "Jugador 2")
GWInputManager.i.get_player(player_id) -> GWPlayerGWInputManager.i.get_default_player() -> GWPlayer # atajo de get_player(0)GWInputManager.i.remove_player(player_id)Consultar input por polling
Sección titulada «Consultar input por polling»Seguro llamarlo desde _process() o _physics_process() — no requiere InputEvent:
GWInputManager.i.is_action_pressed("saltar", player_id)GWInputManager.i.is_action_just_pressed("saltar", null, player_id) # por frameGWInputManager.i.is_action_just_released("saltar", null, player_id) # por frameGWInputManager.i.get_action_strength("mover_derecha", player_id)GWInputManager.i.get_action_axis("mover_horizontal", player_id)GWInputManager.i.get_vector("mover_izq", "mover_der", "mover_arr", "mover_abj", player_id)Para consultas basadas en evento (desde _input() o _unhandled_input()), pasa el evento en lugar de null:
func _input(event: InputEvent) -> void: if GWInputManager.i.is_action_just_pressed("saltar", event): velocity.y = -400.0Definir acciones desde código
Sección titulada «Definir acciones desde código»GWInputManager.i.define_action( "saltar", # action_name "Saltar", # display_name [GWBinding.from_key(KEY_SPACE), GWBinding.from_joy_button(JOY_BUTTON_A)], "Movimiento", # category 2 # max_bindings)Rebinding en tiempo de ejecución
Sección titulada «Rebinding en tiempo de ejecución»# Inicia escucha del siguiente input del jugadorGWInputManager.i.start_rebind_listen(player_id, "saltar", binding_index)
# Cancela una escucha en cursoGWInputManager.i.cancel_rebind_listen()
# Verifica si está escuchandoGWInputManager.i.is_listening_for_rebind() -> boolPersistencia
Sección titulada «Persistencia»GWInputManager.i.save_all_profiles("user://input_profiles.json")GWInputManager.i.load_all_profiles("user://input_profiles.json")GWInputManager.i.reset_all_to_defaults()Dispositivos
Sección titulada «Dispositivos»GWInputManager.i.get_connected_devices() -> Array # todos los dispositivos incluyendo teclado/ratónGWInputManager.i.get_unassigned_gamepads() -> Array # gamepads sin jugador asignadoSeñales
Sección titulada «Señales»signal player_created(player_id: int)signal player_removed(player_id: int)signal device_connected(device_id: int, device_name: String)signal device_disconnected(device_id: int)signal binding_remapped(player_id: int, action_name: String)signal rebind_listening(player_id: int, action_name: String, binding_index: int)signal rebind_cancelled(player_id: int)signal unassigned_device_pressed(device_id: int) # Soporte "press to join"API de GWPlayer
Sección titulada «API de GWPlayer»# Asignación de dispositivosp1.assign_gamepad(device_id)p1.unassign_gamepad(device_id)p1.unassign_all_gamepads()p1.accepts_keyboard = true # permitir teclado/ratón para este jugadorp1.has_any_device() -> bool
# Consultas de input por polling (sin InputEvent)p1.is_action_pressed("saltar")p1.get_action_strength("mover_derecha")p1.get_action_axis("mover_horizontal") # devuelve -1.0 a 1.0p1.get_vector("mover_izq", "mover_der", "mover_arr", "mover_abj")
# Consultas basadas en evento (desde _input / _unhandled_input)p1.is_action_just_pressed("saltar", event)p1.is_action_just_released("saltar", event)
# Rebindingp1.set_binding("saltar", index, binding)p1.clear_action_bindings("saltar")p1.find_conflicts(event, exclude_action) # devuelve Array[GWAction]Señales en GWPlayer
Sección titulada «Señales en GWPlayer»signal device_assigned(device_id: int)signal device_unassigned(device_id: int)signal action_rebound(action_name: String)GWProfileManager — perfiles con nombre
Sección titulada «GWProfileManager — perfiles con nombre»Utilidad estática para guardar y cargar perfiles de binding con nombre.
Cada perfil se guarda como un archivo separado: gw_profile_{nombre}.json.
GWProfileManager.save(GWInputManager.i, "perfil_jugador1")GWProfileManager.load_profile(GWInputManager.i, "perfil_jugador1")GWProfileManager.profile_exists("perfil_jugador1") -> boolGWProfileManager.list_profiles() -> Array[String]GWProfileManager.delete_profile("perfil_jugador1")Inicio rápido — un jugador
Sección titulada «Inicio rápido — un jugador»extends Node
func _enter_tree() -> void: GWInputManager.ensure(get_tree().root)
func _ready() -> void: GWInputManager.i.define_action( "saltar", "Saltar", [GWBinding.from_key(KEY_SPACE), GWBinding.from_joy_button(JOY_BUTTON_A)], "Movimiento", 2 ) GWInputManager.i.define_action( "mover_derecha", "Mover Derecha", [GWBinding.from_key(KEY_D), GWBinding.from_joy_axis(JOY_AXIS_LEFT_X, 1.0)], "Movimiento", 2 ) GWInputManager.i.define_action( "mover_izquierda", "Mover Izquierda", [GWBinding.from_key(KEY_A), GWBinding.from_joy_axis(JOY_AXIS_LEFT_X, -1.0)], "Movimiento", 2 )
var p1 := GWInputManager.i.create_player(0) p1.accepts_keyboard = true
func _physics_process(_delta: float) -> void: if GWInputManager.i.is_action_just_pressed("saltar"): velocity.y = -400.0 velocity.x = GWInputManager.i.get_action_axis("mover_horizontal") * 200.0Inicio rápido — co-op local
Sección titulada «Inicio rápido — co-op local»func _ready() -> void: var config := preload("res://gw_config.tres") as GWConfig GWInputManager.i.load_config(config) # player_mode = LOCAL_COOP, max = 4
GWInputManager.i.unassigned_device_pressed.connect(_on_press_to_join)
var _player_count := 0
func _on_press_to_join(device_id: int) -> void: var p := GWInputManager.i.create_player(_player_count) p.assign_gamepad(device_id) _player_count += 1 print("Jugador %d se unió con dispositivo %d" % [_player_count, device_id])UI de rebinding en tiempo de ejecución
Sección titulada «UI de rebinding en tiempo de ejecución»Usa las señales de GWInputManager para construir tu propia pantalla de rebinding:
# Mostrar la etiqueta del binding actualfunc refresh_binding_label(player_id: int, action_name: String) -> void: var p := GWInputManager.i.get_player(player_id) var action: GWAction = p.actions[action_name] if action.bindings.is_empty(): label.text = "Sin asignar" else: label.text = action.bindings[0].get_display_name()
# Iniciar escuchafunc _on_rebind_button_pressed() -> void: GWInputManager.i.start_rebind_listen(0, "saltar", 0)
# Reaccionar al completarsefunc _ready() -> void: GWInputManager.i.binding_remapped.connect( func(pid, action): refresh_binding_label(pid, action) )