Controlleraktionen verarbeiten

Auf Systemebene meldet Android Eingabeereigniscodes von Gamecontrollern als Android-Schlüsselcodes und -Achsenwerte. In Ihrem Spiel können Sie diese Codes und Werte erhalten und in bestimmte In-Game-Aktionen umwandeln.

Wenn Spieler einen Gamecontroller physisch mit ihren Android-Geräten verbinden oder kabellos koppeln, erkennt das System den Controller automatisch als Eingabegerät und beginnt, seine Eingabeereignisse zu melden. Dein Spiel kann diese Eingabeereignisse empfangen, indem du die folgenden Callback-Methoden in den aktiven Activity oder die fokussierte View implementierst. Du solltest die Callbacks entweder für Activity oder für View implementieren, aber nicht für beide:

Es wird empfohlen, die Ereignisse aus dem spezifischen View-Objekt zu erfassen, mit dem der Nutzer interagiert. Untersuchen Sie die folgenden von den Callbacks bereitgestellten Objekte, um Informationen zum Typ des empfangenen Eingabeereignisses zu erhalten:

KeyEvent
Ein Objekt, das die Richtungspads (Steuerkreuz) und Gamepad-Tastenereignisse beschreibt. Zu Schlüsselereignissen gehört ein Schlüsselcode, der die ausgelöste Schaltfläche angibt, z. B. DPAD_DOWN oder BUTTON_A. Sie können den Schlüsselcode abrufen, indem Sie getKeyCode() oder über Schlüsselereignis-Callbacks wie onKeyDown() aufrufen.
MotionEvent
Ein Objekt, das die Eingaben über den Joystick und den Schulterauslöser beschreibt. Bewegungsereignisse werden durch einen Aktionscode und eine Reihe von Achsenwerten begleitet. Der Aktionscode gibt die Statusänderung an, z. B. wenn ein Joystick bewegt wird. Die Achsenwerte beschreiben die Position und andere Bewegungseigenschaften für eine bestimmte physische Steuerung, z. B. AXIS_X oder AXIS_RTRIGGER. Um den Aktionscode zu erhalten, rufen Sie getAction() und den Achsenwert durch Aufrufen von getAxisValue() auf.

In dieser Lektion erfahren Sie, wie Sie Eingaben über die gängigsten physischen Steuerelemente (Gamepad-Tasten, Richtungstasten und Joysticks) in einem Spielebildschirm verarbeiten. Dazu implementieren Sie die oben genannten Callback-Methoden View und verarbeiten die Objekte KeyEvent und MotionEvent.

Prüfen, ob ein Gamecontroller verbunden ist

Beim Melden von Eingabeereignissen unterscheidet Android nicht zwischen Ereignissen, die von einem Gerät, das kein Gamecontroller ist, und Ereignissen, die von einem Gamecontroller stammen. Bei einer Touchscreen-Aktion wird beispielsweise ein AXIS_X-Ereignis generiert, das die X-Koordinate der Touchoberfläche darstellt, während ein Joystick ein AXIS_X-Ereignis generiert, das die X-Position des Joysticks darstellt. Wenn die Verarbeitung von Game-Controller-Eingaben in Ihrem Spiel wichtig ist, sollten Sie zuerst prüfen, ob das Eingabeereignis von einem relevanten Quelltyp stammt.

Wenn Sie prüfen möchten, ob ein verbundenes Eingabegerät ein Gamecontroller ist, rufen Sie getSources() auf. Dadurch erhalten Sie ein kombiniertes Bitfeld mit den auf diesem Gerät unterstützten Eingabequellentypen. Anschließend können Sie testen, ob die folgenden Felder festgelegt sind:

  • Der Quelltyp SOURCE_GAMEPAD bedeutet, dass das Eingabegerät Gamepad-Tasten hat (z. B. BUTTON_A). Beachte, dass dieser Quelltyp nicht genau angibt, ob der Gamecontroller Steuerkreuztasten hat, obwohl die meisten Gamepads normalerweise Richtungssteuerelemente haben.
  • Der Quelltyp SOURCE_DPAD bedeutet, dass das Eingabegerät Tasten auf dem Steuerkreuz hat, z. B. DPAD_UP.
  • Der Quelltyp SOURCE_JOYSTICK gibt an, dass das Eingabegerät analoge Sticks hat, z. B. einen Joystick, der Bewegungen entlang von AXIS_X und AXIS_Y aufzeichnet.

Das folgende Code-Snippet zeigt eine Hilfsmethode, mit der Sie prüfen können, ob die verbundenen Eingabegeräte Gamecontroller sind. In diesem Fall ruft die Methode die Geräte-IDs für die Game-Controller ab. Anschließend können Sie jede Geräte-ID einem Spieler in Ihrem Spiel zuordnen und Spielaktionen für jeden verbundenen Spieler separat verarbeiten. Weitere Informationen zur Unterstützung mehrerer Gamecontroller, die gleichzeitig mit demselben Android-Gerät verbunden sind, findest du unter Mehrere Gamecontroller unterstützen.

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;
}

Außerdem können Sie einzelne Eingabefunktionen prüfen, die von einem verbundenen Gamecontroller unterstützt werden. Dies kann beispielsweise nützlich sein, wenn Ihr Spiel nur Eingaben aus der Reihe physischer Steuerelemente verwenden soll, die es versteht.

Mit den folgenden Methoden können Sie feststellen, ob ein bestimmter Schlüsselcode oder Achsencode von einem verbundenen Game-Controller unterstützt wird:

  • Unter Android 4.4 (API-Level 19) und höher kannst du durch Aufrufen von hasKeys(int...) feststellen, ob ein Schlüsselcode auf einem verbundenen Gamecontroller unterstützt wird.
  • Ab Android 3.1 (API-Level 12) findest du alle verfügbaren Achsen, die auf einem verbundenen Gamecontroller unterstützt werden, indem du zuerst getMotionRanges() aufrufst. Rufen Sie dann für jedes zurückgegebene InputDevice.MotionRange-Objekt getAxis() auf, um die Achsen-ID abzurufen.

Tastendruck des Gamepads verarbeiten

Abbildung 1 zeigt, wie Android den physischen Steuerelementen auf den meisten Controllern Schlüsselcodes und Achsenwerte zuordnet.

Abbildung 1: Profil für einen generischen Gamecontroller

Die Zusatzinformationen in der Abbildung beziehen sich auf Folgendes:

Gängige Tastencodes, die durch Drücken der Gamepad-Taste generiert werden, sind BUTTON_A, BUTTON_B, BUTTON_SELECT und BUTTON_START. Einige Game-Controller lösen auch den Tastencode DPAD_CENTER aus, wenn die Mitte der Querleiste des Steuerkreuzes gedrückt wird. Dein Spiel kann den Schlüsselcode prüfen, indem es getKeyCode() oder über Schlüsselereignis-Callbacks wie onKeyDown() aufruft. Wenn es ein für dein Spiel relevantes Ereignis repräsentiert, wird es als Spielaktion verarbeitet. In Tabelle 1 sind die empfohlenen Spielaktionen für die gängigsten Gamepad-Schaltflächen aufgeführt.

Tabelle 1 Empfohlene Spielaktionen für Gamepad-Tasten

Spiel-Action Tastencode
Spiel im Hauptmenü starten oder während des Spiels pausieren/Pausierung aufheben BUTTON_START*
Menü einblenden BUTTON_SELECT* und KEYCODE_MENU*
Entspricht dem Verhalten der Android-Funktion Zurück, das in der Designanleitung Navigation beschrieben wird. KEYCODE_BACK
Zum vorherigen Element in einem Menü zurückkehren BUTTON_B
Auswahl bestätigen oder primäre Spielaktion ausführen BUTTON_A und DPAD_CENTER

* Für dein Spiel sollte die Start-, Auswahl- oder Menütaste nicht erforderlich sein.

Tipp : Sie können in Ihrem Spiel einen Konfigurationsbildschirm bereitstellen, damit Nutzer ihre eigenen Controller-Zuordnungen für Spielaktionen personalisieren können.

Das folgende Snippet zeigt, wie Sie onKeyDown() überschreiben können, um das Drücken der BUTTON_A- und DPAD_CENTER-Schaltflächen einer Spielaktion zuzuordnen.

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;
    }
}

Hinweis : Unter Android 4.2 (API-Ebene 17) und niedriger behandelt das System BUTTON_A standardmäßig als Android-Schlüssel Zurück. Wenn deine App diese Android-Versionen unterstützt, musst du BUTTON_A als primäre Spielaktion behandeln. Die aktuelle Version des Android SDK auf dem Gerät kannst du anhand des Werts für Build.VERSION.SDK_INT ermitteln.

Eingabe der Richtungstasten verarbeiten

Die 4-Wege-Richtungstasten (D-Pad) sind eine gängige physische Steuerung in vielen Game-Controllern. Android meldet das Drücken des Steuerkreuzes nach oben und unten als AXIS_HAT_Y-Ereignisse mit einem Bereich von -1,0 (nach oben) bis 1,0 (nach unten) und das Drücken des Steuerkreuzes nach links oder rechts als AXIS_HAT_X-Ereignisse mit einem Bereich von -1,0 (links) bis 1,0 (rechts).

Einige Controller melden das Drücken des Steuerkreuzes stattdessen mit einem Tastencode. Wenn für Ihr Spiel das Drücken des Steuerkreuzes wichtig ist, sollten Sie die Ereignisse für die Hut-Achse und die Tastencodes des Steuerkreuzes wie in Tabelle 2 empfohlen als dieselben Eingabeereignisse behandeln.

Tabelle 2 Empfohlene Standardspielaktionen für Tastenkombinationen und Werte der Hutachse auf dem Steuerkreuz.

Spiel-Action Tastencode des Steuerkreuzes Hutachsencode
Nach oben KEYCODE_DPAD_UP AXIS_HAT_Y (für die Werte 0 bis -1,0)
Nach unten KEYCODE_DPAD_DOWN AXIS_HAT_Y (für die Werte 0 bis 1,0)
Nach links bewegen KEYCODE_DPAD_LEFT AXIS_HAT_X (für die Werte 0 bis -1,0)
Nach rechts bewegen KEYCODE_DPAD_RIGHT AXIS_HAT_X (für die Werte 0 bis 1,0)

Das folgende Code-Snippet zeigt eine Hilfsklasse, mit der Sie die Hut-Achse und die Schlüsselcodewerte eines Eingabeereignisses überprüfen können, um die Richtung des Steuerkreuzes zu bestimmen.

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;
         }
     }
}

Sie können diese Hilfsklasse überall in Ihrem Spiel verwenden, wo Sie Eingaben über das Steuerkreuz verarbeiten möchten, z. B. im onGenericMotionEvent()- oder onKeyDown()-Callback.

Beispiele:

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.
    ...
}

Joystickbewegungen verarbeiten

Wenn Spieler einen Joystick auf ihren Controllern bewegen, meldet Android ein MotionEvent, das den ACTION_MOVE-Aktionscode und die aktualisierten Positionen der Joystick-Achsen enthält. Dein Spiel kann anhand der von MotionEvent bereitgestellten Daten feststellen, ob eine für das Spiel wichtige Joystickbewegung stattgefunden hat.

Bewegungsereignisse des Joysticks können mehrere Bewegungsproben innerhalb eines Objekts zusammenfassen. Das MotionEvent-Objekt enthält die aktuelle Position für jede Joystick-Achse sowie mehrere Verlaufspositionen für jede Achse. Bei der Meldung von Bewegungsereignissen mit dem Aktionscode ACTION_MOVE (z. B. Joystickbewegungen) fasst Android die Achsenwerte aus Gründen der Effizienz zusammen. Die historischen Werte für eine Achse bestehen aus einer Reihe unterschiedlicher Werte, die älter als der aktuelle Achsenwert sind und aktueller als die Werte, die bei früheren Bewegungsereignissen gemeldet wurden. Weitere Informationen finden Sie in der MotionEvent-Referenz.

Anhand der Verlaufsdaten können Sie die Bewegung eines Spielobjekts basierend auf der Joystickeingabe genauer rendern. Rufen Sie zum Abrufen der aktuellen und bisherigen Werte getAxisValue() oder getHistoricalAxisValue() auf. Die Anzahl der bisherigen Punkte im Joystick-Ereignis lässt sich auch durch Aufrufen von getHistorySize() abrufen.

Das folgende Snippet zeigt, wie Sie den Callback onGenericMotionEvent() überschreiben können, um die Joystickeingabe zu verarbeiten. Sie sollten zuerst die historischen Werte für eine Achse und dann ihre aktuelle Position verarbeiten.

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);
    }
}

Bevor Sie die Joystick-Eingabe verwenden können, müssen Sie ermitteln, ob der Joystick zentriert ist, und dann seine Achsenbewegungen entsprechend berechnen. Joysticks haben in der Regel eine flache Fläche, also einen Wertebereich in der Nähe der Koordinate (0, 0), in der die Achse als zentriert betrachtet wird. Wenn der von Android gemeldete Achsenwert in dem flachen Bereich liegt, sollten Sie den Controller als Ruhezustand betrachten, d. h. ihn auf beiden Achsen beweglich machen.

Das folgende Snippet zeigt eine Hilfsmethode, mit der die Bewegung entlang jeder Achse berechnet wird. Sie rufen dieses Hilfsprogramm in der unten näher beschriebenen processJoystickInput()-Methode auf.

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;
}

Wenn Sie alles zusammensetzen, können Sie die Joystickbewegungen in Ihrem Spiel wie folgt verarbeiten:

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
}

Zur Unterstützung von Controllern, die über einen einzelnen Joystick hinaus anspruchsvollere Funktionen haben, sollten Sie die folgenden Best Practices befolgen:

  • Mit Dual-Controller-Sticks umgehen Viele Controller haben einen linken und einen rechten Joystick. Für den linken Stick meldet Android horizontale Bewegungen als AXIS_X-Ereignisse und vertikale Bewegungen als AXIS_Y-Ereignisse. Für den rechten Stick meldet Android horizontale Bewegungen als AXIS_Z-Ereignisse und vertikale Bewegungen als AXIS_RZ-Ereignisse. Behandeln Sie beide Controller-Sticks in Ihrem Code.
  • Drücken von Schulter-Triggern verarbeiten (aber alternative Eingabemethoden bereitstellen). Einige Controller haben einen linken und rechten Schulter-Trigger. Wenn diese Trigger vorhanden sind, meldet Android das Drücken des linken Triggers als AXIS_LTRIGGER-Ereignis und das Drücken eines rechten Triggers als AXIS_RTRIGGER-Ereignis. Unter Android 4.3 (API-Level 18) meldet ein Controller, der einen AXIS_LTRIGGER erzeugt, auch einen identischen Wert für die AXIS_BRAKE-Achse. Dasselbe gilt für AXIS_RTRIGGER und AXIS_GAS. Android meldet alle Drücken von analogen Triggern mit einem normalisierten Wert zwischen 0,0 (veröffentlicht) und 1,0 (vollständig betätigt). Nicht alle Controller haben Trigger. Erwägen Sie daher, Spielern zu erlauben, diese Spielaktionen mit anderen Schaltflächen auszuführen.