Steam Input es un servicio que permite a los usuarios de Steam jugar a cualquier juego compatible con mandos con el dispositivo de su elección. Steam Input traducirá las entradas del usuario a algo que el juego entienda a través de emulación de mando, emulación de ratón y teclado o la API de Steam Input. Aquí nos centraremos en cómo usar la emulación de mando de Steam Input para ampliar la compatibilidad con mandos existente de tu juego.
¿Qué es la emulación de mando?
- En Windows, la interfaz superpuesta de Steam interceptará la API de entrada tradicional de mando, como Xinput, DirectInput, RawInput y Windows.Gaming.Input e inyectará un dispositivo de mando de Xbox emulado. En macOS y Linux la entrada de mando emulada la proporciona un controlador.
- El mando aparecerá en el juego como un mando de Xbox, lo que significa que los mandos con entradas adicionales tendrán algunas de ellas duplicadas (p. ej., el clic del trackpad y el botón «OPTIONS» de PlayStation, ambos asignados al botón «START» de XInput).
- Además de la entrada de mando normal, es posible asignar las entradas de giroscopio de los mandos de Nintendo Switch, PlayStation y del Steam Controller a la emulación de ratón y proporcionar controles de movimiento. Esto funciona solamente en juegos para un solo jugador local debido a que solo hay una entrada de ratón y también depende de que el juego acepte simultáneamente entradas de ratón y mando. Si te interesan estas funciones sin estas limitaciones, considera añadir la API de Steam Input.
- Puedes consultar el tipo de mando desde Steam para mostrar glifos específicos del dispositivo, aunque esto te limitará a los tipos compatibles con tu versión actual del SDK de Steamworks. Si te interesa la compatibilidad con glifos que esté preparada para el futuro, integra la API de Steam Input/doclink].
- Puede que tengas usuarios jugando con Steam Input incluso con mandos que ya sean compatibles debido a que Steam Remote Play lo usa para enviar la entrada durante la transmisión y un número considerable de usuarios ha habilitado Steam Input para reconfigurar sus mandos en toda su biblioteca de Steam. En general, aproximadamente una cuarta parte de todas las sesiones en Steam en 2020 usaron Steam Input, incluyendo casi la mitad de todas las sesiones con mandos de PlayStation.
Más de 2000 juegos en Steam usan la emulación para al menos un tipo de mando, incluidos juegos notables como «Monster Hunter: World», «Ace Combat 7», «Dragon Quest XI», «Into the Breach» y «Middle Earth: Shadow of War», aunque no todos siguen la lista completa de prácticas recomendadas. Aquí destacamos «Into the Breach» porque implementa todas las prácticas recomendadas de forma ejemplar.
Mostrar material gráfico específico del dispositivo
La emulación de mando de Steam Input ofrece varias maneras de mostrar glifos específicos según el mando: una para juegos que pueden cargar imágenes en tiempo de ejecución que está preparada para el futuro (lo que significa que cuando Steam actualice Steam Input, funcionará sin necesidad de actualizar el juego) y dos para juegos que necesitan integrar las imágenes en sus recursos o usar el mismo arte estilizado de las versiones de consola de su juego. «Into the Breach» usa su propio material gráfico para el mando de Xbox y el Steam Controller:

PlayStation:

y los mandos de Nintedo Switch:

Nota: Incluso si utilizas tu propio material gráfico, te recomendamos recurrir al de Steam como respaldo cuando no se reconozca un mando, o llamar a nuestra función auxiliar para encontrar la opción más cercana disponible en el momento del lanzamiento de tu juego. Así, los dispositivos que se añadan en el futuro tendrán glifos razonables.
Mostrar los glifos preparados para el futuro de Steam
Tendrás que utilizar las siguientes funciones:
Código de ejemplo:
// Inicializar la interfaz antes de usar funciones individuales (solo necesita llamarse una vez)
SteamInput()->Init()
// ...
// SteamAPI_RunCallbacks() llamará a las funciones RunFrame de todas las interfaces que
// se hayan inicializado, y la mayoría de los juegos ya la estarán llamando periódicamente. Si no lo haces, tendrás que actualizar manualmente
// la interfaz de Steam Input
SteamInput()->RunFrame();
// ...
// Reemplazar con la posición de Xinput por la que consultas (de 0 a 3).
// Si está utilizando RawInput para la detección de dispositivos antes de decidir qué API usar,
// consulta la sección «Usar RawInput para la detección de dispositivos»
int nXinputSlot = 0;
// Reemplazar con el botón por el que consultas
EXboxOrigin eXboxButtonToGetGlyphFor = k_EXboxOrigin_A;
EInputActionOrigin buttonOrigin = k_EInputActionOrigin_XBoxOne_A;
// Si el mando está configurado a través de Steam Input, traducir el botón
InputHandle_t controller1Handle = SteamInput()->GetControllerForGamepadIndex( nXinputSlot );
if ( controller1Handle > 0 )
{
// Los handles válidos son mayores que 0; este es un mando configurado a través de Steam Input
// Nota: Los mandos que utilizan la API de Steam Input no devolverán un handle a través de GetControllerForGamePadIndex()
buttonOrigin = SteamInput()->GetActionOriginFromXboxOrigin( controller1Handle, k_EXboxOrigin_A );
}
else
{
// Los handles válidos son mayores que 0; este es un mando normal de Xbox
// Continuar usando el botón original
}
// Los valores de EInputActionOrigin seguirán aumentando a medida que Steam añada compatiblidad, pero no pasa nada porque
// en este ejemplo obtendremos las imágenes del dispositivo desde Steam, que también puede proporcionar una nueva imagen de glifo
// Obtener la imagen desde el cliente de Steam
const char *localGlyphPath = SteamInput()->GetGlyphForActionOrigin( buttonOrigin );
printf( "path = %s\n", localGlyphPath ); // "path = C:\Program Files (x86)\Steam\tenfoot\resource\images\library\controller\api\ps4_button_x.png"
// Reemplazar esto con una función del juego que convierte una ruta de archivo en una textura de juego utilizable
glyphTextureID = loadButtonGlyphTextureFromLocalPath( localGlyphPath );
Mostrar tu propio material gráfico: paleta de glifos
Si estás usando varias paletas de glifos y eliges cuál usar basándote en tu tipo de mando, puedes pedirle a Steam esta información usando la posición de Xinput:
Código de ejemplo:
// Inicializar la interfaz antes de usar funciones individuales (solo necesita llamarse una vez)
SteamInput()->Init()
// ...
// SteamAPI_RunCallbacks() llamará a las funciones RunFrame de todas las interfaces que
// se hayan inicializado, y la mayoría de los juegos ya la estarán llamando periódicamente. Si no lo haces, tendrás que actualizar manualmente
// la interfaz de Steam Input
SteamInput()->RunFrame();
// ...
// Reemplazar con la posición de Xinput por la que consultas (de 0 a 3).
// Si está utilizando RawInput para la detección de dispositivos antes de decidir qué API usar,
// consulta la sección «Usar RawInput para la detección de dispositivos»
int nXinputSlotIndex = 0;
InputHandle_t inputHandle = SteamInput()->GetControllerForGamepadIndex( nXinputSlotIndex );
if ( inputHandle == 0 )
{
// Los handles de entrada válidos son mayores que 0; este es un mando normal de Xbox
}
else
{
// Steam siempre devolverá un valor de enumeración válido para tu versión del SDK
ESteamInputType inputType = SteamInput()->GetInputTypeForHandle( inputHandle );
switch( inputType )
{
case k_ESteamInputType_Unknown:
printf( "¡Desconocido!\n" ); break;
case k_ESteamInputType_SteamController:
printf( "¡Steam Controller!\n" ); break;
case k_ESteamInputType_XBox360Controller:
printf( "¡Mando de Xbox 360!\n" ); break;
case k_ESteamInputType_XBoxOneController:
printf( "¡Mando de XBox One!\n" ); break;
case k_ESteamInputType_GenericXInput:
printf( "¡XInput genérico!\n" ); break;
case k_ESteamInputType_PS4Controller:
printf( "¡Mando de PS4!\n" ); break;
}
}
Mostrar tu propio material gráfico: arte por botón
Si estás usando una tabla de búsqueda sencilla indexada por orígenes de acción, puedes usar estas funciones para implementar glifos de dispositivos específicos y traducir cualquier mando no reconocido a sus equivalentes más cercanos.
Código de ejemplo:
// Inicializar la interfaz antes de usar funciones individuales (solo necesita llamarse una vez)
SteamInput()->Init()
// ...
// SteamAPI_RunCallbacks() llamará a las funciones RunFrame de todas las interfaces que
// se hayan inicializado, y la mayoría de los juegos ya la estarán llamando periódicamente. Si no lo haces, tendrás que actualizar manualmente
// la interfaz de Steam Input
SteamInput()->RunFrame();
// ...
// Reemplazar con la posición de Xinput por la que consultas (de 0 a 3).
// Si está utilizando RawInput para la detección de dispositivos antes de decidir qué API usar,
// consulta la sección «Usar RawInput para la detección de dispositivos»
int nXinputSlot = 0;
// Reemplazar con el botón por el que consultas
EXboxOrigin eXboxButtonToGetGlyphFor = k_EXboxOrigin_A;
EInputActionOrigin buttonOrigin = k_EInputActionOrigin_XBoxOne_A;
Si el mando está configurado a través de Steam Input, traducir el botón
InputHandle_t controller1Handle = SteamInput()->GetControllerForGamepadIndex( nXinputSlot );
if ( controller1Handle > 0 )
{
// Los handles válidos son mayores que 0; este es un mando configurado a través de Steam Input
// Nota: Los mandos que utilizan la API de Steam Input no devolverán un handle a través de GetControllerForGamePadIndex()
buttonOrigin = SteamInput()->GetActionOriginFromXboxOrigin( controller1Handle, k_EXboxOrigin_A ); // por ejemplo, k_EInputActionOrigin_PS4_X
}
else
{
// Los handles válidos son mayores que 0; este es un mando normal de Xbox
// Continuar usando el botón original
}
// Steam seguirá añadiendo orígenes de acciones y los controladores futuros superarán el rango actual
// Si quieres hacer una prueba, puedes fingir que los controladores de Switch/PS5 no existen y cambiarlo por:
// if ( buttonOrigin >= k_EInputActionOrigin_XBox360_Reserved10 )
if ( buttonOrigin >= k_EInputActionOrigin_Count )
{
// ¡No incluimos ningún material gráfico en nuestro juego para este origen! Supongo que Steam ha añadido compatibilidad para
// un nuevo mando. Vamos a obtener el valor más cercano que era compatible con el SDK con el que creamos
buttonOrigin = SteamInput()->TranslateActionOrigin( k_ESteamInputType_Unknown, buttonOrigin );
}
// Reemplazar esto por una función del juego que devuelva tu arte por un botón
int glyphTextureID = getHardCodedButtonGlyphTexture( buttonOrigin );
Usar RawInput para la detección de dispositivos
Algunos juegos utilizarán RawInput para admitir más de 4 mandos o para determinar si hay mandos de PlayStation conectados. Esta API también devolverá otros dispositivos que no son mandos y carecen de un índice fiable para asignar directamente desde las funciones de Steam Input. En su lugar, puedes obtener esta información consultando la cadena RIDI_DEVICENAME desde GetRawInputDeviceInfo(), que se codifica con el VID/PID USB del dispositivo real, el índice del mando y el handle de Steam Input con el siguiente formato:
\\.\pipe\HID#VID_045E&PID_028E&IG_00#{VID del dispositivo real}&{PID del dispositivo real}&{handle de la API de Steam Input}#{índice del mando de Steam Input}#{id. de proceso}
Por ejemplo, \\.\pipe\HID#VID_045E&PID_028E&IG_00#045E&0B00&00045EB00704DAF3#0#20160 para un mando de Xbox One con un VID/PID de 0x45e/0x0b00.
Código de ejemplo:
#define VALVE_DIRECTINPUT_GAMEPAD_VID 0x28DE
#define VALVE_DIRECTINPUT_GAMEPAD_PID 0x11FF
#define STEAM_INPUT_VID_INDEX 37
#define STEAM_INPUT_PID_INDEX 42
#define STEAM_INPUT_SIAPI_HANDLE_INDEX 47
#define STEAM_INPUT_GAMEPAD_INDEX 64
#define MAX_PATH 260
//...
void YourFunction()
{
PRAWINPUTDEVICELIST devices = NULL;
UINT i, j, device_count = 0;
if ((GetRawInputDeviceList(NULL, &device_count, sizeof(RAWINPUTDEVICELIST)) == -1) || (!device_count)) {
return; /* oh well. */
}
devices = (PRAWINPUTDEVICELIST)malloc(sizeof(RAWINPUTDEVICELIST) * device_count);
if (devices == NULL) {
return;
}
if (GetRawInputDeviceList(devices, &device_count, sizeof(RAWINPUTDEVICELIST)) == -1) {
free(devices);
return; /* oh well. */
}
for (i = 0; i < device_count; i++) {
RID_DEVICE_INFO rdi;
char devName[MAX_PATH];
UINT rdiSize = sizeof(rdi);
UINT nameSize = MAX_PATH;
rdi.cbSize = sizeof(rdi);
if ( devices[i].dwType == RIM_TYPEHID &&
GetRawInputDeviceInfoA( devices[i].hDevice, RIDI_DEVICEINFO, &rdi, &rdiSize ) != (UINT)-1 &&
GetRawInputDeviceInfoA( devices[i].hDevice, RIDI_DEVICENAME, devName, &nameSize ) != (UINT)-1 )
{
if ( rdi.hid.dwVendorId == VALVE_DIRECTINPUT_GAMEPAD_VID && rdi.hid.dwProductId == VALVE_DIRECTINPUT_GAMEPAD_PID )
{
uint32 ulVID = strtoul( &devName[STEAM_INPUT_VID_INDEX], NULL, 16 );
uint32 ulPID = strtoul( &devName[STEAM_INPUT_PID_INDEX], NULL, 16 );
uint64 ulDeviceHandle = strtoull( &devName[STEAM_INPUT_SIAPI_HANDLE_INDEX], NULL, 16 );
uint32 unGamepadIndex = strtoul( &devName[STEAM_INPUT_GAMEPAD_INDEX], NULL, 16 );
Log( "Raw input device: VID = 0x%x, PID = 0x%x, handle = 0x%llx, index = 0x%x, %s\n", ulVID, ulPID, ulDeviceHandle, unGamepadIndex, devName );
//Ahora puedes usar tanto el VID como el PID para identificar el dispositivo directamente o usar ulDeviceHandle con GetInputTypeForHandle
ESteamInputType inputType = SteamInput()->GetInputTypeForHandle( ulDeviceHandle );
switch( inputType )
{
case k_ESteamInputType_Unknown:
printf( "¡Desconocido!\n" ); break;
case k_ESteamInputType_SteamController:
printf( "¡Steam Controller!\n" ); break;
case k_ESteamInputType_XBox360Controller:
printf( "¡Mando de Xbox 360!\n" ); break;
case k_ESteamInputType_XBoxOneController:
printf( "¡Mando de XBox One!\n" ); break;
case k_ESteamInputType_GenericGamepad:
printf( "¡XInput genérico!\n" ); break;
case k_ESteamInputType_PS3Controller:
case k_ESteamInputType_PS4Controller:
case k_ESteamInputType_PS5Controller:
printf( "¡Mando de PlayStation!\n" ); break;
}
}
else
{
// El dispositivo no es un mando que funcione a través de Steam Input, utiliza tu lógica de id. de mando normal
continue;
}
}
}
free(devices);
}
Configurar los ajustes de Steamworks
El primer paso es establecer qué mandos utilizará Steam Input en la sección «Vincular mandos a Steam Input» en los ajustes de «Aplicación» > «Steam Input» en Steamworks. Para un juego estándar compatible con un mando de Xbox, te recomendamos marcar todas las casillas que no sean Xbox:

Si tu juego admite palancas de vuelo o volantes, querrás desmarcar la casilla de mandos genéricos/DirectInput, ya que Steam no admite la reasignación de esos dispositivos.
Seleccionar una configuración
Puedes optar por crear tu propia configuración o elegir una de nuestras plantillas prediseñadas. No es necesario crear tu propia configuración a menos que planees retocar las configuraciones fuera de nuestros valores predeterminados o etiquetar asignaciones individuales con lo que hacen en el juego. Las plantillas de Steam Input integradas están localizadas a todos los idiomas de Steam, así que asegúrate de localizar también tu configuración para evitar que se muestre el inglés a los usuarios de otros idiomas.
Elegir una plantilla
Los mandos de consola no diferencian entre las distintas variantes de plantilla de mando, pero estas configuraciones son importantes para el Steam Controller. Aquí tienes una explicación de las plantillas y de los tipos de juegos a los que están destinadas:
- Mando: emula un mando de Xbox con el trackpad derecho convertido en joystick emulado sin procesar. Perfecto para juegos de doble stick, juegos de plataformas o juegos deportivos.
- Mando con cámara o puntería de alta precisión: esta configuración es la ideal para un juego de disparos en primera persona o cualquier título que utilice controles de cámara. Antes de seleccionar esta opción, asegúrate de que tu juego es compatible con el uso simultáneo de mando y ratón, incluyendo las pantallas auxiliares, como las de inventario o mapa.
- Mando con controles de cámara: esta configuración toma una entrada estilo ratón del trackpad y la traduce en movimientos de un joystick emulado. Si tu juego puede configurarse para tener una sensibilidad de joystick muy alta, sin zona muerta y una curva de aceleración lineal, esta configuración puede funcionar bien. Si no, considera la posibilidad de utilizar una configuración de ratón/teclado en su lugar.

Puedes encontrar los pasos para publicar una plantilla para tu juego en Steamworks
aquí.
Crear una configuración personalizada
Las configuraciones se pueden convertir automáticamente entre la mayoría de los tipos de mandos, pero como mínimo deberías crear una configuración para el Steam Controller y el mando de Xbox.
Nota: Si piensas aprovechar características específicas del dispositivo, como los controles de movimiento, querrás tener también una para los mandos de PlayStation 4 o Nintendo Switch.
Entrada de texto
La entrada de texto en pantalla técnicamente no forma parte de
ISteamInput (Steam Input), sino que se encuentra en
ISteamUtils. Actualmente esto solo se implementa cuando el usuario inicia el juego a través del modo Big Picture.
Algunas referencias rápidas:
Localizar tu configuraciónCrear una configuración localizada es un poco más complicado cuando no se utiliza la API de Steam Input, pero aun así se puede conseguir.
- Para empezar, asegúrate de que no has modificado tu configuración desde la última vez que se exportó. Abre el configurador y haz un cambio (no pasa nada por hacerlo y luego revertirlo).
- Ve a tu registro Steam\Logs\controller_ui.txt y busca una cadena de texto como esta:
Steam Controller Saving Controller Configuration Autosave for [NÚMERO DE SERIE DEL MANDO]- AppID: [TU ID. DE APLICACIÓN].
Loaded Config for Local Selection Path for App ID [TU ID. DE APLICACIÓN], Controller 0: F:\ProgramFiles\Steam\client\userdata\[ID. DE STEAM]\config\controller_configs\apps\[TU ID. DE APLICACIÓN]\[NÚMERO DE SERIE DEL MANDO]\guest\controller_configuration.vdf
- Inserta los tokens de localización para las asignaciones que desees localizar.
- Título y descripción:
"controller_mappings"
{
"version" "3"
"revision" "5"
"title" "#título"
"description" "#descripción"
...
- Asignaciones de botones:
"button_a"
{
"activators"
{
"Full_Press"
{
"bindings"
{
"binding" "xinput_button A, #botón_a"
}
}
}
}
...
- Y luego crea los valores correspondientes para cada idioma en el bloque de localización:
"localization"
{
"spanish"
{
"title" "Este es un título localizado"
"description" "Esta es una descripción localizada. La asignación de la botonera también tiene nombres localizables."
"abutton" "Tu nombre aquí 1"
"bbutton" "Tu nombre aquí 2"
"xbutton" "Tu nombre aquí 3"
"ybutton" "Tu nombre aquí 4"
}
...
- Ahora puedes exportar la configuración y seguir estas instrucciones para añadirla al juego en el sitio web de asociados. Convendría que marcases la casilla «Usar bloque de acciones» para una de tus configuraciones.
La configuración de ejemplo se puede ver en el cliente de Steam introduciendo este URL en tu navegador: steam://controllerconfig/681280/1744110334. Si prefieres, el archivo VDF está disponible
aquí.