Controlleraktionen verarbeiten

Controller haben zwei Arten von Aktionen:

  • KeyEvent für alle Schaltflächen mit dem binären Status „Ein“ und „Aus“
  • MotionEvent wird für alle Achsen verwendet, die einen Wertebereich zurückgeben. Beispiele: -1 bis 1 für Analogsticks oder 0 bis 1 für analoge Trigger.

Sie können diese Eingaben aus dem View mit focus lesen.

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 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. Eine Touchscreen-Aktion generiert beispielsweise ein AXIS_X-Ereignis, das die X-Koordinate der Berührungsfläche darstellt, während ein Gamepad ein AXIS_X-Ereignis generiert, das die X-Position des linken Sticks darstellt. Sie müssen also den Quelltyp prüfen, um Eingabeereignisse richtig zu interpretieren.

Mit der Funktion supportsSource(int) können Sie prüfen, ob ein verbundenes InputDevice ein Controller ist:

  • Der Quelltyp SOURCE_GAMEPAD gibt an, dass das Eingabegerät Controller-Tasten hat (z. B. KEYCODE_BUTTON_A). Dieser Quelltyp gibt nicht unbedingt an, ob der Gamecontroller D-Pad-Tasten hat, obwohl die meisten Controller in der Regel über Richtungstasten verfügen.
  • Ein Quelltyp von SOURCE_DPAD gibt an, dass das Eingabegerät über D-Pad-Tasten verfügt (z. B. DPAD_UP).
  • Der Quelltyp SOURCE_JOYSTICK gibt an, dass das Eingabegerät analoge Steuerknüppel hat (z. B. ein 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. Wenn ja, ruft die Methode die Geräte-IDs für die Gamecontroller ab. Sie können dann 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 (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 von Controllern beschrieben, die auf Android unterstützt werden.

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

Diese Abbildung zeigt, wie ein typischer Controller auf Android aussehen kann.

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

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. Steuerkreuz
AXIS_HAT_X
(horizontal input)
AXIS_HAT_Y
(vertical input)
2. Linker Analogstick
KEYCODE_BUTTON_THUMBL
(wenn eingedrückt)
AXIS_X
(horizontale Bewegung)
AXIS_Y
(vertikale Bewegung)
3. Rechter Analogstick
KEYCODE_BUTTON_THUMBR
(wenn eingedrü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 Controller-Tastendrücke identisch mit Tastatur-Tastendrücken meldet, müssen Sie Folgendes tun:

  • Prüfen Sie, ob das Ereignis von einem SOURCE_GAMEPAD stammt.
  • Achte darauf, dass du die Schaltfläche nur einmal empfängst, da Android sonst wiederholte Tastaturereignisse sendet, als ob du eine Tastaturtaste gedrückt gehalten hättest.KeyEvent.getRepeatCount()
  • 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 ein gängiges physisches Steuerelement auf vielen Gamecontrollern. Android meldet das Drücken der Richtungstasten AUF und AB als AXIS_HAT_Y-Ereignisse, wobei -1,0 für AUF und 1,0 für AB steht. Es werden D-Pad-DRÜCKEN NACH LINKS oder RECHTS als AXIS_HAT_X-Ereignisse gemeldet. Dabei steht -1,0 für links und 1,0 für rechts.

Einige Controller melden D-Pad-Eingaben stattdessen mit einem Tastencode. Wenn Ihr Spiel D-Pad-Eingaben berücksichtigt, sollten Sie die Hat-Achsen-Ereignisse und die D-Pad-Schlüsselcodes als dieselben Eingabeereignisse behandeln, wie in Tabelle 2 empfohlen.

Tabelle 2 Empfohlene Standardspielaktionen für D-Pad-Schlüsselcodes und Hat-Achsenwerte.

Actionspiele Steuerkreuz-Tastencode Hat Axis Code
Nach oben KEYCODE_DPAD_UP AXIS_HAT_Y (für Werte zwischen 0 und –1,0)
Nach unten KEYCODE_DPAD_DOWN AXIS_HAT_Y (für Werte zwischen 0 und 1,0)
Nach links bewegen KEYCODE_DPAD_LEFT AXIS_HAT_X (für Werte zwischen 0 und –1,0)
Nach rechts bewegen KEYCODE_DPAD_RIGHT AXIS_HAT_X (für Werte zwischen 0 und 1,0)

Das folgende Code-Snippet zeigt eine Hilfsklasse, mit der Sie die Werte für die Hutachse und den Tastencode eines Eingabeereignisses 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 D-Pad-Eingaben verarbeiten möchten, z. B. in den Callbacks onGenericMotionEvent() oder onKeyDown().

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

Joystick-Bewegungen verarbeiten

Wenn Spieler einen Joystick auf ihren Gamecontrollern bewegen, meldet Android ein MotionEvent, das den Aktionscode ACTION_MOVE und die aktualisierten Positionen der Joystick-Achsen enthält. Ihr Spiel kann die von MotionEvent bereitgestellten Daten verwenden, um festzustellen, ob eine für das Spiel relevante Joystick-Bewegung stattgefunden hat.

Beachten Sie, dass bei Joystick-Bewegungsereignissen mehrere Bewegungsbeispiele in einem einzigen Objekt zusammengefasst werden können. Das MotionEvent-Objekt enthält die aktuelle Position für jede Joystickachse sowie mehrere Verlaufs-Positionen für jede Achse. Wenn Sie Bewegungsereignisse mit dem Aktionscode ACTION_MOVE (z. B. Joystick-Bewegungen) melden, werden die Achsenwerte von Android aus Effizienzgründen zusammengefasst. Die bisherigen Werte für eine Achse bestehen aus der Menge der unterschiedlichen Werte, die älter als der aktuelle Achsenwert und neuer als die Werte sind, die in früheren Bewegungsereignissen gemeldet wurden. Weitere Informationen finden Sie in der Referenz zu MotionEvent.

Um die Bewegung eines Spielobjekts basierend auf der Joystick-Eingabe genau zu rendern, können Sie die Verlaufsdaten 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 Joystick-Eingaben zu verarbeiten. Verarbeiten Sie zuerst die historischen Werte für eine Achse und dann ihre aktuelle Position.

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, müssen Sie feststellen, ob der Joystick zentriert ist, und die Achsenbewegungen entsprechend berechnen. Joysticks haben in der Regel einen flachen Bereich, d. h. einen Wertebereich in der Nähe der Koordinate (0, 0), in dem die Achse als zentriert gilt. Wenn der von Android gemeldete Achsenwert in den flachen Bereich fällt, sollten Sie den Controller als in Ruhe befindlich betrachten (d. h. bewegungslos entlang beider Achsen).

Das Snippet zeigt eine Hilfsmethode, mit der die Bewegung entlang der einzelnen Achsen berechnet wird. Sie rufen diesen Helfer 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 Joystick-Bewegungen 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 du Gamecontroller mit anspruchsvolleren Funktionen als nur einem Joystick unterstützen möchtest, solltest du die folgenden Best Practices beachten:

  • Mit zwei Controller-Sticks umgehen Viele Controller 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 berücksichtigen.
  • Drücken der Schultertasten verarbeiten (und dafür sorgen, dass Ihr Spiel mit AXIS_- und KEYCODE_BUTTON_-Ereignissen funktioniert): Einige Controller haben linke und rechte Schultertasten. Wenn diese Trigger vorhanden sind, wird ein AXIS_*TRIGGER- oder KEYCODE_BUTTON_*2-Ereignis oder beides ausgegeben. 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 Schaltflächenereignisse aus. Spiele müssen sowohl AXIS_- als auch KEYCODE_BUTTON_-Ereignisse unterstützen, um mit allen gängigen Controllern kompatibel zu sein. Wenn ein Controller beides meldet, sollte das Ereignis bevorzugt werden, das für das Gameplay am sinnvollsten ist. Unter Android 4.3 (API-Ebene 18) und höher meldet ein Controller, der 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 analogen Trigger-Betätigungen mit einem normalisierten Wert von 0,0 (losgelassen) bis 1,0 (vollständig gedrückt).
  • Bestimmte Verhaltensweisen und die Unterstützung können in emulierten Umgebungen abweichen. Emulierte Plattformen wie Google Play Games können je nach den Funktionen des Hostbetriebssystems geringfügig unterschiedlich reagieren. Einige Controller, die sowohl AXIS_- als auch KEYCODE_BUTTON_-Ereignisse ausgeben, geben beispielsweise nur AXIS_-Ereignisse aus. Außerdem wird die Unterstützung für einige Controller möglicherweise vollständig entfernt.

Häufige Varianten

Da Android eine Vielzahl von Controllern unterstützt, ist es möglicherweise unklar, wie Sie Ihr Spiel so entwickeln und testen können, dass es bei allen Spielern fehlerfrei funktioniert. Trotz dieser scheinbaren Vielfalt halten sich Controller-Hersteller auf der ganzen Welt in der Regel an drei verschiedene Controller-Stile. Bei einigen Geräten können Sie zwischen zwei oder mehr dieser Optionen umschalten.

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

Gängige Controller-Typen

Die meisten Controller ähneln dem Layout beliebter Spielekonsolen. Das betrifft sowohl die Ästhetik der Schaltflächenlabels und des Layouts als auch die Funktionalität in Bezug auf die ausgelösten Ereignisse. Bei Controllern mit Hardware-Schaltern zwischen verschiedenen Konsolentypen ändern sich die gesendeten Ereignisse und oft auch das logische Tastenlayout.

Beim Testen empfehlen wir, dass du prüfst, ob dein Spiel mit einem Controller aus jeder Kategorie funktioniert. Sie können mit Controllern von Erst- oder beliebten Drittanbietern testen. Im Allgemeinen ordnen wir die beliebtesten Controller nach bestem Wissen und Gewissen der oben stehenden Definition zu.

Controller-Typ Verhaltensunterschiede Varianten von Labels
Controller im Xbox-Stil

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

Diese Controller entsprechen den Funktionen, die unter Controller-Eingaben verarbeiten beschrieben werden. Die L2-/R2-Tasten auf diesen Controllern 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 L2-/R2-Tasten auf diesen Controllern sind mit ZL/ZR beschriftet.

Bei diesen Controllern sind 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*-Konsolen konzipiert.

Diese Controller senden MotionEvents wie die Xbox-Controller, aber auch KeyEvents wie die Switch-Controller, wenn sie vollständig gedrückt werden. Diese Controller verwenden andere Glyphen für die Buchstabentasten.

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

Triggertasten unterscheiden

Einige Controller senden AXIS_LTRIGGER und AXIS_RTRIGGER, andere KEYCODE_BUTTON_L2 und KEYCODE_BUTTON_R2 und wieder andere alle diese Ereignisse, je nach Hardware. Maximieren Sie die Kompatibilität, indem Sie alle diese Ereignisse unterstützen.

Alle Controller, die AXIS_LTRIGGER senden, senden auch AXIS_BRAKE. Das gilt auch für AXIS_RTRIGGER und AXIS_GAS. So wird die Kompatibilität zwischen Rennlenkrädern und typischen Controllern maximiert. Im Allgemeinen führt dies nicht zu Problemen, aber bei Funktionen wie Bildschirmen für die Neubelegung von Tasten ist Vorsicht geboten.

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

Achte darauf, dass dein Spiel sowohl KeyEvent als auch MotionEvent verarbeiten kann, um die Kompatibilität mit möglichst vielen Controllern zu gewährleisten, und dass Ereignisse dedupliziert werden.

Unterstützte Controller

Beim Testen empfehlen wir, dass du prüfst, ob dein Spiel mit einem Controller aus jeder Kategorie funktioniert.

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

Sie können mit Controllern von Erst- oder beliebten Drittanbietern testen. Wir ordnen die beliebtesten Controller in der Regel so genau wie möglich der Definition zu.