Documentazione di Steamworks
Emulazione del gamepad di Steam Input: pratiche consigliate
Steam Input è un servizio di Steam che consente agli utenti di giocare a qualsiasi gioco compatibile con i controller con il proprio dispositivo preferito. Steam Input tradurrà l'input dell'utente in qualcosa che il gioco può comprendere utilizzando l'emulazione del gamepad, l'emulazione di mouse e tastiera o l'API di Steam Input. In questo articolo ci concentreremo su come utilizzare al meglio l'emulazione del gamepad di Steam Input per estendere il supporto ai controller esistente per il tuo gioco.

Che cos'è l'emulazione del gamepad?
  • Su Windows, l'Overlay di Steam si aggancerà alle API di input dei gamepad tradizionali come XInput, DirectInput, RawInput e Windows.Gaming.Input e inserirà un controller Xbox emulato. Su MacOS e Linux, l'input del controller emulato viene fornito da un driver.
  • Il controller verrà visualizzato nel tuo gioco come un controller Xbox, il che significa che i controller con input extra ne avranno alcuni duplicati, ad esempio: il clic del trackpad della PlayStation e il pulsante delle opzioni mappano entrambi al pulsante Start di XInput.
  • Oltre al normale input del gamepad è possibile assegnare gli input del giroscopio di Switch, PlayStation e Steam Controller all'emulazione del mouse e fornire il controllo del movimento. Questo funziona solo per i giochi con un unico giocatore locale in quanto è presente solo un input del mouse e dipende anche dal fatto che il gioco accetti contemporaneamente l'input del mouse e del controller. Se sei interessato a queste funzionalità senza questi problemi, puoi prendere in considerazione l'aggiunta dell'API di Steam Input
  • Puoi interrogare il tipo di controller attuale da Steam per mostrare i glifi specifici del dispositivo, ma sei limitato ai tipi supportati dal tuo SDK di Steamworks corrente. Se sei interessato a un supporto dei glifi a prova di compatibilità futura, integra l'API di Steam Input
  • Ci possono essere momenti in cui gli utenti giocano con Steam Input anche su controller già supportati, perché Steam Remote Play lo utilizza per fornire l'input durante lo streaming e una grande percentuale di utenti ha abilitato Steam Input per la riconfigurazione dei propri controller nell'intera Libreria di Steam. Nel complesso, circa un quarto di tutte le sessioni con controller su Steam nel 2020 ha utilizzato Steam Input, tra cui quasi la metà di tutte le sessioni di gioco con un controller PlayStation.

Oltre 2000 giochi su Steam usano l'emulazione del gamepad per almeno un tipo di controller, tra cui titoli celebri come Monster Hunter: World, Ace Combat 7, Dragon Quest XI, Into the Breach e Middle Earth: Shadow of War. Non tutti questi giochi, tuttavia, seguono le pratiche consigliate. Qui prenderemo in considerazione Into the Breach poiché gli sviluppatori si sono impegnati per seguire tutte le pratiche consigliate.

Mostrare illustrazioni specifiche per un dispositivo

Sono supportati due modi di ottenere icone specifiche per un dispositivo attraverso l'emulazione del Gamepad di Steam Input: uno è per giochi in grado di caricare immagini in un tempo di esecuzione indipendente dal futuro (ovvero, le immagini funzioneranno quando Steam aggiorna Steam Input, senza che il gioco stesso debba essere aggiornato), l'altro è per giochi che devono integrare le immagini nei loro asset o che utilizzano le stesse illustrazioni stilizzate delle versioni console. Into the Breach utilizza illustrazioni originali per i controller Xbox/Steam:

intothebreach_xbox.PNG

PlayStation:

intothebreach_ps4.PNG

e controller Nintendo Switch:

intothebreach_switchpro.PNG
Nota: anche quando utilizzi le tue stesse illustrazioni, suggeriamo di tenere quelle di Steam come riserva quando non riconosci un controller o chiami la nostra funzione d'aiuto per trovare l'opzione più simile disponibile al momento del lancio del tuo gioco. In questo modo, quando in futuro saranno aggiunti altri dispositivi, avranno icone adatte.

Mostrare icone di Steam indipendenti dal futuro

Dovranno essere utilizzate le seguenti funzioni:

Esempio di codice:

// Avvia l'interfaccia prima di usare funzioni individuali. Questa chiamata va effettuata solo una volta! SteamInput()->Init() // ... // SteamAPI_RunCallbacks() chiamerà le funzioni RunFrame per tutte le interfacce avviate e la maggior parte // dei giochi starà già effettuando periodicamente questa chiamata. Se non lo stai facendo, dovrai aggiornare manualmente // l'interfaccia di Steam Input SteamInput()->RunFrame(); // ... // Sostituisci con lo slot XInput per cui stai eseguendo la query. Questo numero è compreso tra 0 e 3 // Se stai utilizzando RawInput per il rilevamento del dispositivo prima di decidere quale API utilizzare, per favore // consulta la sezione "Utilizzare RawInput per il rilevamento del dispositivo". int nXinputSlot = 0; // Sostituisci con il pulsante per il quale stai eseguendo la query EXboxOrigin eXboxButtonToGetGlyphFor = k_EXboxOrigin_A; EInputActionOrigin buttonOrigin = k_EInputActionOrigin_XBoxOne_A; // Se il controller è configurato tramite Steam Input, traduci il pulsante InputHandle_t controller1Handle = SteamInput()->GetControllerForGamepadIndex( nXinputSlot ); if ( controller1Handle > 0 ) { // Gli handle validi sono diversi da zero, questo è un controller configurato tramite Steam Input // Nota: i controller che utilizzano l'API di Steam Input non restituiranno un handle tramite GetControllerForGamepadIndex() buttonOrigin = SteamInput()->GetActionOriginFromXboxOrigin( controller1Handle, k_EXboxOrigin_A ); } else { // Gli handle validi sono diversi da zero, questo è un normale controller Xbox // Continua usando il pulsante originale } // I valori EInputActionOrigin continueranno ad aumentare man mano che Steam aggiunge supporto, ma questo non è un problema perché // in questo esempio otterremo le immagini del dispositivo da Steam, che può anche fornire una nuova immagine per il glifo // Ottieni l'immagine dal client di Steam const char *localGlyphPath = SteamInput()->GetGlyphForActionOrigin( buttonOrigin ); printf( "path = %s\n", localGlyphPath ); // "path = C:\Programmi (x86)\Steam\tenfoot\resource\images\library\controller\api\ps4_button_x.png" // Sostituisci con una funzione del gioco che trasforma un percorso di file in una texture di gioco utilizzabile glyphTextureID = loadButtonGlyphTextureFromLocalPath( localGlyphPath );

Mostrare le tue illustrazioni - Tavolozza glifi

Se stai usando più tavolozze glifi e scegli quella da utilizzare in base al tipo di controller, puoi chiedere questa informazione a Steam tramite lo slot XInput usando:

Esempio di codice:
// Avvia l'interfaccia prima di usare funzioni individuali. Questa chiamata va effettuata solo una volta! SteamInput()->Init() // ... // SteamAPI_RunCallbacks() chiamerà le funzioni RunFrame per tutte le interfacce avviate e la maggior parte // dei giochi starà già effettuando periodicamente questa chiamata. Se non lo stai facendo, dovrai aggiornare manualmente // l'interfaccia di Steam Input SteamInput()->RunFrame(); // ... // Sostituisci con lo slot XInput per cui stai eseguendo la query. Questo numero è compreso tra 0 e 3 // Se stai utilizzando RawInput per il rilevamento del dispositivo prima di decidere quale API utilizzare, per favore // consulta la sezione "Utilizzare RawInput per il rilevamento del dispositivo". int nXinputSlotIndex = 0; InputHandle_t inputHandle = SteamInput()->GetControllerForGamepadIndex( nXinputSlotIndex ); if ( inputHandle == 0 ) { // Gli handle validi sono diversi da zero, questo è un normale controller Xbox. } oppure { // Steam restituirà sempre un valore di enumerazione che era valido per la tua versione dell'SDK. ESteamInputType inputType = SteamInput()->GetInputTypeForHandle( inputHandle ); switch( inputType ) { case k_ESteamInputType_Unknown: printf( "unknown\n!" ); break; case k_ESteamInputType_SteamController: printf( "Steam controller\n!" ); break; case k_ESteamInputType_XBox360Controller: printf( "XBox 360 controller\n!" ); break; case k_ESteamInputType_XBoxOneController: printf( "XBox One controller\n!" ); break; case k_ESteamInputType_GenericXInput: printf( "Generic XInput\n!" ); break; case k_ESteamInputType_PS4Controller: printf( "PS4 controller\n!" ); break; } }

Mostrare le tue illustrazioni - Immagine per ciascun pulsante

Se stai usando una singola tabella di consultazione indicizzata per azioni di origine, puoi usare queste funzioni per implementare glifi specifici per i vari dispositivi e tradurre qualunque controller non identificato nel suo equivalente più vicino.

Esempio di codice:
// Avvia l'interfaccia prima di usare funzioni individuali. Questa chiamata va effettuata solo una volta! SteamInput()->Init() // ... // SteamAPI_RunCallbacks() chiamerà le funzioni RunFrame per tutte le interfacce avviate e la maggior parte // dei giochi starà già effettuando periodicamente questa chiamata. Se non lo stai facendo, dovrai aggiornare manualmente // l'interfaccia di Steam Input SteamInput()->RunFrame(); // ... // Sostituisci con lo slot XInput per cui stai eseguendo la query. Questo numero è compreso tra 0 e 3 // Se stai utilizzando RawInput per il rilevamento del dispositivo prima di decidere quale API utilizzare, per favore // consulta la sezione "Utilizzare RawInput per il rilevamento del dispositivo". int nXinputSlot = 0; // Sostituisci con il pulsante per il quale stai effettuando la query EXboxOrigin eXboxButtonToGetGlyphFor = k_EXboxOrigin_A; EInputActionOrigin buttonOrigin = k_EInputActionOrigin_XBoxOne_A; // Se il controller è configurato tramite Steam Input, traduci il pulsante InputHandle_t controller1Handle = SteamInput()->GetControllerForGamepadIndex( nXinputSlot ); if ( controller1Handle > 0 ) { // Gli handle validi sono diversi da zero, questo è un controller configurato tramite Steam Input // Nota: i controller che stanno utilizzando l'API di Steam Input non restituiranno un handle tramite GetControllerForGamepadIndex() buttonOrigin = SteamInput()->GetActionOriginFromXboxOrigin( controller1Handle, k_EXboxOrigin_A ); //i.e, k_EInputActionOrigin_PS4_X } else { // Gli handle validi sono diversi da zero, questo è un normale controller Xbox // Continua usando il pulsante originale } // Steam continuerà ad aggiungere le origini dell'azione e i controller futuri supereranno l'intervallo corrente // Se vuoi eseguire un test, puoi fingere che i controller Switch/PS5 non esistano e cambiare in: // if ( buttonOrigin >= k_EInputActionOrigin_XBox360_Reserved10 ) if ( buttonOrigin >= k_EInputActionOrigin_Count ) { // Non abbiamo distribuito immagini nel nostro gioco per questa origine! Sembra che Steam ora supporti // un nuovo controller. Otteniamo il valore più vicino supportato dall'SDK utilizzato buttonOrigin = SteamInput()->TranslateActionOrigin( k_ESteamInputType_Unknown, buttonOrigin ); } // Sostituiscilo con una funzione del gioco che restituisce un'immagine per un pulsante int glyphTextureID = getHardCodedButtonGlyphTexture( buttonOrigin );

Utilizzare RawInput per il rilevamento del dispositivo

Alcuni giochi fanno uso di RawInput per supportare più di 4 controller o per determinare se ci sono controller della Playstation connessi. Questa API restituirà anche gli altri dispositivi diversi dai gamepad e non dispone di un indice affidabile per mappare direttamente dalle funzioni di Steam Input. Questa informazione può invece essere ottenuta richiedendo la stringa RIDI_DEVICENAME da GetRawInputDeviceInfo(), codificata con il VIP/PID USB del dispositivo reale, l'indice del gamepad e la handle di Steam Input nel seguente formato:
\\.\pipe\HID#VID_045E&PID_028E&IG_00#{Real device VID}&{Real Device PID}&{Steam Input API handle}#{Steam Input Gamepad Index}#{ProcessID}
Ad esempio, \\.\pipe\HID#VID_045E&PID_028E&IG_00#045E&0B00&00045EB00704DAF3#0#20160 per un controller di Xbox One con un VID/PID di 0x45e/0x0b00.

Esempio di codice:
#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 ); [/i][/i][/i] Log( "Raw input device: VID = 0x%x, PID = 0x%x, handle = 0x%llx, index = 0x%x, %s\n", ulVID, ulPID, ulDeviceHandle, unGamepadIndex, devName ); // Ora si può usare VID/PID per identificare direttamente il dispositivo oppure ulDeviceHandle con GetInputTypeForHandle ESteamInputType inputType = SteamInput()->GetInputTypeForHandle( ulDeviceHandle ); switch( inputType ) { case k_ESteamInputType_Unknown: printf( "unknown!\n" ); break; case k_ESteamInputType_SteamController: printf( "Steam controller!\n" ); break; case k_ESteamInputType_XBox360Controller: printf( "XBox 360 controller!\n" ); break; case k_ESteamInputType_XBoxOneController: printf( "XBox One controller!\n" ); break; case k_ESteamInputType_GenericGamepad: printf( "Generic Gamepad(DirectInput)!\n" ); break; case k_ESteamInputType_PS3Controller: case k_ESteamInputType_PS4Controller: case k_ESteamInputType_PS5Controller: printf( "PlayStation controller!\n" ); break; } } else { // "Device" non è un controller tramite Steam Input. Usa la tua normale logica di ID dei controller. continue; } } } free(devices); }

Regolazione delle impostazioni di Steamworks

La prima cosa da fare è impostare quali controller useranno Steam Input, attraverso la sezione delle impostazioni di Steamworks sotto "Applicazione" e "Steam Input". Per un gioco standard compatibile con il controller della Xbox, consigliamo di spuntare tutte le caselle tranne quella relativa a Xbox:

steamworks_steam_input_optin_settings.png

Se il tuo gioco supporta l'utilizzo di un volante o una levetta di volo, è consigliabile deselezionare la casella relativa ai controller generici/DirectInput, poiché Steam non consente di rimappare tali dispositivi.

Selezionare una configurazione

Puoi scegliere se creare la tua configurazione o sceglierne una tra quelle da noi predefinite. Non è necessario creare una configurazione personalizzata, a meno che non si vogliano apportare modifiche alle nostre impostazioni predefinite o contrassegnare i singoli comandi in base all'effetto che producono in gioco. I template integrati di Steam Input sono tradotti in tutte le lingue di Steam, quindi assicurati di tradurre anche il tuo template per evitare di mostrare scritte non tradotte agli utenti che non parlano inglese.

Scegliere un teplate

I controller delle console non fanno distinzione tra i vari template dei diversi gamepad, ma queste impostazioni sono importanti per lo Steam Controller. Ecco una spiegazione dei template e i tipi di gioco per cui sono pensati:
  • Gamepad: questa configurazione emula un controller Xbox convertendo il trackpad destro in un joystick virtuale non elaborato. Perfetta per i giochi a due levette, platform o sportivi.
  • Gamepad con telecamera/mirino ad alta precisione: questa configurazione è ideale per un FPS o un qualsiasi gioco che utilizza i controlli della telecamera. Verifica che il gioco supporti l'uso simultaneo di controller e mouse prima di selezionare questa configurazione, anche in eventuali schermate accessorie come l'inventario o le schermate della mappa.
  • Gamepad con controlli della telecamera: questa configurazione rileva l'input di tipo mouse dal trackpad e lo traduce nei movimenti tipici di un joystick virtuale. Questa configurazione è adatta ai giochi che possono essere configurati con una sensibilità del joystick molto alta, nessuna zona morta e una curva di accelerazione lineare. In caso contrario, è meglio utilizzare una configurazione mouse/tastiera.

steam_input_gamepad_templates.png

Fai clic qui per i passaggi su come pubblicare il template per il tuo gioco su Steamworks

Creare una configurazione personalizzata

Le configurazioni possono essere convertite in maniera automatica tra gran parte dei tipi di controller, ma è necessario creare almeno una configurazione per lo Steam Controller e una per il controller Xbox.
Nota: se intendi utilizzare funzionalità specifiche di un dispositivo, come il rivelamento di movimento, dovrai avere una configurazione anche per il controller PlayStation 4 o quello Nintendo Switch.

Input di testo


L'input di testo a schermo non fa tecnicamente parte di ISteamInput (Steam Input), ma si trova invece in ISteamUtils. Al momento, questa funzione è implementata soltanto quando il giocatore avvia il gioco tramite la modalità Big Picture.

Alcuni riferimenti rapidi:

Tradurre la tua configurazione
Creare una configurazione tradotta diventa leggermente complicato quando non si usa l'API di Steam Input, ma comunque fattibile.
  • Per iniziare, assicurati che la tua configurazione sia stata aggiornata dall'ultima volta che è stata esportata. Apri il configuratore ed effettua una modifica (che poi potrà essere annullata).
  • Cerca la seguente stringa nel tuo registro Steam\Logs\controller_ui.xt:
    Steam Controller Saving Controller Configuration Autosave for [NUMERO SERIALE DEL CONTROLLER]- AppID: [IL TUO APPID]. Loaded Config for Local Selection Path for App ID [IL TUO APPID], Controller 0: F:\ProgramFiles\Steam\client\userdata\[ID DI STEAM]\config\controller_configs\apps\[IL TUO APPID]\[NUMERO SERIALE DEL CONTROLLER]\guest\controller_configuration.vdf
  • Inserisci i token di traduzione per i comandi che vuoi tradurre
    • Titolo/descrizione:
      "controller_mappings" { "version" "3" "revision" "5" "title" "#title" "description" "#description" ...
    • Assegnazioni dei pulsanti:
      "button_a" { "activators" { "Full_Press" { "bindings" { "binding" "xinput_button A, #abutton" } } } } ...
  • Poi, crea i valori corrispondenti per ogni lingua nel blocco di codice relativo alla traduzione:
    "localization" { "italian" { "title" "Questo è un titolo tradotto." "description" "Questa è una descrizione tradotta. Anche i nomi dei pulsanti frontali sono tradotti." "abutton" "Inserisci qui il nome da te assegnato 1" "bbutton" "Inserisci qui il nome da te assegnato 2" "xbutton" "Inserisci qui il nome da te assegnato 3" "ybutton" "Inserisci qui il nome da te assegnato 4" } ...
  • Ora puoi esportare la configurazione e impostarla nel sito dei partner seguendo queste istruzioni. Consigliamo di selezionare l'opzione "Usa blocco di azioni" per una delle tue configurazioni.

La configurazione di esempio può essere visualizzata nel client di Steam inserendo questo URL nel browser: steam://controllerconfig/681280/1744110334, oppure è possibile trovare il file VDF qui