Kumanda işlemlerini gerçekleştirme

Android, sistem düzeyinde oyun kontrol cihazlarından gelen giriş etkinliği kodlarını Android tuş kodları ve eksen değerleri olarak bildirir. Oyununuzda bu kodları ve değerleri alıp belirli oyun içi işlemlere dönüştürebilirsiniz.

Oyuncular bir oyun kumandasını Android cihazlarına fiziksel olarak bağladığında veya kablosuz olarak eşlediğinde sistem, kumandayı giriş cihazı olarak otomatik olarak algılar ve giriş etkinliklerini bildirmeye başlar. Oyununuz, etkin Activity veya odaklanmış View'inizde aşağıdaki geri çağırma yöntemlerini uygulayarak bu giriş etkinliklerini alabilir (geri çağırma yöntemlerini Activity veya View için uygulamanız gerekir, her ikisi için de uygulamamanız gerekir):

Önerilen yaklaşım, etkinlikleri kullanıcının etkileşimde bulunduğu belirli View nesnesinden yakalamaktır. Alınan giriş etkinliğinin türü hakkında bilgi almak için geri çağırmaların sağladığı aşağıdaki nesneleri inceleyin:

KeyEvent
Yön tuşları (D-pad) ve oyun kumandası düğme etkinliklerini tanımlayan bir nesne. Önemli etkinliklere, tetiklenen belirli bir düğmeyi gösteren DPAD_DOWN veya BUTTON_A gibi bir anahtar kodu eşlik eder. Anahtar kodu, getKeyCode() yöntemini çağırarak veya onKeyDown() gibi önemli etkinlik geri çağırmalarından alabilirsiniz.
MotionEvent
Joystick ve omuz tetik hareketlerinden gelen girişi açıklayan bir nesne. Hareket etkinliklerine bir işlem kodu ve bir dizi eksen değerleri eşlik eder. İşlem kodu, gerçekleşen durum değişikliğini (ör. bir kontrol çubuğu hareket ettirildiğinde) belirtir. Eksen değerleri, AXIS_X veya AXIS_RTRIGGER gibi belirli bir fiziksel kontrol için konum ve diğer hareket özelliklerini tanımlar. getAction() işlevini çağırarak işlem kodunu, getAxisValue() işlevini çağırarak da eksen değerini alabilirsiniz.

Bu derste, yukarıda belirtilen View geri çağırma yöntemlerini uygulayarak ve KeyEvent ile MotionEvent nesnelerini işleyerek oyun ekranındaki en yaygın fiziksel kontrol türlerinden (oyun kumandası düğmeleri, yön düğmeleri ve kontrol çubukları) gelen girişleri nasıl işleyeceğinize odaklanılmaktadır.

Oyun kumandasının bağlı olduğunu doğrulama

Android, giriş etkinliklerini raporlarken oyun kumandası olmayan bir cihazdan gelen etkinlikler ile oyun kumandasından gelen etkinlikler arasında ayrım yapmaz. Örneğin, dokunmatik ekran işlemi, dokunma yüzeyinin X koordinatını temsil eden bir AXIS_X etkinliği oluştururken kontrol çubuğu, kontrol çubuğunun X konumunu temsil eden bir AXIS_X etkinliği oluşturur. Oyununuzda oyun kumandası girişinin işlenmesi önemliyse öncelikle giriş etkinliğinin alakalı bir kaynak türünden gelip gelmediğini kontrol etmeniz gerekir.

Bağlı bir giriş cihazının oyun kumandası olduğunu doğrulamak için getSources() işlevini çağırarak söz konusu cihazda desteklenen giriş kaynağı türlerinin birleşik bit alanını alın. Ardından aşağıdaki alanların ayarlanıp ayarlanmadığını test edebilirsiniz:

  • SOURCE_GAMEPAD kaynak türü, giriş cihazında oyun kumandası düğmeleri olduğunu gösterir (ör. BUTTON_A). Çoğu oyun kumandasında yön kontrolleri olsa da bu kaynak türünün, oyun kumandasında D-pad düğmelerinin olup olmadığını tam olarak belirtmediğini unutmayın.
  • 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ında analog kontrol çubukları (örneğin, AXIS_X ve AXIS_Y boyunca hareketleri kaydeden bir kontrol çubuğu) 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. Daha sonra 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 cihaza aynı anda bağlı 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 (sources and InputDevice.SOURCE_GAMEPAD == InputDevice.SOURCE_GAMEPAD
                    || sources and InputDevice.SOURCE_JOYSTICK == InputDevice.SOURCE_JOYSTICK) {
                // This device is a game controller. Store its device ID.
                gameControllerDeviceIds
                        .takeIf { !it.contains(deviceId) }
                        ?.add(deviceId)
            }
        }
    }
    return gameControllerDeviceIds
}

Java

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

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

Buna ek olarak, bağlı bir oyun kumandası tarafından desteklenen bağımsız giriş özelliklerini kontrol etmek isteyebilirsiniz. Bu, örneğin, oyununuzun yalnızca anladığı fiziksel kontrol grubundan gelen girişleri kullanmasını istiyorsanız yararlı olabilir.

Belirli bir tuş kodunun veya eksen kodunun bağlı bir oyun kumandası tarafından desteklenip desteklenmediğini tespit etmek için aşağıdaki teknikleri kullanın:

  • Android 4.4 (API düzeyi 19) veya sonraki sürümlerde, hasKeys(int...) işlevini çağırarak bağlı bir oyun kumandasında bir tuş kodunun desteklenip desteklenmediğini belirleyebilirsiniz.
  • Android 3.1 (API düzeyi 12) veya sonraki sürümlerde, bağlı bir oyun kumandasında desteklenen tüm eksenleri bulmak için önce getMotionRanges() işlevini çağırmanız gerekir. Daha sonra, döndürülen her InputDevice.MotionRange nesnesinde, eksen kimliğini almak için getAxis() yöntemini çağırın.

Oyun kumandası düğme basışlarını işleme

Şekil 1'de, Android'in tuş kodlarını ve eksen değerlerini çoğu oyun kumandasındaki fiziksel kontrollere nasıl eşleştirdiği gösterilmektedir.

Şekil 1. Genel oyun kumandası profili.

Şekildeki açıklama metinleri aşağıdakilere karşılık gelir:

Oyun kumandası düğmelerine basıldığında oluşturulan yaygın anahtar kodları arasında BUTTON_A, BUTTON_B, BUTTON_SELECT ve BUTTON_START bulunur. Bazı oyun kumandaları, D-pad çapraz çubuğunun ortasına basıldığında da DPAD_CENTER tuş kodunu tetikler. Oyununuz, getKeyCode() veya onKeyDown() gibi önemli etkinlik geri çağırmalarından birini çağırarak anahtar kodunu inceleyebilir ve oyununuzla alakalı bir etkinliği temsil ediyorsa bunu oyun işlemi olarak işleyebilir. Tablo 1'de, en yaygın oyun kumandası düğmeleri için önerilen oyun işlemleri listelenmektedir.

Tablo 1. Oyun kumandası düğmeleri için önerilen oyun işlemleri.

Oyun Aksiyonu Düğme tuş kodu
Oyunu ana menüden başlatın veya oyun sırasında duraklatın/duraklatmayı kaldırın BUTTON_START*
Menüyü göster BUTTON_SELECT* ve KEYCODE_MENU*
Gezinme tasarım kılavuzunda açıklanan Android Geri gezinme davranışıyla aynıdır. KEYCODE_BACK
Menüdeki önceki bir öğeye geri gitme BUTTON_B
Seçimi onaylayın veya birincil oyun işlemini gerçekleştirin BUTTON_A ve DPAD_CENTER

* Oyununuz Başlat, Seç veya Menü düğmelerinin varlığına bağlı olmamalıdır.

İpucu: Kullanıcıların oyun hareketleri için kendi oyun kumandası eşlemelerini kişiselleştirmelerine olanak tanımak amacıyla oyununuzda bir yapılandırma ekranı sunabilirsiniz.

Aşağıdaki snippet'te, BUTTON_A ve DPAD_CENTER düğmelerine basma işlemlerini bir oyun hareketiyle ilişkilendirmek için onKeyDown()'ü nasıl geçersiz kılabileceğiniz gösterilmektedir.

Kotlin

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

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

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

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

Java

public class GameView extends View {
    ...

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

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

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

Not: Android 4.2 (API düzeyi 17) ve önceki sürümlerde sistem, BUTTON_A simgesini varsayılan olarak Android Geri anahtarı olarak kabul eder. Uygulamanız bu Android sürümlerini destekliyorsa BUTTON_A öğesini birincil oyun işlemi olarak kabul ettiğinizden emin olun. Cihazdaki mevcut Android SDK sürümünü belirlemek için Build.VERSION.SDK_INT değerine bakın.

Yön tuşları girişini işleme

4 yönlü yön çubuğu (D-pad), birçok oyun kontrol cihazında bulunan yaygın bir fiziksel kontroldür. Android, D-pad'in yukarı ve aşağı düğmelerine basılmasını -1,0 (yukarı) ile 1,0 (aşağı) arasında değişen bir aralığa sahip AXIS_HAT_Y etkinlikleri olarak, D-pad'in sol veya sağ düğmelerine basılmasını ise -1,0 (sol) ile 1,0 (sağ) arasında değişen bir aralığa sahip AXIS_HAT_Y etkinlikleri olarak raporlar.AXIS_HAT_X

Bunun yerine bazı kumandalar d-pad'e basmaları bir tuş koduyla bildirir. Oyununuzda D-pad tuşlarına basılması önemliyse şapka ekseni etkinliklerini ve D-pad tuş kodlarını tablo 2'de önerildiği gibi aynı giriş etkinlikleri olarak ele almalısınız.

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

Aksiyon D-pad tuş kodu Şapka Ekseni Kodu
Yukarı Taşı KEYCODE_DPAD_UP AXIS_HAT_Y (0 ila -1,0 arasındaki değerler için)
Aşağı Taşı KEYCODE_DPAD_DOWN AXIS_HAT_Y (0 - 1,0 değerleri için)
Sola Taşı KEYCODE_DPAD_LEFT AXIS_HAT_X (0 ila -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 bir giriş etkinliğindeki şapka eksenini 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.
            event.source and InputDevice.SOURCE_DPAD != InputDevice.SOURCE_DPAD
    }
}

Java

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

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

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

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

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

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

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

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

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

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

Örnek:

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

Kontrol çubuğu hareketlerini işleme

Oyuncular oyun kontrol cihazlarındaki bir kontrol çubuğunu hareket ettirdiğinde Android, ACTION_MOVE işlem kodunu ve kontrol çubuğunun eksenlerinin güncellenmiş konumlarını içeren bir MotionEvent bildirir. Oyununuz, önemsediği bir kontrol çubuğu hareketinin olup olmadığını belirlemek için MotionEvent tarafından sağlanan verileri kullanabilir.

Kontrol çubuğu hareket etkinliklerinin tek bir nesne içinde birden fazla hareket örneğini gruplandırabileceğini unutmayın. MotionEvent nesnesi, her kontrol çubuğu ekseninin mevcut konumunun yanı sıra her eksen için birden fazla geçmiş konum içerir. Android, ACTION_MOVE işlem koduyla hareket etkinliklerini raporlarken (ör. kontrol çubuğu hareketleri) verimlilik için eksen değerlerini gruplandırır. Bir eksenin geçmiş değerleri, geçerli eksen değerinden eski ve önceki hareket etkinliklerinde raporlanan değerlerden daha yeni olan farklı değerlerden oluşur. Ayrıntılar için MotionEvent referansına bakın.

Geçmiş bilgileri kullanarak bir oyun nesnesinin hareketini kontrol çubuğu girişine göre daha doğru şekilde oluşturabilirsiniz. Mevcut ve geçmiş değerleri almak için getAxisValue() veya getHistoricalAxisValue() işlevini çağırın. getHistorySize() işlevini çağırarak kontrol çubuğu etkinliğindeki geçmiş noktaların sayısını da bulabilirsiniz.

Aşağıdaki snippet'te, kontrol çubuğu girişini işlemek için onGenericMotionEvent() geri çağırmasını nasıl geçersiz kılabileceğiniz gösterilmektedir. Önce bir eksenin geçmiş değerlerini, ardından mevcut konumunu işlemeniz gerekir.

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

Kontrol çubuğu girişini kullanmadan önce kontrol çubuğunun ortada olup olmadığını belirlemeniz ve ardından eksen hareketlerini buna göre hesaplamanız gerekir. Kontrol çubuklarında genellikle sabit bir alan bulunur. Bu alan, eksenin ortada olduğu kabul edilen (0,0) koordinatına yakın bir değer aralığıdır. Android tarafından bildirilen eksen değeri düz alandaysa kumandayı hareketsiz (yani her iki eksende hareketsiz) olarak değerlendirmeniz gerekir.

Aşağıdaki snippet'te, her eksen boyunca hareketi hesaplayan bir yardımcı yöntem gösterilmektedir. Bu yardımcıyı, aşağıda 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 getirerek oyununuzda kontrol çubuğu hareketlerini nasıl işleyebileceğinizi aşağıda görebilirsiniz:

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ğundan daha gelişmiş özelliklere sahip oyun denetleyicilerini desteklemek için aşağıdaki en iyi uygulamaları uygulayın:

  • Çift kumanda çubuğunu tutma. Birçok oyun kumandasında hem sol hem de sağ kontrol çubuğu 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 raporlar. Kodunuzda her iki kumanda çubuğunu da kullandığınızdan emin olun.
  • Kenar tetiği basma işlemlerini kontrol edin (ve oyununuzun AXIS_ ve KEYCODE_BUTTON_ etkinlikleriyle çalıştığından emin olun). Bazı kontrol cihazlarında sol ve sağ omuz tetikleyicileri bulunur. Bu tetikleyiciler mevcut olduğunda bir AXIS_*TRIGGER veya KEYCODE_BUTTON_*2 etkinliği ya da her ikisi de yayınlanır. Sol tetikleyici için bu AXIS_LTRIGGER ve KEYCODE_BUTTON_L2 olur. Doğru tetikleyici için bu değerler AXIS_RTRIGGER ve KEYCODE_BUTTON_R2 olmalıdır. Eksen etkinlikleri yalnızca tetikleyici 0 ile 1 arasında bir değer aralığı yayarsa gerçekleşir ve analog çıkışa sahip bazı denetleyiciler, eksen etkinliklerine ek olarak düğme etkinlikleri de yayar. Oyunlar, yaygın olarak kullanılan tüm oyun kumandalarıyla uyumlu kalmak için hem AXIS_ hem de KEYCODE_BUTTON_ etkinliklerini desteklemelidir. Ancak bir kumanda her ikisini de bildirirse oyununuz için en uygun etkinliği tercih edin. Android 4.3 (API düzeyi 18) ve sonraki sürümlerde, AXIS_LTRIGGER üreten bir denetleyici 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 basma işlemlerini 0,0 (bırakılmış) ile 1,0 (tamamen basılmış) arasında normalleştirilmiş bir değerle raporlar.
  • Belirli davranışlar ve destek, taklit edilen ortamlarda farklılık gösterebilir. Google Play Games gibi taklit edilen platformların davranışı, barındırıcı işletim sisteminin özelliklerine bağlı olarak biraz farklılık gösterebilir. Örneğin, hem AXIS_ hem de KEYCODE_BUTTON_ etkinliklerini yayan bazı kumandalar yalnızca AXIS_ etkinliklerini yayar. Bazı kumandalar için sunulan destek tamamen eksik olabilir.