কন্ট্রোলার অ্যাকশন ম্যানেজ করা

কন্ট্রোলারের অ্যাকশন দুই ধরনের হয়:

  • KeyEvent "চালু" ও "বন্ধ" বাইনারি স্টেট সহ যেকোনও বোতামের জন্য ব্যবহার করা হয়
  • MotionEvent এমন কোনও অ্যাক্সিসের জন্য ব্যবহার করা হয় যা ভ্যালুর রেঞ্জ রিটার্ন করে। যেমন -১ থেকে ১ পর্যন্ত অ্যানালগ স্টিক বা অ্যানালগ ট্রিগারের জন্য ০ থেকে ১ পর্যন্ত।

focus আছে এমন View থেকে আপনি এইসব ইনপুট পড়তে পারবেন।

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

জাভা

@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)। মনে রাখবেন এই সোর্স টাইপ থেকে গেম কন্ট্রোলারে D-প্যাড বোতাম আছে কিনা তা স্পষ্টভাবে বোঝা যায় না, যদিও বেশিরভাগ কন্ট্রোলারে সাধারণত দিকনির্দেশক কন্ট্রোল থাকে।
  • SOURCE_DPAD সোর্স টাইপ থেকে বোঝা যায় যে ইনপুট ডিভাইসে D-প্যাড বোতাম আছে (যেমন, DPAD_UP)।
  • SOURCE_JOYSTICK সোর্স টাইপ থেকে বোঝা যায় যে ইনপুট ডিভাইসে অ্যানালগ কন্ট্রোল স্টিক আছে (যেমন, একটি জয়স্টিক যা AXIS_X ও AXIS_Y বরাবর মুভমেন্ট রেকর্ড করে)।

নিচে দেওয়া কোড স্নিপেটে একটি হেল্পার পদ্ধতি দেখানো হয়েছে যা আপনাকে চেক করে দেখতে দেয় যে কানেক্ট করা ইনপুট ডিভাইসগুলি গেম কন্ট্রোলার কিনা। যদি তাই হয়, তাহলে পদ্ধতিটি গেম কন্ট্রোলারের জন্য ডিভাইস আইডি পুনরুদ্ধার করে। তারপরে, আপনি প্রতিটি ডিভাইস আইডিকে আপনার গেমের প্লেয়ারের সাথে যুক্ত করতে এবং প্রতিটি কানেক্ট করা প্লেয়ারের জন্য গেম অ্যাকশন আলাদাভাবে প্রসেস করতে পারবেন। একই Android-পরিচালিত ডিভাইসে একসাথে কানেক্ট করা একাধিক গেম কন্ট্রোলারের ব্যাপারে আরও জানতে, একাধিক গেম কন্ট্রোলার কাজ করে লিঙ্ক দেখুন।

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
}

জাভা

 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++ ডেভেলপারদের Game Controller Library ব্যবহার করা উচিত। এটি সব কন্ট্রোলারকে ফিচারের সবচেয়ে সাধারণ সাবসেটে একত্রিত করে এবং বোতাম লেআউট শনাক্ত করার ক্ষমতা সহ সেগুলির মধ্যে একটি সামঞ্জস্যপূর্ণ ইন্টারফেস প্রদান করে।

এই ছবিতে দেখানো হয়েছে যে Android গেম ডেভেলপাররা Android-এ সাধারণ কন্ট্রোলার কেমন দেখতে হবে বলে আশা করতে পারেন।

লেবেল করা ইনপুট সহ জেনেরিক গেম কন্ট্রোলার, যার মধ্যে D-প্যাড, অ্যানালগ স্টিক ও বোতাম অন্তর্ভুক্ত
ছবি ১. সাধারণ গেম কন্ট্রোলারের জন্য প্রোফাইল।

টেবিলে গেম কন্ট্রোলারের জন্য স্ট্যান্ডার্ড ইভেন্টের নাম ও ধরন তালিকাভুক্ত করা হয়েছে। ইভেন্টের সম্পূর্ণ তালিকা দেখতে, সাধারণ ভেরিয়েন্ট দেখুন। সিস্টেম onGenericMotionEvent-এর মাধ্যমে MotionEvent ইভেন্ট এবং onKeyDown ও onKeyUp-এর মাধ্যমে KeyEvent ইভেন্ট পাঠায়।

কন্ট্রোলার ইনপুট 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 রিটার্ন করে ইঙ্গিত দিন যে ইভেন্টটি হ্যান্ডেল করা হয়েছে।
  • Android-এর বিভিন্ন মানানসই লেয়ার সঠিকভাবে কাজ করছে কিনা তা যাচাই করতে, super-এ আনহ্যান্ডেলড ইভেন্ট পাস করুন।

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

    জাভা

    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, D-pad-এর উপরের ও নিচের দিকের প্রেসকে AXIS_HAT_Y ইভেন্ট হিসেবে রিপোর্ট করে, যেখানে -১.০ উপরের দিক ও ১.০ নিচের দিক নির্দেশ করে। এটি D-pad-এর বাঁদিকে বা ডানদিকে প্রেস করাকে AXIS_HAT_X ইভেন্ট হিসেবে রিপোর্ট করে, যেখানে -১.০ বাঁদিকে এবং ১.০ ডানদিকে বোঝায়।

এর পরিবর্তে, কিছু কন্ট্রোলার কী কোডের মাধ্যমে D-প্যাড প্রেস করার কথা জানায়। আপনার গেম যদি D-pad প্রেসের ব্যাপারে খেয়াল রাখে, তাহলে আপনাকে হ্যাটের অ্যাক্সিস ইভেন্ট এবং D-pad কী কোডকে একই ইনপুট ইভেন্ট হিসেবে বিবেচনা করতে হবে, যেমনটি সারণী ২-এ সাজেস্ট করা হয়েছে।

টেবিল ২. D-pad কী কোড ও হ্যাট অ্যাক্সিস ভ্যালুর জন্য সাজেস্ট করা ডিফল্ট গেম অ্যাকশন।

গেম অ্যাকশন ডি-প্যাড কী কোড Hat Axis Code
উপরে যান KEYCODE_DPAD_UP AXIS_HAT_Y (০ থেকে -১.০ পর্যন্ত ভ্যালুর জন্য)
নিচে সরান KEYCODE_DPAD_DOWN AXIS_HAT_Y (০ থেকে ১.০ পর্যন্ত ভ্যালুর জন্য)
বাঁদিকে সরান KEYCODE_DPAD_LEFT AXIS_HAT_X (০ থেকে -১.০ পর্যন্ত ভ্যালুর জন্য)
ডানদিকে সরান KEYCODE_DPAD_RIGHT AXIS_HAT_X (০ থেকে ১.০ পর্যন্ত ভ্যালুর জন্য)

নিচের কোড স্নিপেটে একটি হেল্পার ক্লাস দেখানো হয়েছে যা আপনাকে D-pad দিক নির্ধারণ করতে ইনপুট ইভেন্ট থেকে হ্যাট অ্যাক্সিস এবং কী কোড ভ্যালু চেক করতে দেয়।

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

জাভা

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

আপনি নিজের গেমে যেখানেই D-pad ইনপুট প্রসেস করতে চান, সেখানে এই হেল্পার ক্লাস ব্যবহার করতে পারবেন (যেমন, onGenericMotionEvent() বা onKeyDown() কলব্যাক)।

যেমন:

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

জাভা

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() কলব্যাক ওভাররাইড করতে পারেন তা নিম্নলিখিত স্নিপেট থেকে জানতে পারবেন। আপনাকে প্রথমে কোনও অ্যাক্সিসের ঐতিহাসিক মান প্রসেস করতে হবে, তারপরে এর বর্তমান পজিশন প্রসেস করতে হবে।

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

জাভা

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() পদ্ধতিতে আহ্বান করেন, যা নিম্নলিখিত নমুনাতে আরও বিস্তারিতভাবে বর্ণনা করা হয়েছে:

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
}

জাভা

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

সবকিছু একসাথে করে, আপনার গেমে জয়স্টিক মুভমেন্ট কীভাবে প্রসেস করতে পারেন তা এখানে দেখানো হল:

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
}

জাভা

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, সব অ্যানালগ ট্রিগার প্রেসের জন্য একটি স্বাভাবিক মান সহ রিপোর্ট করে, যা 0.0 (রিলিজ করা) থেকে 1.0 (সম্পূর্ণ প্রেস করা) পর্যন্ত হয়।
  • নির্দিষ্ট আচরণ ও সহায়তা এমুলেটেড এনভায়রনমেন্টে আলাদা হতে পারে। Google Play Games-এর মতো এমুলেটেড প্ল্যাটফর্মের ব্যবহার হোস্ট অপারেটিং সিস্টেমের ক্ষমতার উপর নির্ভর করে কিছুটা আলাদা হতে পারে। যেমন, কিছু কন্ট্রোলার যেগুলি 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* কনসোল পরিবারের জন্য ডিজাইন করা হয়।

এইসব কন্ট্রোলার Xbox স্টাইল কন্ট্রোলারের মতো MotionEvent পাঠায় তবে সম্পূর্ণভাবে প্রেস করা হলে Switch স্টাইল কন্ট্রোলারের মতো KeyEventও পাঠায় । এইসব কন্ট্রোলার ফেস বোতামের জন্য আলাদা গ্লিফ ব্যবহার করে।

* 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 স্টাইল

আপনি ফার্স্ট-পার্টি কন্ট্রোলার বা জনপ্রিয় থার্ড-পার্টি প্রস্তুতকারকের সাথে পরীক্ষা করতে পারেন, এবং আমরা সাধারণত সবচেয়ে জনপ্রিয় কন্ট্রোলারকে যতটা সম্ভব সংজ্ঞা অনুযায়ী ম্যাপ করি।