Usare un normale Pad/Joystik con XNA

Xna, di sua natura, mette a disposizione una classe (GamePad) per l'utilizzo del joypad Xbox 360. E' possibile utilizzare questo joypad anche su un pc ma noi non volgiamo che un utente sia costretto a comprare un apposito joypad per giocare con il nostro gioco, noi volgiamo che si possa usare un qualunque joypad il giocatore abbia a disposizione (purché riconosciuto dalle DirectX di Windows).
Esiste un programmino che, se copiato nella cartella dell'eseguibile del nostro gioco, emula un controller Xbox 360. Noi però siamo "professionisti" e vogliamo scrivere il nostro codice senza usare questo emulatore così da poter gestire al cento per cento e nei dettagli l'uso del joypad.


Per fare questo ci appoggeremo ad una .dll chiamata Soopah che potrete scaricare sia dal sito dell'autore che direttamente a questo link. Nel caso in cui vogliate effettuare delle modifiche al codice della .dll, sul sito dell'autore sono presenti anche i files sorgenti della soluzione.

Scaricata la dll, bisogna inanzitutto renderla disponibile tra i riferimenti del nostro progetto.
Dunque, sulla soluzione del progetto clicchiamo su "Riferimenti" (il primo elemento dopo "Properties", non quello dentro "Content"), clicchiamo con il tasto destro, scegliamo "Aggiungi Riferimento" e poi, sulla linguetta "Sfoglia" andiamo ad aggiungere la dll appena scaricata: Soopah.Xna.Input.dll.

Ora dovremo creare la classe che gestirà il nostro joypad, vi basterà creare una nuova classe nel vostro progetto e incollarci questo codice:

       
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework;
using Soopah.Xna.Input; //Ecco la riga che permette di usare la Soopah.dll e le sue classi.

namespace GamePadClass
{

public static class MyGamePad
{

public struct MyGamePadState
{
public MyGamePadButtons Buttons;
public MyGamePadDPad DPad;
public MyGamePadThumbSticks Thumbsticks;
}

public struct MyGamePadButtons
{
public ButtonState button_1;
public ButtonState button_2;
public ButtonState button_3;
public ButtonState button_4;
public ButtonState button_5;
public ButtonState button_6;
public ButtonState button_7;
public ButtonState button_8;
public ButtonState button_9;
public ButtonState button_10;
public ButtonState button_11;
public ButtonState button_12;
}

public struct MyGamePadDPad
{
public ButtonState Left;
public ButtonState Right;
public ButtonState Up;
public ButtonState Down;
}

public struct MyGamePadThumbSticks
{
public Vector2 Left;
public Vector2 Right;
}

public static MyGamePadState GetState(PlayerIndex player)
{
int playerIndex = System.Convert.ToInt16(player);

MyGamePadState myGamePadState = new MyGamePadState();

myGamePadState.Buttons.button_1 = Soopah.Xna.Input.DirectInputGamepad.Gamepads[playerIndex].Buttons.List[0];
myGamePadState.Buttons.button_2 = Soopah.Xna.Input.DirectInputGamepad.Gamepads[playerIndex].Buttons.List[1];
myGamePadState.Buttons.button_3 = Soopah.Xna.Input.DirectInputGamepad.Gamepads[playerIndex].Buttons.List[2];
myGamePadState.Buttons.button_4 = Soopah.Xna.Input.DirectInputGamepad.Gamepads[playerIndex].Buttons.List[3];
myGamePadState.Buttons.button_5 = Soopah.Xna.Input.DirectInputGamepad.Gamepads[playerIndex].Buttons.List[4];
myGamePadState.Buttons.button_6 = Soopah.Xna.Input.DirectInputGamepad.Gamepads[playerIndex].Buttons.List[5];
myGamePadState.Buttons.button_7 = Soopah.Xna.Input.DirectInputGamepad.Gamepads[playerIndex].Buttons.List[6];
myGamePadState.Buttons.button_8 = Soopah.Xna.Input.DirectInputGamepad.Gamepads[playerIndex].Buttons.List[7];
myGamePadState.Buttons.button_9 = Soopah.Xna.Input.DirectInputGamepad.Gamepads[playerIndex].Buttons.List[8];
myGamePadState.Buttons.button_10 = Soopah.Xna.Input.DirectInputGamepad.Gamepads[playerIndex].Buttons.List[9];
// myGamePadState.Buttons.button_11 = Soopah.Xna.Input.DirectInputGamepad.Gamepads[playerIndex].Buttons.List[10];
// myGamePadState.Buttons.button_12 = Soopah.Xna.Input.DirectInputGamepad.Gamepads[playerIndex].Buttons.List[11];

myGamePadState.DPad.Down = Soopah.Xna.Input.DirectInputGamepad.Gamepads[playerIndex].DPad.Down;
myGamePadState.DPad.Up = Soopah.Xna.Input.DirectInputGamepad.Gamepads[playerIndex].DPad.Up;
myGamePadState.DPad.Left = Soopah.Xna.Input.DirectInputGamepad.Gamepads[playerIndex].DPad.Left;
myGamePadState.DPad.Right = Soopah.Xna.Input.DirectInputGamepad.Gamepads[playerIndex].DPad.Right;

myGamePadState.Thumbsticks.Left = Soopah.Xna.Input.DirectInputGamepad.Gamepads[playerIndex].ThumbSticks.Left;
//myGamePadState.Thumbsticks.Right = Soopah.Xna.Input.DirectInputGamepad.Gamepads[playerIndex].ThumbSticks.Right;

return myGamePadState;
}
}
}





Come avrete notato ci sono alcune righe "commentate" che dunque non verranno eseguite. Ho preferito centrare il tutorial su ciò che riguarda un joypad semplice, con 10 tasti e senza movimenti analogici. Da notare che utilizzeremo il movimento del thumbsticks per rilevare il movimento del pad normale, voi potrete fare tutte le prove che volete a seconda del joypad che vorrete utilizzare, usando lo stick Left, lo stick Right o il DPad.

A questo punto potremo utilizzare il nostro joypad, ovvero, nel metodo Update() del progetto potremo inserire questo codice:

protected override void Update(GameTime gameTime)
{

if (MyGamePad.GetState(PlayerIndex.One).Buttons.button_1 == ButtonState.Pressed)
{
//....codice da eseguire per la pressione del Pulsante 1
}

if (MyGamePad.GetState(PlayerIndex.One).Thumbsticks.Left.X > 0.1f)
{
//....codice da eseguire per la pressione del pad verso destra
}

if (MyGamePad.GetState(PlayerIndex.One).Thumbsticks.Left.X < -0.1f)
{
//....codice da eseguire per la pressione del pad verso sinistra
}

if (MyGamePad.GetState(PlayerIndex.One).Thumbsticks.Left.Y < -0.1f)
{
//....codice da eseguire per la pressione del pad verso l'alto
}

if (MyGamePad.GetState(PlayerIndex.One).Thumbsticks.Left.Y > 0.1f)
{
//....codice da eseguire per la pressione del pad verso il basso
}

....
}


N.B: E' probabile che possa ritornare questo errore durante la complazione:

Tentativo di esecuzione gestita da parte della DLL 'C:\Windows\assembly\GAC\Microsoft.DirectX.DirectInput\1.0.2902.0__31bf3856ad364e35\Microsoft.DirectX.DirectInput.dll' all'interno del blocco OS Loader. Non provare a eseguire codice gestito all'interno di una DllMain o di una funzione di inizializzazione delle immagini. Questa operazione può causare il blocco dell'applicazione.

Questo accade con alcune versioni di Visual Studio (compresa la Express 2008) ma non è un vero errore di compilazione, basta continuare la compilazione e ignorare questo avvertimento che non condizionerà la corretta esecuzione del programma.
Per evitare questa segnalazione, è sufficiente togliere la spuntatura su " LoaderLock" nella gestione delle eccezioni. Per aprire questa finestra, cliccare sul menù Debug/Eccezioni... e togliere la spuntature dove indicato.

Gestione eccezioni

Grazie a johnnyfreak per il supporto dato a questo tutorial. :)