Controlleraktionen verarbeiten

Controller haben zwei Arten von Aktionen:

  • KeyEvent für jede Taste mit einem binären Status „Ein“ und „Aus“
  • MotionEvent für jede Achse, die einen Wertebereich zurückgibt z. B. -1 bis 1 für Analog-Sticks oder 0 bis 1 für analoge Trigger

Sie können diese Eingaben aus der View lesen, die focus hat.

Kotlin

override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
  if (event.isFromSource(SOURCE_GAMEPAD)
      && event.repeatCount == 0
  ) {
      Log.d("GameView", "Gamepad key pressed: $keyCode")
      return true
  }

  return super.onKeyDown(keyCode, event)
}

override fun onGenericMotionEvent(event: MotionEvent): Boolean {
  if (event.isFromSource(SOURCE_JOYSTICK)) {
      Log.d("GameView", "Gamepad event: $event")
      return true
  }

  return super.onGenericMotionEvent(event)
}

Java

@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
  if (event.isFromSource(SOURCE_GAMEPAD)
          && event.getRepeatCount() == 0
  ) {
      Log.d("GameView", "Gamepad key pressed: " + keyCode);
      return true;
  }

  return super.onKeyDown(keyCode, event);
}

@Override
public boolean onGenericMotionEvent(MotionEvent event) {
  if (event.isFromSource(SOURCE_JOYSTICK)) {
      Log.d("GameView", "Gamepad event: " + event);
      return true;
  }
  return super.onGenericMotionEvent(event);
}

Bei Bedarf können Sie Ereignisse stattdessen direkt aus dem Activity lesen.

Prüfen, ob ein Gamecontroller verbunden ist

Beim Melden von Eingabeereignissen verwendet Android dieselben Schlüssel- oder Achsen-IDs für verschiedene Eingabegerätetypen. Beispielsweise generiert eine Touchscreen-Aktion ein AXIS_X-Ereignis, das die X -Koordinate der Touch-Oberfläche darstellt. Ein Gamepad generiert jedoch ein AXIS_X-Ereignis, das die X -Position des linken Sticks darstellt. Das bedeutet, dass Sie den Quelltyp prüfen müssen, um Eingabeereignisse richtig zu interpretieren.

Verwenden Sie die supportsSource(int) Funktion, um zu prüfen, ob ein verbundenes InputDevice ein Gamecontroller ist:

  • Ein Quelltyp von SOURCE_GAMEPAD gibt an, dass das Eingabegerät Controller-Tasten hat (z. B. KEYCODE_BUTTON_A). Beachten Sie , dass dieser Quelltyp nicht unbedingt angibt, ob der Gamecontroller Steuerkreuz-Tasten hat, obwohl die meisten Controller in der Regel Richtungstasten haben.
  • Ein Quelltyp von SOURCE_DPAD gibt an, dass das Eingabegerät Steuerkreuz-Tasten hat (z. B. DPAD_UP).
  • Ein Quelltyp von SOURCE_JOYSTICK gibt an, dass das Eingabegerät analoge Steuersticks hat (z. B. ein Joystick, der Bewegungen entlang AXIS_X und AXIS_Y erfasst).

Das folgende Code-Snippet zeigt eine Hilfsmethode, mit der Sie prüfen können, ob die verbundenen Eingabegeräte Gamecontroller sind. Wenn ja, ruft die Methode die Geräte-IDs für die Gamecontroller 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, finden Sie 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 (supportsSource(SOURCE_GAMEPAD)
              || supportsSource(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);

      if (dev == null) {
          continue;
      }

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

Controllereingaben verarbeiten

In diesem Abschnitt werden die Typen für Gamecontroller beschrieben, die unter Android unterstützt werden.

C++-Entwickler sollten die Game Controller Library verwenden. Sie vereinheitlicht alle Controller auf die gängigste Teilmenge von Funktionen und bietet eine konsistente Schnittstelle zwischen ihnen, einschließlich der Möglichkeit, das Tastenlayout zu erkennen.

Diese Abbildung zeigt, wie ein gängiger Controller unter Android für einen Android-Spieleentwickler aussehen kann.

Generischer Gamecontroller mit beschrifteten Eingaben, einschließlich Steuerkreuz, Analogsticks und Tasten
Abbildung 1 Profil für einen generischen Gamecontroller

In der Tabelle sind die Standardereignisnamen und -typen für Gamecontroller aufgeführt. Eine vollständige Liste der Ereignisse finden Sie unter Häufige Varianten. Das System sendet MotionEvent Ereignisse über onGenericMotionEvent und KeyEvent Ereignisse über onKeyDown und onKeyUp.

Controllereingabe KeyEvent MotionEvent
1. D-Pad
AXIS_HAT_X
(horizontale Eingabe)
AXIS_HAT_Y
(vertikale Eingabe)
2. Linker Analog-Stick
KEYCODE_BUTTON_THUMBL
(wenn gedrückt)
AXIS_X
(horizontale Bewegung)
AXIS_Y
(vertikale Bewegung)
3. Rechter Analog-Stick
KEYCODE_BUTTON_THUMBR
(wenn gedrückt)
AXIS_Z
(horizontale Bewegung)
AXIS_RZ
(vertikale Bewegung)
4. X-Taste KEYCODE_BUTTON_X
5. A-Taste KEYCODE_BUTTON_A
6. Y-Taste KEYCODE_BUTTON_Y
7. B-Taste KEYCODE_BUTTON_B
8. Rechter Bumper
KEYCODE_BUTTON_R1
9. Rechter Trigger
AXIS_RTRIGGER
10. Linker Trigger AXIS_LTRIGGER
11. Linker Bumper KEYCODE_BUTTON_L1
12. Starten KEYCODE_BUTTON_START
13. Auswählen KEYCODE_BUTTON_SELECT

Tastendrücke verarbeiten

Da Android das Drücken von Controller-Tasten genauso meldet wie das Drücken von Tastaturtasten, müssen Sie Folgendes tun:

  • Prüfen Sie, ob das Ereignis von einem SOURCE_GAMEPAD stammt.
  • Achten Sie darauf, dass Sie die Taste nur einmal mit KeyEvent.getRepeatCount(), empfangen. Android sendet wiederholte Tastendrücke genauso, als würden Sie eine Tastatur Taste gedrückt halten.
  • Geben Sie true zurück, um anzugeben, dass ein Ereignis verarbeitet wurde.
  • Übergeben Sie nicht verarbeitete Ereignisse an super, um zu prüfen, ob die verschiedenen Kompatibilitätsebenen von Android ordnungsgemäß funktionieren.

    Kotlin

    class GameView : View {
    // ...
    override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
        event.apply {
            var handled = false
    
            // make sure we're handling gamepad events
            if (isFromSource(SOURCE_GAMEPAD)) {
    
                // avoid processing the keycode repeatedly
                if (repeatCount == 0) {
                    when (keyCode) {
                        // handle the "A" button
                        KEYCODE_BUTTON_A -> {
                          handled = true
                        }
                    }
                    // ...
                }
            }
            if (handled) {
                return true
            }
       }
       return super.onKeyDown(keyCode, event)
      }
    }
    

    Java

    public class GameView extends View {
    // ...
    
    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        boolean handled = false;
        // make sure we're handling gamepad events
        if (event.isFromSource(SOURCE_GAMEPAD)) {
            // avoid processing the keycode repeatedly
            if (event.getRepeatCount() == 0) {
                switch (keyCode) {
                    case KEYCODE_BUTTON_A:
                        // handle the "A" button
                        handled = true;
                        break;
                    // ...
                }
            }
            // mark this event as handled
            if (handled) {
                return true;
            }
        }
        // Always do this instead of "return false"
        // it allows Android's input compatibility layers to work
        return super.onKeyDown(keyCode, event);
      }
    }
    

Eingabe über das Steuerkreuz verarbeiten

Das 4-Wege-Steuerkreuz ist eine gängige physische Steuerung bei vielen Gamecontrollern. Android meldet das Drücken von „Nach oben“ und „Nach unten“ auf dem Steuerkreuz als AXIS_HAT_Y-Ereignisse, wobei -1,0 für „Nach oben“ und 1,0 für „Nach unten“ steht. Das Drücken von „Nach links“ oder „Nach rechts“ auf dem Steuerkreuz wird als AXIS_HAT_X-Ereignisse gemeldet, wobei -1,0 für „Nach links“ und 1,0 für „Nach rechts“ steht.

Einige Controller melden das Drücken des Steuerkreuzes stattdessen mit einem Schlüsselcode. Wenn Ihr Spiel das Drücken des Steuerkreuzes berücksichtigt, sollten Sie die Ereignisse der Hat-Achse und die Schlüsselcodes des Steuerkreuzes wie in Tabelle 2 empfohlen als dieselben Eingabeereignisse behandeln.

Tabelle 2 Empfohlene Standardspielaktionen für Schlüsselcodes des Steuerkreuzes und Werte der Hat-Achse

Spielaktion Schlüsselcode des Steuerkreuzes Code der Hat-Achse
Nach oben KEYCODE_DPAD_UP AXIS_HAT_Y (für Werte von 0 bis -1,0)
Nach unten KEYCODE_DPAD_DOWN AXIS_HAT_Y (für Werte von 0 bis 1,0)
Nach links KEYCODE_DPAD_LEFT AXIS_HAT_X (für Werte von 0 bis -1,0)
Nach rechts KEYCODE_DPAD_RIGHT AXIS_HAT_X (für Werte von 0 bis 1,0)

Das folgende Code-Snippet zeigt eine Hilfsklasse, mit der Sie die Werte der Hat-Achse und des Schlüsselcodes aus einem Eingabeereignis prüfen können, um die Richtung des Steuerkreuzes zu ermitteln.

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.
            return event.isFromSource(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.
        return event.isFromSource(InputDevice.SOURCE_DPAD);
     }
}

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

Beispiel:

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 Gamecontrollern bewegen, meldet Android ein MotionEvent, das den ACTION_MOVE Aktions code und die aktualisierten Positionen der Achsen des Joysticks enthält. Ihr Spiel kann anhand der Daten von MotionEvent ermitteln, ob eine Joystickbewegung stattgefunden hat, die für das Spiel relevant ist.

Beachten Sie, dass bei Joystick-Motion-Events mehrere Bewegungsbeispiele in einem einzigen Objekt zusammengefasst werden können. Das MotionEvent-Objekt enthält die aktuelle Position für jede Joystickachse sowie mehrere historische Positionen für jede Achse. Beim Melden von Bewegungsereignissen mit dem Aktionscode ACTION_MOVE (z. B. Joystickbewegungen) fasst Android die Achsenwerte aus Effizienzgründen zusammen. Die historischen Werte für eine Achse bestehen aus der Menge der eindeutigen Werte, die älter als der aktuelle Achsenwert und neuer als die in früheren Motion-Events gemeldeten Werte sind. Weitere Informationen finden Sie in der MotionEvent Referenz.

Um die Bewegung eines Spielobjekts basierend auf der Joystickeingabe genau zu rendern, können Sie die historischen Informationen verwenden, die von MotionEvent-Objekten bereitgestellt werden.

Sie können aktuelle und historische Werte mit den folgenden Methoden abrufen:

Das folgende Snippet zeigt, wie Sie den onGenericMotionEvent() Callback überschreiben können, um Joystickeingaben 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 Joystickeingaben verwenden, müssen Sie prüfen, ob der Joystick zentriert ist, und dann die Achsenbewegungen entsprechend berechnen. Joysticks haben in der Regel einen flachen Bereich, d. h. einen Wertebereich in der Nähe der Koordinate (0, 0), bei der die Achse als zentriert gilt. Wenn der von Android gemeldete Achsenwert in den flachen Bereich fällt, sollten Sie den Controller als im Ruhezustand befindlich behandeln (d. h. bewegungslos entlang beider Achsen).

Das Snippet zeigt eine Hilfsmethode, mit der die Bewegung entlang jeder Achse berechnet wird. Sie rufen diese Hilfsmethode in der Methode processJoystickInput() auf, die im folgenden Beispiel beschrieben wird:

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

So können Sie Joystickbewegungen in Ihrem Spiel 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
}

Wenn Sie Gamecontroller mit anspruchsvolleren Funktionen als nur einem Joystick unterstützen möchten, sollten Sie diese Best Practices befolgen:

  • Zwei Controller-Sticks verarbeiten Viele Gamecontroller haben sowohl einen linken als auch 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. Achten Sie darauf, dass Sie beide Controller-Sticks in Ihrem Code verarbeiten.
  • Drücken der Schultertrigger verarbeiten und sicherstellen, dass Ihr Spiel mit AXIS_ und KEYCODE_BUTTON_-Ereignissen funktioniert. Einige Controller haben linke und rechte Schultertrigger. Wenn diese Trigger vorhanden sind, geben sie ein AXIS_*TRIGGER- oder KEYCODE_BUTTON_*2-Ereignis oder beides aus. Für den linken Trigger wären das AXIS_LTRIGGER und KEYCODE_BUTTON_L2. Für den rechten Trigger wären das AXIS_RTRIGGER und KEYCODE_BUTTON_R2. Achsenereignisse treten nur auf, wenn der Trigger einen Wertebereich zwischen 0 und 1 ausgibt. Einige Controller mit analoger Ausgabe geben zusätzlich zu Achsenereignissen auch Tastenereignisse aus. Spiele müssen sowohl AXIS_- als auch KEYCODE_BUTTON_-Ereignisse unterstützen, um mit allen gängigen Gamecontrollern kompatibel zu bleiben. Wenn ein Controller beide meldet, sollten Sie jedoch das Ereignis bevorzugen, das für Ihr Gameplay am sinnvollsten ist. Unter Android 4.3 (API-Ebene 18) und höher meldet ein Controller, der ein AXIS_LTRIGGER auch einen identischen Wert für die AXIS_BRAKE Achse. Dasselbe gilt für AXIS_RTRIGGER und AXIS_GAS. Android meldet alle analogen Triggerdrücke mit einem normalisierten Wert von 0,0 (losgelassen) bis 1,0 (vollständig gedrückt).
  • Bestimmte Verhaltensweisen und Unterstützung können in emulierten Umgebungen unterschiedlich sein. Emulierte Plattformen wie Google Play Games, können sich je nach den Funktionen des Host betriebssystems leicht unterscheiden. Einige Controller, die sowohl AXIS_- als auch KEYCODE_BUTTON_-Ereignisse ausgeben, geben beispielsweise nur AXIS_-Ereignisse aus. Die Unterstützung für einige Controller ist möglicherweise gar nicht vorhanden.

Häufige Varianten

Angesichts der breiten Unterstützung von Controllern unter Android ist es möglicherweise unklar, wie Sie Ihr Spiel erstellen und testen können, um sicherzustellen, dass es bei Ihren Spielern fehlerfrei funktioniert. Trotz dieser scheinbaren Vielfalt halten sich Controllerhersteller auf der ganzen Welt in der Regel an drei verschiedene Controller-Stile. Einige bieten Hardware-Umschalter zwischen zwei oder mehr dieser Stile.

Das bedeutet, dass Sie mit nur drei Controllern in Ihrem Entwicklerteam testen können und sicher sein können, dass Ihr Spiel spielbar ist, ohne auf Zulassungs- und Sperrlisten zurückgreifen zu müssen.

Häufige Controllertypen

Die gängigsten Controller-Stile ahmen in der Regel die Layouts beliebter Spielkonsolen nach. Das gilt sowohl für die Beschriftung und das Layout der Tasten als auch für die ausgelösten Ereignisse. Bei Controllern mit Hardware-Umschaltern zwischen verschiedenen Konsolentypen ändern sich die gesendeten Ereignisse und oft sogar das logische Tastenlayout.

Beim Testen empfehlen wir, dass Sie prüfen, ob Ihr Spiel mit einem Controller in jeder der Kategorien funktioniert. Sie können mit Controllern von Erstanbietern oder beliebten Drittanbietern testen. Im Allgemeinen ordnen wir die beliebtesten Controller nach bestem Wissen und Gewissen der obigen Definition zu.

Controllertyp Verhaltensunterschiede Beschriftungsvariationen
Controller im Xbox-Stil

Diese Controller werden in der Regel für die Microsoft Xbox- und Windows* Plattform entwickelt.

Diese Controller entsprechen dem Feature-Set, das unter Controllereingaben verarbeiten beschrieben ist. Die Tasten L2/R2 dieser Controller sind mit LT/RT beschriftet.
Controller im Switch-Stil

Diese Controller sind in der Regel für die Nintendo Switch*-Konsolenfamilie konzipiert.

Diese Controller senden die KeyEvent KEYCODE_BUTTON_R2 KEYCODE_BUTTON_L2 MotionEvents Die Tasten L2/R2 dieser Controller sind mit ZL/ZR beschriftet.

Bei diesen Controllern werden auch die Tasten A und B sowie die Tasten X und Y vertauscht. KEYCODE_BUTTON_A ist also die Taste mit der Beschriftung B und umgekehrt.

Controller im PlayStation-Stil

Diese Controller sind in der Regel für die Sony PlayStation* Familie von Konsolen konzipiert.

Diese Controller senden MotionEvents wie die Controller im Xbox-Stil, aber auch KeyEvents wie die Controller im Switch-Stil, wenn sie vollständig gedrückt werden. Diese Controller verwenden eine andere Reihe von Symbolen für die Tasten an der Oberseite.

* Microsoft, Xbox und Windows sind eingetragene Marken von Microsoft. Nintendo Switch ist eine eingetragene Marke von Nintendo of America Inc. PlayStation ist eine eingetragene Marke von Sony Interactive Entertainment Inc.

Trigger-Tasten unterscheiden

Einige Controller senden AXIS_LTRIGGER und AXIS_RTRIGGER, andere KEYCODE_BUTTON_L2 und KEYCODE_BUTTON_R2 und wieder andere senden alle diese Ereignisse basierend auf ihren Hardwarefunktionen. Maximieren Sie die Kompatibilität, indem Sie alle diese Ereignisse unterstützen.

Alle Controller, die AXIS_LTRIGGER senden, senden auch AXIS_BRAKE. Ähnliches gilt für AXIS_RTRIGGER und AXIS_GAS. So wird die Kompatibilität zwischen Rennlenkrädern und typischen Gamecontrollern maximiert. Im Allgemeinen verursacht das keine Probleme, aber bei Funktionen wie Bildschirmen zur Tastenbelegung sollten Sie darauf achten.

Trigger MotionEvent KeyEvent
Linker Trigger AXIS_LTRIGGER
AXIS_BRAKE
KEYCODE_BUTTON_L2
Rechter Trigger AXIS_RTRIGGER
AXIS_GAS
KEYCODE_BUTTON_R2

Achten Sie darauf, dass Ihr Spiel sowohl KeyEvent als auch MotionEvent verarbeiten kann, um die Kompatibilität mit möglichst vielen Controllern aufrechtzuerhalten, und dass Ereignisse dedupliziert werden.

Unterstützte Controller

Beim Testen empfehlen wir, dass Sie prüfen, ob Ihr Spiel mit einem Controller in jeder der Kategorien funktioniert.

  • Xbox-Stil
  • Nintendo Switch-Stil
  • PlayStation-Stil

Sie können mit Controllern von Erstanbietern oder beliebten Drittanbietern testen. Im Allgemeinen ordnen wir die beliebtesten Controller so genau wie möglich der Definition zu.