Gestire le azioni del controller

A livello di sistema, Android segnala i codici evento di input dei controller come codici tasti e valori asse Android. Nel tuo gioco, puoi ricevere questi codici e valori e convertirli in azioni in-game specifiche.

Quando i giocatori collegano fisicamente o accoppiano tramite Bluetooth un controller ai propri dispositivi Android, il sistema rileva automaticamente il controller come dispositivo di input e inizia a segnalarne gli eventi di input. Il tuo gioco può ricevere questi eventi di input implementando i seguenti metodi di callback nel Activity attivo o nel View attivo (devi implementare i callback per Activity o View, ma non per entrambi):

L'approccio consigliato è acquisire gli eventi dall'oggetto View specifico con cui l'utente interagisce. Esamina i seguenti oggetti forniti dai callback per ottenere informazioni sul tipo di evento di input ricevuto:

KeyEvent
Un oggetto che descrive gli eventi del pulsante del pad direzionale (D-pad) e del gamepad. Gli eventi chiave sono accompagnati da un codice chiave che indica il pulsante specifico attivato, ad esempio DPAD_DOWN o BUTTON_A. Puoi ottenere il codice chiave chiamando getKeyCode() o dai callback degli eventi chiave come onKeyDown().
MotionEvent
Un oggetto che descrive l'input proveniente dai movimenti del joystick e dei trigger. Gli eventi relativi al movimento sono accompagnati da un codice di azione e da un insieme di valori degli assi. Il codice azione specifica il cambiamento di stato che si è verificato, ad esempio lo spostamento di un joystick. I valori dell'asse descrivono la posizione e altre proprietà di movimento per un controllo fisico specifico, ad esempio AXIS_X o AXIS_RTRIGGER. Puoi ottenere il codice di azione chiamando getAction() e il valore dell'asse chiamando getAxisValue().

Questa lezione spiega come gestire l'input dei tipi più comuni di controlli fisici (pulsanti del gamepad, d-pad e joystick) nella schermata di un gioco implementando i metodi di callback View e l'elaborazione degli oggetti KeyEvent e MotionEvent menzionati sopra.

Verificare che un controller di gioco sia collegato

Quando segnala gli eventi di input, Android non distingue tra gli eventi provenienti da un dispositivo diverso da un controller di gioco e quelli provenienti da un controller di gioco. Ad esempio, un'azione sul touchscreen genera un evento AXIS_X che rappresenta la coordinata X della superficie touch, mentre un joystick genera un evento AXIS_X che rappresenta la posizione X del joystick.AXIS_X Se il tuo gioco è interessato a gestire l'input del controller di gioco, devi prima controllare che l'evento di input provenga da un'origine pertinente.

Per verificare che un dispositivo di input collegato sia un controller, chiama getSources() per ottenere un campo di bit combinato dei tipi di sorgenti di input supportati sul dispositivo. Puoi quindi verificare se i seguenti campi sono impostati:

  • Un tipo di origine SOURCE_GAMEPAD indica che il dispositivo di input ha pulsanti del gamepad (ad esempio, BUTTON_A). Tieni presente che questo tipo di origine non indica esattamente se il controller di gioco ha pulsanti del D-pad, sebbene la maggior parte dei gamepad in genere abbia controlli direzionali.
  • Una sorgente di tipo SOURCE_DPAD indica che il dispositivo di input è dotato di pulsanti D-pad (ad esempio, DPAD_UP).
  • Una sorgente di tipo SOURCE_JOYSTICK indica che il dispositivo di input dispone di stick di controllo analogici (ad esempio, un joystick che registra i movimenti lungo AXIS_X e AXIS_Y).

Il seguente snippet di codice mostra un metodo di supporto che consente di verificare se i dispositivi di input collegati sono controller per videogiochi. In questo caso, il metodo recupera gli ID dispositivo per i controller di gioco. Puoi quindi associare ogni ID dispositivo a un giocatore nel tuo gioco ed elaborare le azioni di gioco per ogni giocatore collegato separatamente. Per scoprire di più sul supporto di più controller di gioco contemporaneamente collegati allo stesso dispositivo Android, consulta Supportare più controller di gioco.

Kotlin

fun getGameControllerIds(): List<Int> {
    val gameControllerDeviceIds = mutableListOf<Int>()
    val deviceIds = InputDevice.getDeviceIds()
    deviceIds.forEach { deviceId ->
        InputDevice.getDevice(deviceId).apply {

            // Verify that the device has gamepad buttons, control sticks, or both.
            if (sources and InputDevice.SOURCE_GAMEPAD == InputDevice.SOURCE_GAMEPAD
                    || sources and InputDevice.SOURCE_JOYSTICK == InputDevice.SOURCE_JOYSTICK) {
                // This device is a game controller. Store its device ID.
                gameControllerDeviceIds
                        .takeIf { !it.contains(deviceId) }
                        ?.add(deviceId)
            }
        }
    }
    return gameControllerDeviceIds
}

Java

public ArrayList<Integer> getGameControllerIds() {
    ArrayList<Integer> gameControllerDeviceIds = new ArrayList<Integer>();
    int[] deviceIds = InputDevice.getDeviceIds();
    for (int deviceId : deviceIds) {
        InputDevice dev = InputDevice.getDevice(deviceId);
        int sources = dev.getSources();

        // Verify that the device has gamepad buttons, control sticks, or both.
        if (((sources & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD)
                || ((sources & InputDevice.SOURCE_JOYSTICK)
                == InputDevice.SOURCE_JOYSTICK)) {
            // This device is a game controller. Store its device ID.
            if (!gameControllerDeviceIds.contains(deviceId)) {
                gameControllerDeviceIds.add(deviceId);
            }
        }
    }
    return gameControllerDeviceIds;
}

Inoltre, ti consigliamo di verificare le singole funzionalità di input supportate da un controller di gioco collegato. Questo può essere utile, ad esempio, se vuoi che il tuo gioco utilizzi solo l'input dall'insieme di controlli fisici che comprende.

Per rilevare se un codice chiave o un codice asse specifico è supportato da un controller di gioco collegato, utilizza queste tecniche:

  • In Android 4.4 (livello API 19) o versioni successive, puoi determinare se un codice chiave è supportato su un controller di gioco collegato chiamando hasKeys(int...).
  • In Android 3.1 (livello API 12) o versioni successive, puoi trovare tutti gli assi disponibili supportati su un controller di gioco collegato chiamando prima getMotionRanges(). Poi, su ogni oggetto InputDevice.MotionRange restituito, chiama getAxis() per ottenere l'ID asse.

Elaborare pressioni dei pulsanti del gamepad

La Figura 1 mostra come Android mappa i codici tasti e i valori dell'asse ai controlli fisici sulla maggior parte dei controller per videogiochi.

Figura 1. Profilo per un controller di gioco generico.

I callout nella figura si riferiscono a quanto segue:

I codici chiave comuni generati dalle pressioni dei pulsanti del gamepad includono BUTTON_A, BUTTON_B, BUTTON_SELECT e BUTTON_START. Alcuni controller per giochi attivano anche il codice a tasti DPAD_CENTER quando viene premuto il centro della barra trasversale del D-pad. Il tuo gioco può esaminare il codice chiave chiamando getKeyCode() o dai callback di eventi chiave come onKeyDown() e, se rappresenta un evento pertinente al tuo gioco, elaboralo come un'azione di gioco. La tabella 1 elenca le azioni di gioco consigliate per i pulsanti del gamepad più comuni.

Tabella 1. Azioni di gioco consigliate per i pulsanti del gamepad.

Azione di gioco Codice tasto del pulsante
Avviare la partita nel menu principale o mettere in pausa/riattivare la pausa durante la partita BUTTON_START*
Visualizza menu BUTTON_SELECT* e KEYCODE_MENU*
Lo stesso comportamento di navigazione Indietro di Android descritto nella Guida al design per la navigazione. KEYCODE_BACK
Tornare a un elemento precedente in un menu BUTTON_B
Conferma la selezione o esegui l'azione principale del gioco BUTTON_A e DPAD_CENTER

* Il gioco non deve fare affidamento sulla presenza dei pulsanti Start, Seleziona o Menu.

Suggerimento : ti consigliamo di fornire una schermata di configurazione nel tuo gioco per consentire agli utenti di personalizzare le mappature dei controller per le azioni di gioco.

Il seguente snippet mostra come eseguire l'override di onKeyDown() per associare le pressioni dei pulsanti BUTTON_A e DPAD_CENTER a un'azione di gioco.

Kotlin

class GameView(...) : View(...) {
    ...

    override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
        var handled = false
        if (event.source and InputDevice.SOURCE_GAMEPAD == InputDevice.SOURCE_GAMEPAD) {
            if (event.repeatCount == 0) {
                when (keyCode) {
                    // Handle gamepad and D-pad button presses to navigate the ship
                    ...

                    else -> {
                        keyCode.takeIf { isFireKey(it) }?.run {
                            // Update the ship object to fire lasers
                            ...
                            handled = true
                        }
                    }
                }
            }
            if (handled) {
                return true
            }
        }
        return super.onKeyDown(keyCode, event)
    }

    // Here we treat Button_A and DPAD_CENTER as the primary action
    // keys for the game.
    private fun isFireKey(keyCode: Int): Boolean =
            keyCode == KeyEvent.KEYCODE_DPAD_CENTER || keyCode == KeyEvent.KEYCODE_BUTTON_A
}

Java

public class GameView extends View {
    ...

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        boolean handled = false;
        if ((event.getSource() & InputDevice.SOURCE_GAMEPAD)
                == InputDevice.SOURCE_GAMEPAD) {
            if (event.getRepeatCount() == 0) {
                switch (keyCode) {
                    // Handle gamepad and D-pad button presses to
                    // navigate the ship
                    ...

                    default:
                         if (isFireKey(keyCode)) {
                             // Update the ship object to fire lasers
                             ...
                             handled = true;
                         }
                     break;
                }
            }
            if (handled) {
                return true;
            }
        }
        return super.onKeyDown(keyCode, event);
    }

    private static boolean isFireKey(int keyCode) {
        // Here we treat Button_A and DPAD_CENTER as the primary action
        // keys for the game.
        return keyCode == KeyEvent.KEYCODE_DPAD_CENTER
                || keyCode == KeyEvent.KEYCODE_BUTTON_A;
    }
}

Nota: su Android 4.2 (livello API 17) e versioni precedenti, il sistema considera BUTTON_A come il tasto Indietro di Android per impostazione predefinita. Se la tua app supporta queste versioni di Android, assicurati di considerare BUTTON_A come azione di gioco principale. Per determinare la versione corrente dell'SDK Android sul dispositivo, fai riferimento al valore Build.VERSION.SDK_INT.

Elabora l'input del d-pad

Il D-pad a 4 direzioni è un controllo fisico comune in molti controller per giochi. Android registra le pressioni del D-pad SU e GIU come eventi AXIS_HAT_Y con un intervallo da -1,0 (su) a 1,0 (giù) e le pressioni del D-pad SINISTRA o DESTRA come eventi AXIS_HAT_Y con un intervallo da -1,0 (sinistra) a 1,0 (destra).AXIS_HAT_X

Alcuni controller segnalano invece le pressioni del D-pad con un codice a tasti. Se il tuo gioco tiene conto delle pressioni del D-pad, devi trattare gli eventi dell'asse del joystick e i codici pulsante del D-pad come gli stessi eventi di input, come consigliato nella tabella 2.

Tabella 2. Azioni di gioco predefinite consigliate per i codici dei tasti del pad direzionale e i valori dell'asse del cappello.

Azione di gioco Codice tasto D-pad Codice asse cappello
Sposta su KEYCODE_DPAD_UP AXIS_HAT_Y (per valori da 0 a -1,0)
Sposta giù KEYCODE_DPAD_DOWN AXIS_HAT_Y (per valori da 0 a 1,0)
Sposta a sinistra KEYCODE_DPAD_LEFT AXIS_HAT_X (per valori da 0 a -1,0)
Sposta a destra KEYCODE_DPAD_RIGHT AXIS_HAT_X (per valori da 0 a 1,0)

Il seguente snippet di codice mostra una classe di supporto che ti consente di controllare i valori dell'asse e del codice chiave del hat da un evento di input per determinare la direzione del D-pad.

Kotlin

class Dpad {

    private var directionPressed = -1 // initialized to -1

    fun getDirectionPressed(event: InputEvent): Int {
        if (!isDpadDevice(event)) {
            return -1
        }

        // If the input event is a MotionEvent, check its hat axis values.
        (event as? MotionEvent)?.apply {

            // Use the hat axis value to find the D-pad direction
            val xaxis: Float = event.getAxisValue(MotionEvent.AXIS_HAT_X)
            val yaxis: Float = event.getAxisValue(MotionEvent.AXIS_HAT_Y)

            directionPressed = when {
                // Check if the AXIS_HAT_X value is -1 or 1, and set the D-pad
                // LEFT and RIGHT direction accordingly.
                xaxis.compareTo(-1.0f) == 0 -> Dpad.LEFT
                xaxis.compareTo(1.0f) == 0 -> Dpad.RIGHT
                // Check if the AXIS_HAT_Y value is -1 or 1, and set the D-pad
                // UP and DOWN direction accordingly.
                yaxis.compareTo(-1.0f) == 0 -> Dpad.UP
                yaxis.compareTo(1.0f) == 0 -> Dpad.DOWN
                else -> directionPressed
            }
        }
        // If the input event is a KeyEvent, check its key code.
        (event as? KeyEvent)?.apply {

            // Use the key code to find the D-pad direction.
            directionPressed = when(event.keyCode) {
                KeyEvent.KEYCODE_DPAD_LEFT -> Dpad.LEFT
                KeyEvent.KEYCODE_DPAD_RIGHT -> Dpad.RIGHT
                KeyEvent.KEYCODE_DPAD_UP -> Dpad.UP
                KeyEvent.KEYCODE_DPAD_DOWN -> Dpad.DOWN
                KeyEvent.KEYCODE_DPAD_CENTER ->  Dpad.CENTER
                else -> directionPressed
            }
        }
        return directionPressed
    }

    companion object {
        internal const val UP = 0
        internal const val LEFT = 1
        internal const val RIGHT = 2
        internal const val DOWN = 3
        internal const val CENTER = 4

        fun isDpadDevice(event: InputEvent): Boolean =
            // Check that input comes from a device with directional pads.
            event.source and InputDevice.SOURCE_DPAD != InputDevice.SOURCE_DPAD
    }
}

Java

public class Dpad {
    final static int UP       = 0;
    final static int LEFT     = 1;
    final static int RIGHT    = 2;
    final static int DOWN     = 3;
    final static int CENTER   = 4;

    int directionPressed = -1; // initialized to -1

    public int getDirectionPressed(InputEvent event) {
        if (!isDpadDevice(event)) {
           return -1;
        }

        // If the input event is a MotionEvent, check its hat axis values.
        if (event instanceof MotionEvent) {

            // Use the hat axis value to find the D-pad direction
            MotionEvent motionEvent = (MotionEvent) event;
            float xaxis = motionEvent.getAxisValue(MotionEvent.AXIS_HAT_X);
            float yaxis = motionEvent.getAxisValue(MotionEvent.AXIS_HAT_Y);

            // Check if the AXIS_HAT_X value is -1 or 1, and set the D-pad
            // LEFT and RIGHT direction accordingly.
            if (Float.compare(xaxis, -1.0f) == 0) {
                directionPressed =  Dpad.LEFT;
            } else if (Float.compare(xaxis, 1.0f) == 0) {
                directionPressed =  Dpad.RIGHT;
            }
            // Check if the AXIS_HAT_Y value is -1 or 1, and set the D-pad
            // UP and DOWN direction accordingly.
            else if (Float.compare(yaxis, -1.0f) == 0) {
                directionPressed =  Dpad.UP;
            } else if (Float.compare(yaxis, 1.0f) == 0) {
                directionPressed =  Dpad.DOWN;
            }
        }

        // If the input event is a KeyEvent, check its key code.
        else if (event instanceof KeyEvent) {

           // Use the key code to find the D-pad direction.
            KeyEvent keyEvent = (KeyEvent) event;
            if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_DPAD_LEFT) {
                directionPressed = Dpad.LEFT;
            } else if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_DPAD_RIGHT) {
                directionPressed = Dpad.RIGHT;
            } else if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_DPAD_UP) {
                directionPressed = Dpad.UP;
            } else if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_DPAD_DOWN) {
                directionPressed = Dpad.DOWN;
            } else if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_DPAD_CENTER) {
                directionPressed = Dpad.CENTER;
            }
        }
        return directionPressed;
    }

    public static boolean isDpadDevice(InputEvent event) {
        // Check that input comes from a device with directional pads.
        if ((event.getSource() & InputDevice.SOURCE_DPAD)
             != InputDevice.SOURCE_DPAD) {
             return true;
         } else {
             return false;
         }
     }
}

Puoi utilizzare questa classe di supporto nel tuo gioco ovunque tu voglia elaborare l'input del D-pad (ad esempio nei callback onGenericMotionEvent() o onKeyDown()).

Ad esempio:

Kotlin

private val dpad = Dpad()
...
override fun onGenericMotionEvent(event: MotionEvent): Boolean {
    if (Dpad.isDpadDevice(event)) {
        when (dpad.getDirectionPressed(event)) {
            Dpad.LEFT -> {
                // Do something for LEFT direction press
                ...
                return true
            }
            Dpad.RIGHT -> {
                // Do something for RIGHT direction press
                ...
                return true
            }
            Dpad.UP -> {
                // Do something for UP direction press
                ...
                return true
            }
            ...
        }
    }

    // Check if this event is from a joystick movement and process accordingly.
    ...
}

Java

Dpad dpad = new Dpad();
...
@Override
public boolean onGenericMotionEvent(MotionEvent event) {

    // Check if this event if from a D-pad and process accordingly.
    if (Dpad.isDpadDevice(event)) {

       int press = dpad.getDirectionPressed(event);
       switch (press) {
            case LEFT:
                // Do something for LEFT direction press
                ...
                return true;
            case RIGHT:
                // Do something for RIGHT direction press
                ...
                return true;
            case UP:
                // Do something for UP direction press
                ...
                return true;
            ...
        }
    }

    // Check if this event is from a joystick movement and process accordingly.
    ...
}

Elabora i movimenti del joystick

Quando i giocatori muovono un joystick sui controller per videogiochi, Android segnala un MotionEvent contenente il codice di azione ACTION_MOVE e le posizioni aggiornate degli assi del joystick. Il tuo gioco può utilizzare i dati forniti dal MotionEvent per determinare se si è verificato un movimento del joystick che lo interessa.

Tieni presente che gli eventi di movimento del joystick possono raggruppare più campioni di movimento in un unico oggetto. L'oggetto MotionEvent contiene la posizione corrente per ogni asse del joystick, nonché più posizioni storiche per ogni asse. Quando vengono registrati eventi di movimento con il codice di azione ACTION_MOVE (ad esempio i movimenti del joystick), Android raggruppa i valori dell'asse per motivi di efficienza. I valori storici per un asse sono costituiti dall'insieme di valori distinti precedenti al valore dell'asse corrente e più recenti dei valori registrati in eventuali eventi di movimento precedenti. Per ulteriori dettagli, consulta la documentazione di riferimento MotionEvent.

Puoi utilizzare le informazioni storiche per eseguire il rendering più accurato del movimento di un oggetto di gioco in base all'input del joystick. Per recuperare i valori correnti e storici, chiama getAxisValue() o getHistoricalAxisValue(). Puoi anche trovare il numero di punti storici nell'evento del joystick chiamando getHistorySize().

Lo snippet seguente mostra come eseguire l'override del callback onGenericMotionEvent() per elaborare l'input del joystick. Devi prima elaborare i valori storici di un asse, quindi la sua posizione attuale.

Kotlin

class GameView(...) : View(...) {

    override fun onGenericMotionEvent(event: MotionEvent): Boolean {

        // Check that the event came from a game controller
        return if (event.source and InputDevice.SOURCE_JOYSTICK == InputDevice.SOURCE_JOYSTICK
                && event.action == MotionEvent.ACTION_MOVE) {

            // Process the movements starting from the
            // earliest historical position in the batch
            (0 until event.historySize).forEach { i ->
                // Process the event at historical position i
                processJoystickInput(event, i)
            }

            // Process the current movement sample in the batch (position -1)
            processJoystickInput(event, -1)
            true
        } else {
            super.onGenericMotionEvent(event)
        }
    }
}

Java

public class GameView extends View {

    @Override
    public boolean onGenericMotionEvent(MotionEvent event) {

        // Check that the event came from a game controller
        if ((event.getSource() & InputDevice.SOURCE_JOYSTICK) ==
                InputDevice.SOURCE_JOYSTICK &&
                event.getAction() == MotionEvent.ACTION_MOVE) {

            // Process all historical movement samples in the batch
            final int historySize = event.getHistorySize();

            // Process the movements starting from the
            // earliest historical position in the batch
            for (int i = 0; i < historySize; i++) {
                // Process the event at historical position i
                processJoystickInput(event, i);
            }

            // Process the current movement sample in the batch (position -1)
            processJoystickInput(event, -1);
            return true;
        }
        return super.onGenericMotionEvent(event);
    }
}

Prima di utilizzare l'input del joystick, devi determinare se il joystick è centrato, quindi calcolare di conseguenza i movimenti dell'asse. I joystick in genere hanno un'area piatta, ovvero un intervallo di valori vicino alla coordinata (0,0) in cui l'asse è considerato centrato. Se il valore dell'asse riportato da Android rientra nell'area piatta, devi considerare che il controller è a riposo (ovvero immobile lungo entrambi gli assi).

Lo snippet riportato di seguito mostra un metodo di supporto che calcola il movimento lungo ciascun asse. Puoi richiamare questo helper nel metodo processJoystickInput() descritto più avanti.

Kotlin

private fun getCenteredAxis(
        event: MotionEvent,
        device: InputDevice,
        axis: Int,
        historyPos: Int
): Float {
    val range: InputDevice.MotionRange? = device.getMotionRange(axis, event.source)

    // A joystick at rest does not always report an absolute position of
    // (0,0). Use the getFlat() method to determine the range of values
    // bounding the joystick axis center.
    range?.apply {
        val value: Float = if (historyPos < 0) {
            event.getAxisValue(axis)
        } else {
            event.getHistoricalAxisValue(axis, historyPos)
        }

        // Ignore axis values that are within the 'flat' region of the
        // joystick axis center.
        if (Math.abs(value) > flat) {
            return value
        }
    }
    return 0f
}

Java

private static float getCenteredAxis(MotionEvent event,
        InputDevice device, int axis, int historyPos) {
    final InputDevice.MotionRange range =
            device.getMotionRange(axis, event.getSource());

    // A joystick at rest does not always report an absolute position of
    // (0,0). Use the getFlat() method to determine the range of values
    // bounding the joystick axis center.
    if (range != null) {
        final float flat = range.getFlat();
        final float value =
                historyPos < 0 ? event.getAxisValue(axis):
                event.getHistoricalAxisValue(axis, historyPos);

        // Ignore axis values that are within the 'flat' region of the
        // joystick axis center.
        if (Math.abs(value) > flat) {
            return value;
        }
    }
    return 0;
}

Riassumendo, ecco come puoi elaborare i movimenti del joystick nel tuo gioco:

Kotlin

private fun processJoystickInput(event: MotionEvent, historyPos: Int) {

    val inputDevice = event.device

    // Calculate the horizontal distance to move by
    // using the input value from one of these physical controls:
    // the left control stick, hat axis, or the right control stick.
    var x: Float = getCenteredAxis(event, inputDevice, MotionEvent.AXIS_X, historyPos)
    if (x == 0f) {
        x = getCenteredAxis(event, inputDevice, MotionEvent.AXIS_HAT_X, historyPos)
    }
    if (x == 0f) {
        x = getCenteredAxis(event, inputDevice, MotionEvent.AXIS_Z, historyPos)
    }

    // Calculate the vertical distance to move by
    // using the input value from one of these physical controls:
    // the left control stick, hat switch, or the right control stick.
    var y: Float = getCenteredAxis(event, inputDevice, MotionEvent.AXIS_Y, historyPos)
    if (y == 0f) {
        y = getCenteredAxis(event, inputDevice, MotionEvent.AXIS_HAT_Y, historyPos)
    }
    if (y == 0f) {
        y = getCenteredAxis(event, inputDevice, MotionEvent.AXIS_RZ, historyPos)
    }

    // Update the ship object based on the new x and y values
}

Java

private void processJoystickInput(MotionEvent event,
        int historyPos) {

    InputDevice inputDevice = event.getDevice();

    // Calculate the horizontal distance to move by
    // using the input value from one of these physical controls:
    // the left control stick, hat axis, or the right control stick.
    float x = getCenteredAxis(event, inputDevice,
            MotionEvent.AXIS_X, historyPos);
    if (x == 0) {
        x = getCenteredAxis(event, inputDevice,
                MotionEvent.AXIS_HAT_X, historyPos);
    }
    if (x == 0) {
        x = getCenteredAxis(event, inputDevice,
                MotionEvent.AXIS_Z, historyPos);
    }

    // Calculate the vertical distance to move by
    // using the input value from one of these physical controls:
    // the left control stick, hat switch, or the right control stick.
    float y = getCenteredAxis(event, inputDevice,
            MotionEvent.AXIS_Y, historyPos);
    if (y == 0) {
        y = getCenteredAxis(event, inputDevice,
                MotionEvent.AXIS_HAT_Y, historyPos);
    }
    if (y == 0) {
        y = getCenteredAxis(event, inputDevice,
                MotionEvent.AXIS_RZ, historyPos);
    }

    // Update the ship object based on the new x and y values
}

Per supportare i controller di gioco con funzionalità più sofisticate rispetto a un singolo joystick, segui queste best practice:

  • Gestisci i due stick del controller. Molti controller di gioco hanno sia un joystick sinistro che uno destro. Per il joystick sinistro, Android registra i movimenti orizzontali come eventi AXIS_X e i movimenti verticali come eventi AXIS_Y. Per la levetta destra, Android registra i movimenti orizzontali come eventi AXIS_Z e i movimenti verticali come eventi AXIS_RZ. Assicurati di gestire entrambe le leve del controller nel codice.
  • Gestisci le pressioni dei trigger laterali (e assicurati che il tuo gioco funzioni con gli eventi AXIS_ e KEYCODE_BUTTON_). Alcuni controller hanno i grilletti sinistro e destro. Quando sono presenti, questi attivatori emettono uno o entrambi gli eventi AXIS_*TRIGGER o KEYCODE_BUTTON_*2. Per il grilletto sinistro, si tratta di AXIS_LTRIGGER e KEYCODE_BUTTON_L2. Per l'attivatore corretto, si tratta di AXIS_RTRIGGER e KEYCODE_BUTTON_R2. Gli eventi asse si verificano solo se l'attivatore emette un intervallo di valori compreso tra 0 e 1 e alcuni controller con uscita analogica emettono eventi di pulsante oltre agli eventi asse. I giochi devono supportare entrambi gli eventi AXIS_ e KEYCODE_BUTTON_ per rimanere compatibili con tutti i controller per videogiochi comuni, ma preferiscono l'evento più adatto al gameplay se un controller ne segnala entrambi. Su Android 4.3 (livello API 18) e versioni successive, un controller che produce un AXIS_LTRIGGER riporta anche un valore identico per l'asse AXIS_BRAKE. Lo stesso vale per AXIS_RTRIGGER e AXIS_GAS. Android segnala tutte le pressioni dei trigger analogiche con un valore normalizzato da 0,0 (rilasciato) a 1,0 (premuto completamente).
  • Il supporto e i comportamenti specifici possono variare negli ambienti virtualizzati. Il comportamento delle piattaforme emulate, come Google Play Giochi, può variare leggermente in base alle funzionalità del sistema operativo host. Ad esempio, alcuni controller che emettono sia eventi AXIS_ che KEYCODE_BUTTON_ emettono solo eventi AXIS_ e il supporto di alcuni controller potrebbe non essere presente del tutto.