کنش‌های کنترل‌کننده را مدیریت کنید

کنترل‌کننده‌ها دو نوع کنش دارند:

  • KeyEvent برای هر دکمه‌ای با وضعیت دودویی «روشن» و «خاموش» استفاده می‌شود
  • MotionEvent برای هر محوری که محدوده‌ای از مقادیر را برمی‌گرداند استفاده می‌شود. برای مثال، از -۱ تا ۱ برای دسته آنالوگ یا از ۰ تا ۱ برای ماشه‌های آنالوگ.

می‌توانید این ورودی‌ها را از View که focus دارد بخوانید.

کاتلین

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

جاوا

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

درصورت نیاز، می‌توانید رویدادها را مستقیماً از Activity بخوانید.

درستی‌سنجی کنید کنترل‌کننده بازی متصل باشد

هنگام گزارش رویدادهای ورودی، Android از همان کلید یا شناسه‌های محور برای انواع مختلف دستگاه ورودی استفاده مجدد می‌کند. برای مثال، کنش صفحه لمسی رویداد AXIS_X را تولید می‌کند که نشان‌دهنده مختصات X سطح لمسی است، اما گیم‌پد رویداد AXIS_X را تولید می‌کند که نشان‌دهنده موقعیت X چوبک سمت چپ است. این یعنی باید نوع منبع را بررسی کنید تا رویدادهای ورودی را به‌درستی تفسیر کنید.

برای تأیید اینکه InputDevice متصل‌شده کنترل‌کننده بازی است، از تابع supportsSource(int) استفاده کنید:

  • نوع منبع SOURCE_GAMEPAD نشان می‌دهد که دستگاه ورودی دکمه‌های کنترل‌کننده دارد (برای مثال، KEYCODE_BUTTON_A). توجه داشته باشید که این نوع منبع به‌طور دقیق نشان نمی‌دهد که کنترل بازی دکمه‌های پد کنترل دارد یا نه، اگرچه اکثر کنترل‌کننده‌ها معمولاً کنترل‌های جهتی دارند.
  • نوع منبع SOURCE_DPAD نشان می‌دهد که دستگاه ورودی دکمه‌های پد کنترل دارد (برای مثال، DPAD_UP).
  • نوع منبع SOURCE_JOYSTICK نشان می‌دهد که دستگاه ورودی دارای دسته‌های کنترل آنالوگ است (برای مثال، دسته بازی که حرکات را در امتداد AXIS_X و AXIS_Y ثبت می‌کند).

تکه کد زیر روش کمکی را نشان می‌دهد که به شما امکان می‌دهد بررسی کنید آیا دستگاه‌های ورودی متصل کنترل‌کننده بازی هستند یا نه. در این صورت، روش شناسه‌های دستگاه را برای کنترل‌کننده‌های بازی بازیابی می‌کند. سپس می‌توانید هر شناسه دستگاه را با بازیکنی در بازی‌تان مرتبط کنید و کنش‌های بازی را برای هر بازیکن متصل به‌صورت جداگانه پردازش کنید. برای کسب اطلاعات بیشتر درباره پشتیبانی از چند دسته کنترل بازی که به‌طور هم‌زمان به یک دستگاه Android متصل هستند، به پشتیبانی از چند دسته کنترل بازی مراجعه کنید.

کاتلین

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
}

جاوا

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

ورودی‌های کنترل‌کننده پردازش

این بخش انواع کنترل‌کننده‌های بازی را که در Android پشتیبانی می‌شوند شرح می‌دهد.

توسعه‌دهندگان C++ باید از کتابخانه کنترل‌کننده بازی استفاده کنند. این ویژگی همه کنترل‌کننده‌ها را در رایج‌ترین زیرمجموعه ویژگی‌ها یکپارچه می‌کند و یک رابط کاربری یکپارچه بین آن‌ها ارائه می‌دهد، ازجمله قابلیت تشخیص چیدمان دکمه.

این شکل نشان می‌دهد که توسعه‌دهنده بازی Android می‌تواند انتظار داشته باشد کنترل‌کننده معمولی در Android چگونه باشد.

کنترل‌کننده بازی عمومی با ورودی‌های برچسب‌گذاری‌شده شامل پد کنترل، چوب‌های آنالوگ، و دکمه‌ها
شکل ۱. نمایه برای دسته کنترل بازی عمومی.

این جدول نام‌ها و انواع رویدادهای استاندارد را برای کنترل‌کننده‌های بازی فهرست می‌کند. برای فهرست کامل رویدادها، گونه‌های رایج را ببینید. سیستم MotionEvent رویداد را ازطریق onGenericMotionEvent و KeyEvent رویداد را ازطریق onKeyDown و onKeyUp ارسال می‌کند.

ورودی کنترل‌کننده KeyEvent MotionEvent
۱. پد کنترل
AXIS_HAT_X
(ورودی افقی)
AXIS_HAT_Y
(ورودی عمودی)
۲. دسته کنترل آنالوگ چپ
‫KEYCODE_BUTTON_THUMBL
(وقتی فشرده می‌شود)
AXIS_X
(حرکت افقی)
AXIS_Y
(حرکت عمودی)
۳. دسته کنترل آنالوگ راست
‫KEYCODE_BUTTON_THUMBR
(وقتی فشرده می‌شود)
AXIS_Z
(حرکت افقی)
AXIS_RZ
(حرکت عمودی)
۴. دکمه X KEYCODE_BUTTON_X
۵. دکمه A KEYCODE_BUTTON_A
۶. دکمه Y KEYCODE_BUTTON_Y
‫۷. دکمه B KEYCODE_BUTTON_B
‫۸. دکمه بامپر راست
KEYCODE_BUTTON_R1
‫۹. راه‌انداز راست
AXIS_RTRIGGER
‫۱۰. راه‌انداز چپ AXIS_LTRIGGER
‫۱۱. دکمه بامپر چپ KEYCODE_BUTTON_L1
‫۱۲. شروع KEYCODE_BUTTON_START
‫۱۳. انتخاب KEYCODE_BUTTON_SELECT

مدیریت فشار دکمه‌ها

ازآنجایی‌که Android فشار دکمه کنترل‌کننده را دقیقاً مشابه فشار دکمه صفحه‌کلید گزارش می‌کند، باید:

  • تأیید کنید که رویداد از SOURCE_GAMEPAD می‌آید.
  • مطمئن شوید که دکمه را فقط یک‌بار با KeyEvent.getRepeatCount() دریافت می‌کنید، Android رویدادهای کلیدی تکراری را درست مثل زمانی که کلید صفحه‌کلید را پایین نگه داشته‌اید ارسال می‌کند.
  • با برگرداندن true نشان دهید که رویدادی مدیریت می‌شود.
  • رویدادهای مدیریت‌نشده را به super ارسال کنید تا مطمئن شوید لایه‌های سازگاری مختلف Android به‌درستی کار می‌کنند.

    کاتلین

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

    جاوا

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

پردازش ورودی پد جهت‌دار

پد کنترل چهارطرفه یا پد کنترل، یک کنترل فیزیکی رایج در بسیاری از کنترل‌کننده‌های بازی است. ‫Android فشار دکمه‌های «بالا» و «پایین» صفحه کلید کنترل را به‌عنوان رویدادهای AXIS_HAT_Y گزارش می‌کند، که در آن ۱- نشان‌دهنده بالا و ۱ نشان‌دهنده پایین است. فشارهای «پد کنترل» به «چپ» یا «راست» را به‌عنوان رویدادهای AXIS_HAT_X گزارش می‌کند، که -۱.۰ نشان‌دهنده چپ و ۱.۰ نشان‌دهنده راست است.

برخی‌از کنترل‌کننده‌ها به‌جای آن، فشار دکمه‌های D-pad را با کد کلید گزارش می‌کنند. اگر بازی شما به فشار دکمه‌های پد کنترل اهمیت می‌دهد، باید رویدادهای محور کلاهک و کدهای کلید پد کنترل را به‌عنوان رویدادهای ورودی یکسان درنظر بگیرید، همان‌طور که در جدول ۲ توصیه شده است.

جدول ۲. کنش‌های بازی پیش‌فرض توصیه‌شده برای کدهای کلید پد کنترل و مقادیر محور کلاهک.

کنش بازی کد کلید پد کنترل کد محور کلاه
حرکت به‌بالا KEYCODE_DPAD_UP ‫AXIS_HAT_Y (برای مقادیر ۰ تا -۱٫۰)
حرکت به‌پایین KEYCODE_DPAD_DOWN ‫AXIS_HAT_Y (برای مقادیر ۰ تا ۱٫۰)
حرکت به‌چپ KEYCODE_DPAD_LEFT ‫AXIS_HAT_X (برای مقادیر ۰ تا -۱٫۰)
حرکت به‌راست KEYCODE_DPAD_RIGHT ‫AXIS_HAT_X (برای مقادیر ۰ تا ۱٫۰)

تکه‌کد زیر کلاس کمکی را نشان می‌دهد که به شما امکان می‌دهد مقادیر محور کلاه و کد کلید را از رویداد ورودی بررسی کنید تا جهت پد کنترل را تعیین کنید.

کاتلین

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

جاوا

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

هرجا که بخواهید ورودی «کلیدهای کنترل» را در بازی‌تان پردازش کنید (برای نمونه، در onGenericMotionEvent() یا onKeyDown() پاسخ‌گویی‌ها)، می‌توانید از این کلاس کمکی استفاده کنید.

برای مثال:

کاتلین

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

جاوا

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

پردازش حرکات دسته کنترل

وقتی بازیکنان دسته کنترل بازی را حرکت می‌دهند، Android MotionEvent را گزارش می‌کند که حاوی ACTION_MOVE کد عمل و موقعیت‌های به‌روزشده محورهای دسته کنترل است. بازی شما می‌تواند از داده‌های ارائه‌شده توسط MotionEvent برای تعیین اینکه آیا حرکت دسته‌ای که به آن اهمیت می‌دهد رخ داده است یا نه استفاده کند.

توجه داشته باشید که رویدادهای حرکت جوی‌استیک ممکن است چندین نمونه حرکت را در یک شیء دسته‌ای کند. شیء MotionEvent حاوی موقعیت فعلی برای هر محور جوی‌استیک و همچنین چندین موقعیت تاریخی برای هر محور است. هنگام گزارش رویدادهای حرکت با کد کنش ACTION_MOVE (مثل حرکات دسته بازی)، Android مقادیر محور را برای کارایی دسته‌بندی می‌کند. مقادیر تاریخی برای یک محور شامل مجموعه مقادیر متمایزی است که قدیمی‌تر از مقدار محور فعلی هستند و جدیدتر از مقادیری هستند که در هریک از رویدادهای حرکتی قبلی گزارش شده‌اند. برای جزئیات، به MotionEvent مرجع مراجعه کنید.

برای ارائه دقیق حرکت یک شیء بازی براساس ورودی دسته‌بازی، می‌توانید از اطلاعات سابقه ارائه‌شده توسط اشیاء MotionEvent استفاده کنید.

بااستفاده از روش‌های زیر می‌توانید مقادیر کنونی و گذشته را بازیابی کنید:

تکه‌کد زیر نشان می‌دهد که چگونه می‌توانید onGenericMotionEvent() بازخوان را برای پردازش ورودی دسته‌بازی ملغی کنید. ابتدا باید مقادیر تاریخی محور را پردازش کنید، سپس موقعیت کنونی آن را پردازش کنید.

کاتلین

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

جاوا

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

قبل‌از استفاده از ورودی دسته کنترل، باید تعیین کنید که آیا دسته کنترل در مرکز قرار دارد یا نه، سپس حرکت‌های محور آن را براساس آن محاسبه کنید. دسته بازی معمولاً دارای یک ناحیه مسطح است، یعنی محدوده‌ای از مقادیر نزدیک به مختصات (۰،۰) که در آن محور به‌عنوان مرکز درنظر گرفته می‌شود. اگر مقدار محور گزارش‌شده توسط Android در منطقه مسطح قرار گیرد، باید کنترل‌کننده را در حالت استراحت درنظر بگیرید (یعنی بدون حرکت در هر دو محور).

گزیده روش کمکی را نشان می‌دهد که حرکت را در امتداد هر محور محاسبه می‌کند. این دستیار را در روش processJoystickInput() که در نمونه زیر بیشتر توضیح داده شده است فرا می‌خوانید:

کاتلین

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
}

جاوا

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

با کنار هم گذاشتن همه این‌ها، در اینجا نحوه پردازش حرکات دسته کنترل در بازی شما آورده شده است:

کاتلین

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
}

جاوا

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
}

برای پشتیبانی از دسته‌های بازی که ویژگی‌های پیچیده‌تری فراتر از یک دسته کنترل دارند، این روال‌های مطلوب را دنبال کنید:

  • دسته‌های کنترلر دوگانه را کنترل کنید. بسیاری از کنترل‌کننده‌های بازی دارای هر دو جوی‌استیک چپ و راست هستند. برای چوبک سمت چپ، Android حرکت‌های افقی را به‌صورت رویدادهای AXIS_X و حرکت‌های عمودی را به‌صورت رویدادهای AXIS_Y گزارش می‌کند. برای دسته راست، Android حرکت‌های افقی را به‌عنوان رویدادهای AXIS_Z و حرکت‌های عمودی را به‌عنوان رویدادهای AXIS_RZ گزارش می‌کند. مطمئن شوید که هر دو چوبک کنترلر را در کد خود مدیریت کنید.
  • فشار دکمه‌های شانه‌ای را مدیریت کنید (و مطمئن شوید که بازی‌تان با رویدادهای AXIS_ و KEYCODE_BUTTON_ کار می‌کند). برخی‌از کنترل‌کننده‌ها دارای کلیدهای شانه‌ای چپ و راست هستند. وقتی این راه‌اندازها وجود داشته باشند، رویداد AXIS_*TRIGGER یا KEYCODE_BUTTON_*2 یا هر دو را منتشر می‌کنند. برای ماشه سمت چپ، این‌ها AXIS_LTRIGGER و KEYCODE_BUTTON_L2 هستند. برای ماشه راست، این‌ها AXIS_RTRIGGER و KEYCODE_BUTTON_R2 هستند. رویدادهای محور فقط درصورتی رخ می‌دهند که راه‌انداز محدوده‌ای از مقادیر بین ۰ و ۱ را منتشر کند، و برخی‌از کنترل‌کننده‌های دارای برونداد آنالوگ علاوه بر رویدادهای محور، رویدادهای دکمه‌ای نیز منتشر می‌کنند. بازی‌ها باید از هر دو رویداد AXIS_ و KEYCODE_BUTTON_ پشتیبانی کنند تا با همه دسته‌های کنترل بازی رایج سازگار بمانند، اما اگر دسته کنترل هر دو رویداد را گزارش کند، رویدادی را ترجیح دهید که برای بازی شما منطقی‌تر است. در Android 4.3 (سطح API 18) و بالاتر، کنترل‌کننده‌ای که AXIS_LTRIGGER تولید می‌کند، مقدار یکسانی را برای محور AXIS_BRAKE نیز گزارش می‌کند. همین امر برای AXIS_RTRIGGER و AXIS_GAS نیز صادق است. ‫Android همه فشارهای محرک آنالوگ را با مقدار نرمال‌سازی‌شده از ۰٫۰ (رهاشده) تا ۱٫۰ (کاملاً فشرده) گزارش می‌کند.
  • عملکردهای خاص و پشتیبانی ممکن است در محیط‌های شبیه‌سازی‌شده متفاوت باشد. پلاتفرم‌های شبیه‌سازی‌شده، مانند بازی‌های Google Play، ممکن است براساس قابلیت‌های سیستم‌عامل میزبان در عملکرد کمی متفاوت باشند. برای مثال، برخی‌از کنترل‌کننده‌هایی که هم رویدادهای AXIS_ و هم KEYCODE_BUTTON_ را منتشر می‌کنند، فقط رویدادهای AXIS_ را منتشر می‌کنند، و پشتیبانی از برخی‌از کنترل‌کننده‌ها ممکن است به‌طور کامل وجود نداشته باشد.

گونه‌های رایج

با تنوع گسترده پشتیبانی Android از کنترل‌کننده‌ها، ممکن است نحوه ساخت و آزمایش برای تأیید اینکه بازی شما بدون اشکال در میان پایگاه بازیکنان کار می‌کند، نامشخص باشد. متوجه شده‌ایم که با وجود این تنوع ظاهری، تولیدکنندگان کنترل‌کننده در سراسر جهان تمایل دارند به‌طور مداوم از سه سبک مختلف کنترل‌کننده پیروی کنند. برخی‌از آن‌ها کلیدهای سخت‌افزاری برای جابه‌جایی بین دو یا چند مورد از این موارد دارند.

این یعنی می‌توانید با حداقل سه کنترل‌کننده در تیم توسعه‌دهنده خود آزمایش کنید و مطمئن باشید که بازی‌تان بدون نیاز به فهرست‌های مجاز و غیرمجاز قابل‌بازی است.

انواع رایج کنترل‌کننده

رایج‌ترین سبک کنترل‌کننده‌ها معمولاً از چیدمان کنسول‌های بازی محبوب تقلید می‌کنند. این هم ازنظر زیبایی‌شناسی در برچسب‌های دکمه و چیدمان و هم ازنظر عملکردی با رویدادهایی که ایجاد می‌شوند، اهمیت دارد. کنترل‌کننده‌هایی که دارای کلیدهای سخت‌افزاری برای جابه‌جایی بین انواع مختلف کنسول هستند، رویدادهایی را که ارسال می‌کنند تغییر می‌دهند و اغلب حتی چیدمان دکمه منطقی آن‌ها نیز تغییر می‌کند.

هنگام آزمایش، توصیه می‌کنیم درستی‌سنجی کنید که بازی‌تان با یک کنترل‌کننده در هر دسته کار می‌کند. می‌توانید انتخاب کنید که با کنترل‌کننده‌های طرف اول یا تولیدکنندگان طرف سوم محبوب آزمایش کنید. به‌طورکلی، ما محبوب‌ترین کنترلرها را با تمام تلاش به تعریف بالا نگاشت خواهیم کرد.

نوع کنترل‌کننده تفاوت‌های رفتاری گونه‌های برچسب‌گذاری
کنترل‌کننده‌های سبک Xbox

این‌ها کنترل‌کننده‌هایی هستند که معمولاً برای پلاتفرم Microsoft Xbox و Windows* ساخته می‌شوند.

این کنترل‌کننده‌ها با مجموعه ویژگی‌های شرح‌داده‌شده در پردازش ورودی‌های کنترل‌کننده مطابقت دارند دکمه‌های L2/R2 در این کنترلرها با برچسب LT/RT مشخص شده‌اند
کنترل‌کننده‌های سبک کلید

این کنترل‌کننده‌ها معمولاً برای خانواده کنسول‌های Nintendo Switch* طراحی شده‌اند.

این کنترل‌کننده‌ها KeyEvent KEYCODE_BUTTON_R2 KEYCODE_BUTTON_L2 MotionEvent را ارسال می‌کنند دکمه‌های L2/R2 در این کنترل‌کننده‌ها با برچسب ZL/ZR مشخص شده‌اند.

این کنترل‌کننده‌ها همچنین دکمه‌های A و B و دکمه‌های X و Y را جابه‌جا می‌کنند، بنابراین KEYCODE_BUTTON_A دکمه‌ای است که با B برچسب‌گذاری شده است و بالعکس.

کنترل‌کننده‌های سبک PlayStation

این کنترل‌کننده‌ها معمولاً برای خانواده کنسول‌های Sony PlayStation* طراحی شده‌اند.

این کنترل‌کننده‌ها MotionEvents مثل کنترل‌کننده‌های سبک Xbox ارسال می‌کنند، اما وقتی کاملاً فشرده می‌شوند KeyEvents مثل کنترل‌کننده‌های سبک Switch نیز ارسال می‌کنند. این کنترل‌کننده‌ها از مجموعه متفاوتی از گلیف‌ها برای دکمه‌های چهره استفاده می‌کنند.

* Microsoft،‏ Xbox، و Windows علامت‌های تجاری ثبت‌شده Microsoft هستند؛ Nintendo Switch علامت تجاری ثبت‌شده Nintendo of America Inc است؛ ‫PlayStation علامت تجاری ثبت‌شده Sony Interactive Entertainment Inc.‎ است.

دکمه‌های راه‌انداز ابهام‌زدایی

برخی‌از کنترل‌کننده‌ها AXIS_LTRIGGER و AXIS_RTRIGGER را ارسال می‌کنند، برخی دیگر KEYCODE_BUTTON_L2 و KEYCODE_BUTTON_R2 را ارسال می‌کنند، و برخی دیگر همه این رویدادها را براساس قابلیت‌های سخت‌افزاری‌شان ارسال می‌کنند. با پشتیبانی از همه این رویدادها، سازگاری را به حداکثر برسانید.

همه کنترل‌کننده‌هایی که AXIS_LTRIGGER را ارسال می‌کنند، AXIS_BRAKE را نیز ارسال می‌کنند، به‌همین ترتیب برای AXIS_RTRIGGER و AXIS_GAS برای کمک به حداکثر رساندن سازگاری بین چرخ‌های مسابقه و کنترل‌کننده‌های بازی معمولی. به‌طورکلی این کار مشکلی ایجاد نمی‌کند، اما برای ویژگی‌هایی مثل صفحه‌های تخصیص مجدد کلیدها مراقب باشید.

ره‌انداز MotionEvent KeyEvent
راه‌انداز چپ AXIS_LTRIGGER
AXIS_BRAKE
KEYCODE_BUTTON_L2
راه‌انداز راست AXIS_RTRIGGER
AXIS_GAS
KEYCODE_BUTTON_R2

باید دقت شود که بازی شما بتواند هم KeyEvent و هم MotionEvent را مدیریت کند تا با بیشترین تعداد ممکن کنترلر سازگار باشد و رویدادها تکراری نباشند.

کنترل‌کننده‌های پشتیبانی‌شده

هنگام آزمایش، توصیه می‌کنیم درستی‌سنجی کنید که بازی‌تان با یک کنترل‌کننده در هر دسته کار می‌کند.

  • سبک Xbox
  • سبک Nintendo Switch
  • سبک PlayStation

می‌توانید با کنترل‌کننده‌های طرف اول یا تولیدکنندگان طرف سوم محبوب آزمایش کنید، و به‌طورکلی کنترل‌کننده‌های محبوب را تا حد امکان به تعریف نزدیک‌تر تخصیص می‌دهیم.