Kumanda işlemlerini gerçekleştirme

Kontrol cihazlarında iki tür işlem vardır:

  • KeyEvent "Açık" ve "kapalı" ikili durumuna sahip tüm düğmeler için kullanılır.
  • MotionEvent Bir değer aralığı döndüren tüm eksenler için kullanılır. Örneğin, analog çubuklar için -1 ile 1 arasında veya analog tetikler için 0 ile 1 arasında.

Bu girişleri View bölümünden okuyabilirsiniz. focus.

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

Gerekirse etkinlikleri doğrudan Activity kaynağından okuyabilirsiniz.

Oyun kumandasının bağlı olduğunu doğrulayın.

Android, giriş etkinliklerini bildirirken farklı giriş cihazı türleri için aynı tuş veya eksen kimliklerini yeniden kullanır. Örneğin, dokunmatik ekran işlemi, dokunma yüzeyinin X koordinatını temsil eden bir AXIS_X etkinliği oluşturur ancak gamepad, sol çubuğun X konumunu temsil eden bir AXIS_X etkinliği oluşturur. Bu nedenle, giriş etkinliklerini doğru şekilde yorumlamak için kaynak türünü kontrol etmeniz gerekir.

Bağlı bir InputDevice cihazın oyun kumandası olduğunu doğrulamak için supportsSource(int) işlevini kullanın:

  • SOURCE_GAMEPAD kaynak türü, giriş cihazında kontrol düğmeleri (örneğin, KEYCODE_BUTTON_A) olduğunu gösterir. Bu kaynak türünün, oyun kumandasında yön tuşları olup olmadığını kesin olarak belirtmediğini unutmayın. Bununla birlikte, çoğu kumandada genellikle yön kontrolleri bulunur.
  • SOURCE_DPAD kaynak türü, giriş cihazında D-pad düğmeleri (ör. DPAD_UP) olduğunu gösterir.
  • SOURCE_JOYSTICK kaynak türü, giriş cihazının analog kontrol çubuklarına (örneğin, AXIS_X ve AXIS_Y boyunca hareketleri kaydeden bir joystick) sahip olduğunu gösterir.

Aşağıdaki kod snippet'inde, bağlı giriş cihazlarının oyun kumandası olup olmadığını kontrol etmenizi sağlayan bir yardımcı yöntem gösterilmektedir. Bu durumda yöntem, oyun kumandalarının cihaz kimliklerini alır. Ardından her cihaz kimliğini oyununuzdaki bir oyuncuyla ilişkilendirebilir ve bağlı her oyuncu için oyun işlemlerini ayrı ayrı işleyebilirsiniz. Aynı Android destekli cihaza aynı anda bağlanan birden fazla oyun denetleyicisini destekleme hakkında daha fazla bilgi edinmek için Birden fazla oyun denetleyicisini destekleme başlıklı makaleyi inceleyin.

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

İşlem denetleyici girişleri

Bu bölümde, Android'de desteklenen oyun kumandası türleri açıklanmaktadır.

C++ geliştiricileri Game Controller Library'yi kullanmalıdır. Tüm denetleyicileri en yaygın özelliklerin alt kümesinde birleştirir ve düğme düzenini algılama özelliği de dahil olmak üzere aralarında tutarlı bir arayüz sağlar.

Bu şekil, Android oyun geliştiricilerin Android'de yaygın bir kontrol cihazının nasıl görünmesini bekleyebileceğini gösterir.

D-pad, analog çubuklar ve düğmeler dahil olmak üzere etiketli girişlere sahip genel oyun kumandası
Şekil 1. Genel bir oyun kumandası profili.

Tabloda, oyun kumandaları için standart etkinlik adları ve türleri listelenmektedir. Etkinliklerin tam listesi için Sık kullanılan varyantlar başlıklı makaleyi inceleyin. Sistem, MotionEvent etkinliklerini onGenericMotionEvent ve KeyEvent etkinliklerini onKeyDown ve onKeyUp üzerinden gönderir.

Kumanda Girişi KeyEvent MotionEvent
1. D-Pad
AXIS_HAT_X
(yatay giriş)
AXIS_HAT_Y
(dikey giriş)
2. Sol Analog Çubuk
KEYCODE_BUTTON_THUMBL
(içe bastırıldığında)
AXIS_X
(yatay hareket)
AXIS_Y
(dikey hareket)
3. Sağ Analog Çubuk
KEYCODE_BUTTON_THUMBR
(içe bastırıldığında)
AXIS_Z
(yatay hareket)
AXIS_RZ
(dikey hareket)
4. X Düğmesi KEYCODE_BUTTON_X
5. A düğmesi KEYCODE_BUTTON_A
6. Y Düğmesi KEYCODE_BUTTON_Y
7. B Düğmesi KEYCODE_BUTTON_B
8. Sağ Bumper
KEYCODE_BUTTON_R1
9. Sağ tetikleyici
AXIS_RTRIGGER
10. Sol tetikleyici AXIS_LTRIGGER
11. Sol Bumper KEYCODE_BUTTON_L1
12. Başlat KEYCODE_BUTTON_START
13. Seç KEYCODE_BUTTON_SELECT

Düğmeye basma işlemlerini işleme

Android, kumanda düğmesine basma işlemlerini klavye düğmesine basma işlemleriyle aynı şekilde bildirdiğinden:

  • Etkinliğin bir SOURCE_GAMEPAD tarafından gönderildiğini doğrulayın.
  • KeyEvent.getRepeatCount() ile düğmeyi yalnızca bir kez aldığınızdan emin olun. Android, klavye tuşunu basılı tuttuğunuzda olduğu gibi tekrarlanan tuş etkinlikleri gönderir.
  • Bir etkinliğin işlendiğini belirtmek için true değerini döndürün.
  • Android'in çeşitli uyumluluk katmanlarının düzgün çalıştığını doğrulamak için işlenmemiş etkinlikleri super'ye iletin.

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

Yön tuşu girişini işleme

4 yönlü yön tuşu veya D-pad, birçok oyun kumandasında yaygın olarak kullanılan fiziksel bir kontrol mekanizmasıdır. Android, D-pad'in YUKARI ve AŞAĞI tuşlarına basma işlemlerini AXIS_HAT_Y etkinlikleri olarak bildirir. -1,0 yukarı, 1,0 ise aşağı yönü gösterir. D-pad'in SOL veya SAĞ tuşuna basma işlemlerini AXIS_HAT_X etkinlikleri olarak bildirir. -1,0 değeri sol, 1,0 değeri ise sağ tuşa basıldığını gösterir.

Bazı kontrol cihazları ise D-pad tuşlarına basıldığında tuş koduyla bildirir. Oyununuzda yön tuşuna basma işlemleri önemliyse 2. tabloda önerildiği gibi şapka ekseni etkinliklerini ve yön tuşu kodlarını aynı giriş etkinlikleri olarak değerlendirmeniz gerekir.

Tablo 2. D-pad tuş kodları ve şapka ekseni değerleri için önerilen varsayılan oyun işlemleri.

Oyun İşlemi D-pad Tuş Kodu Hat Axis Code
Yukarı Taşı KEYCODE_DPAD_UP AXIS_HAT_Y (0 ile -1,0 arasındaki değerler için)
Aşağı Taşı KEYCODE_DPAD_DOWN AXIS_HAT_Y (0 ile 1,0 arasındaki değerler için)
Sola Taşı KEYCODE_DPAD_LEFT AXIS_HAT_X (0 ile -1,0 arasındaki değerler için)
Sağa Taşı KEYCODE_DPAD_RIGHT AXIS_HAT_X (0 ile 1,0 arasındaki değerler için)

Aşağıdaki kod snippet'inde, D-pad yönünü belirlemek için giriş etkinliğindeki şapka ekseni ve tuş kodu değerlerini kontrol etmenize olanak tanıyan bir yardımcı sınıf gösterilmektedir.

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

Bu yardımcı sınıfı, D-pad girişini işlemek istediğiniz her yerde (ör. onGenericMotionEvent() veya onKeyDown() geri çağırmalarında) oyununuzda kullanabilirsiniz.

Örneğin:

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 hareketlerini işleme

Oyuncular oyun kumandalarındaki bir kontrol çubuğunu hareket ettirdiğinde Android, MotionEvent içeren bir ACTION_MOVE işlemi bildirir. Bu işlem, kontrol çubuğunun eksenlerinin güncellenmiş konumlarını içerir. Oyununuz, MotionEvent tarafından sağlanan verileri kullanarak ilgilendiği bir joystick hareketinin gerçekleşip gerçekleşmediğini belirleyebilir.

Joystick hareket etkinliklerinin, tek bir nesne içinde birden fazla hareket örneğini birlikte gruplandırabileceğini unutmayın. MotionEvent nesnesi, her bir kontrol çubuğu ekseninin mevcut konumunun yanı sıra her eksenin birden fazla geçmiş konumunu içerir. Hareket etkinlikleri, ACTION_MOVE işlem koduyla (ör. joystick hareketleri) raporlandığında Android, verimlilik için eksen değerlerini toplu olarak işler. Bir eksenin geçmiş değerleri, geçerli eksen değerinden eski ve önceki hareket etkinliklerinde bildirilen değerlerden daha yeni olan farklı değerler kümesinden oluşur. Ayrıntılar için MotionEvent referansına bakın.

Bir oyun nesnesinin hareketini, kontrol çubuğu girişine göre doğru şekilde oluşturmak için MotionEvent nesneleri tarafından sağlanan geçmiş bilgilerini kullanabilirsiniz.

Aşağıdaki yöntemleri kullanarak mevcut ve geçmiş değerleri alabilirsiniz:

Aşağıdaki snippet'te, onGenericMotionEvent() geri çağırma işlevini geçersiz kılarak joystick girişini nasıl işleyebileceğiniz gösterilmektedir. Öncelikle bir eksenin geçmiş değerlerini, ardından mevcut konumunu işlemelisiniz.

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

Çubuk girişini kullanmadan önce çubuğun ortalanıp ortalanmadığını belirlemeniz ve ardından eksen hareketlerini buna göre hesaplamanız gerekir. Joystick'lerde genellikle düz bir alan bulunur. Bu alan, eksenin ortalanmış olarak kabul edildiği (0,0) koordinatına yakın bir değer aralığıdır. Android tarafından bildirilen eksen değeri düz alanın içindeyse kontrol cihazını dinlenme modunda (yani her iki eksen boyunca hareketsiz) kabul etmeniz gerekir.

Snippet'te, her eksen boyunca hareketi hesaplayan bir yardımcı yöntem gösterilmektedir. Bu yardımcıyı, aşağıdaki örnekte daha ayrıntılı olarak açıklanan processJoystickInput() yönteminde çağırırsınız:

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

Tüm bunları bir araya getirdiğimizde, oyununuzda joystick hareketlerini şu şekilde işleyebilirsiniz:

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
}

Tek bir kontrol çubuğunun ötesinde daha gelişmiş özelliklere sahip oyun kumandalarını desteklemek için aşağıdaki en iyi uygulamaları izleyin:

  • Çift kontrol çubuğunu kullanma Birçok oyun kumandasında hem sol hem de sağ analog çubuk bulunur. Android, sol çubuk için yatay hareketleri AXIS_X etkinlikleri, dikey hareketleri ise AXIS_Y etkinlikleri olarak bildirir. Android, sağ çubuk için yatay hareketleri AXIS_Z etkinlikleri, dikey hareketleri ise AXIS_RZ etkinlikleri olarak bildirir. Kodunuzda her iki kontrol çubuğunu da kullandığınızdan emin olun.
  • Omuz tetikleyicisine basma işlemlerini yönetin (ve oyununuzun AXIS_ ve KEYCODE_BUTTON_ etkinlikleriyle çalıştığından emin olun). Bazı kumandalarda sol ve sağ omuz tetikleri bulunur. Bu tetikleyiciler mevcut olduğunda AXIS_*TRIGGER veya KEYCODE_BUTTON_*2 etkinliği ya da her ikisi de yayınlanır. Sol tetik için bu, AXIS_LTRIGGER ve KEYCODE_BUTTON_L2 olur. Doğru tetikleyici için bu değerler AXIS_RTRIGGER ve KEYCODE_BUTTON_R2 olacaktır. Eksen etkinlikleri yalnızca tetikleyici 0 ile 1 arasında bir değer aralığı yayarsa gerçekleşir. Analog çıkışlı bazı kontrol cihazları, eksen etkinliklerine ek olarak düğme etkinlikleri de yayar. Oyunların, tüm yaygın oyun kumandalarıyla uyumlu kalması için hem AXIS_ hem de KEYCODE_BUTTON_ etkinliklerini desteklemesi gerekir. Ancak bir kumanda her ikisini de bildiriyorsa oyun tarzınız için en mantıklı olan etkinliği tercih edin. Android 4.3 (API düzeyi 18) ve sonraki sürümlerde, AXIS_LTRIGGER üreten bir kontrol cihazı, AXIS_BRAKE ekseni için de aynı değeri bildirir. Aynı durum AXIS_RTRIGGER ve AXIS_GAS için de geçerlidir. Android, tüm analog tetikleyici basmalarını 0,0 (serbest bırakılmış) ile 1,0 (tamamen basılmış) arasında normalleştirilmiş bir değerle bildirir.
  • Belirli davranışlar ve destek, emüle edilmiş ortamlarda farklılık gösterebilir. Google Play Games gibi emüle edilmiş platformların davranışları, ana makine işletim sisteminin özelliklerine bağlı olarak biraz farklılık gösterebilir. Örneğin, hem AXIS_ hem de KEYCODE_BUTTON_ etkinlikleri yayan bazı kontrol cihazları yalnızca AXIS_ etkinlikleri yayar ve bazı kontrol cihazları için destek tamamen eksik olabilir.

Sık kullanılan varyantlar

Android'in oyun kumandaları için sunduğu geniş destek yelpazesi nedeniyle, oyununuzun oyuncu tabanınız arasında hatasız çalıştığını doğrulamak için nasıl geliştirme ve test yapacağınız net olmayabilir. Bu çeşitliliğe rağmen, dünya genelindeki denetleyici üreticilerinin genellikle üç farklı denetleyici tarzına bağlı kaldığını görüyoruz. Bazıları, bu özelliklerden ikisi veya daha fazlası arasında donanım geçişleri sağlar.

Bu sayede, geliştirme ekibinizdeki üç kontrol cihazıyla test yapabilir ve izin verilenler/reddedilenler listelerine başvurmadan oyununuzun oynanabilir olduğundan emin olabilirsiniz.

Sık Kullanılan Kumanda Türleri

En yaygın kumanda stili, popüler oyun konsollarının düzenlerini taklit etme eğilimindedir. Bu, hem düğme etiketleri ve düzen açısından estetik hem de hangi etkinliklerin tetiklendiği açısından işlevseldir. Farklı konsol türleri arasında donanım geçişleri olan kontrol cihazları, gönderdikleri etkinlikleri ve genellikle mantıksal düğme düzenlerini bile değiştirir.

Test sırasında, oyununuzun her kategorideki bir kumandayla çalıştığını doğrulamanızı öneririz. Birinci taraf kontrol cihazlarıyla veya popüler üçüncü taraf üreticilerle test etmeyi seçebilirsiniz. Genellikle en popüler kontrol cihazlarını yukarıdaki tanıma en iyi şekilde eşleriz.

Kumanda Türü Davranışsal Farklılıklar Etiketleme Varyasyonları
Xbox tarzı kumandalar

Bunlar genellikle Microsoft Xbox ve Windows* platformu için üretilen denetleyicilerdir.

Bu kontrol cihazları, İşlem kontrol cihazı girişleri bölümünde belirtilen özellik kümesiyle eşleşir. Bu kumandalardaki L2/R2 düğmeleri LT/RT olarak etiketlenir.
Switch Style Controllers

Bu kumandalar genellikle Nintendo Switch* konsol ailesi için tasarlanır.

Bu denetleyiciler KeyEvent KEYCODE_BUTTON_R2 KEYCODE_BUTTON_L2 MotionEvent gönderir. Bu kumandalardaki L2/R2 düğmeleri ZL/ZR olarak etiketlenir.

Bu kumandalarda A ve B düğmeleri ile X ve Y düğmeleri de yer değiştirir. Bu nedenle KEYCODE_BUTTON_A, B olarak etiketlenmiş düğmedir ve bunun tersi de geçerlidir.

PlayStation Stili Kumandalar

Bu denetleyiciler genellikle Sony PlayStation* konsol ailesi için tasarlanır.

Bu denetleyiciler, Xbox tarzı denetleyiciler gibi MotionEventlar gönderir ancak tamamen basıldığında Switch tarzı denetleyiciler gibi KeyEventlar da gönderir. Bu kumandalar, aksiyon düğmeleri için farklı glifler kullanır.

* Microsoft, Xbox ve Windows, Microsoft'un tescilli ticari markalarıdır; Nintendo Switch, Nintendo of America Inc. şirketinin tescilli ticari markasıdır; PlayStation, Sony Interactive Entertainment Inc. şirketinin tescilli ticari markasıdır.

Açıklığa kavuşturma tetikleme düğmeleri

Bazı kontrol cihazları AXIS_LTRIGGER ve AXIS_RTRIGGER, bazıları KEYCODE_BUTTON_L2 ve KEYCODE_BUTTON_R2, bazıları ise donanım özelliklerine bağlı olarak bu etkinliklerin tümünü gönderir. Bu etkinliklerin tümünü destekleyerek uyumluluğu en üst düzeye çıkarın.

AXIS_LTRIGGER gönderen tüm denetleyiciler AXIS_BRAKE de gönderir. Benzer şekilde, yarış direksiyonları ile normal oyun denetleyicileri arasındaki uyumluluğu en üst düzeye çıkarmak için AXIS_RTRIGGER ve AXIS_GAS de gönderilir. Bu durum genellikle sorunlara neden olmaz ancak tuş yeniden eşleme ekranları gibi özelliklere dikkat edin.

Tetikleyici MotionEvent KeyEvent
Sol tetikleyici AXIS_LTRIGGER
AXIS_BRAKE
KEYCODE_BUTTON_L2
Sağ Tetikleyici AXIS_RTRIGGER
AXIS_GAS
KEYCODE_BUTTON_R2

Oyununuzun mümkün olduğunca çok sayıda kumandayla uyumluluğunu korumak için hem KeyEvent hem de MotionEvent işleyebildiğini ve etkinliklerin yinelenmediğini doğrulamanız gerekir.

Desteklenen kumandalar

Test sırasında, oyununuzun her kategorideki bir kumandayla çalıştığını doğrulamanızı öneririz.

  • Xbox Tarzı
  • Nintendo Switch Stili
  • PlayStation Stili

Birinci taraf kontrol cihazlarıyla veya popüler üçüncü taraf üreticilerle test yapabilirsiniz. Genellikle en popüler kontrol cihazlarını tanıma olabildiğince yakın bir şekilde eşleriz.