ইনপুট SDK দিয়ে শুরু করুন

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

শুরু করার আগে

আপনার গেমে ইনপুট এসডিকে যোগ করার আগে, আপনাকে অবশ্যই আপনার গেম ইঞ্জিনের ইনপুট সিস্টেম ব্যবহার করে কীবোর্ড এবং মাউস ইনপুট সমর্থন করতে হবে।

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

প্রতিটি কন্ট্রোল হলো একটি InputAction (যেমন "জাম্প"-এর জন্য "J") এবং আপনি আপনার InputActions InputGroups সাজান। একটি InputGroup আপনার গেমের একটি ভিন্ন মোডকে উপস্থাপন করতে পারে, যেমন "ড্রাইভিং" বা "ওয়াকিং" বা "মেইন মেনু"। এছাড়াও, গেমের বিভিন্ন পর্যায়ে কোন গ্রুপগুলো সক্রিয় থাকবে তা নির্দেশ করতে আপনি InputContexts ব্যবহার করতে পারেন।

আপনি কিবোর্ড রিম্যাপিং স্বয়ংক্রিয়ভাবে পরিচালনা করার জন্য সক্ষম করতে পারেন, কিন্তু যদি আপনি আপনার নিজস্ব কন্ট্রোল রিম্যাপিং ইন্টারফেস প্রদান করতে পছন্দ করেন, তাহলে আপনি ইনপুট এসডিকে রিম্যাপিং নিষ্ক্রিয় করতে পারেন।

নিম্নলিখিত সিকোয়েন্স ডায়াগ্রামটি বর্ণনা করে যে ইনপুট SDK-এর API কীভাবে কাজ করে:

একটি গেম বাস্তবায়নের সিকোয়েন্স ডায়াগ্রাম যা ইনপুট এসডিকে এপিআই (Input SDK API) কল করে এবং অ্যান্ড্রয়েড ডিভাইসের সাথে এর মিথস্ক্রিয়া।

যখন আপনার গেমে ইনপুট এসডিকে (Input SDK) প্রয়োগ করা হয়, তখন আপনার কন্ট্রোলগুলো পিসির গুগল প্লে গেমস (Google Play Games on PC) ওভারলে-তে প্রদর্শিত হয়।

পিসিতে গুগল প্লে গেমস ওভারলে

পিসিতে গুগল প্লে গেমস ওভারলে ("ওভারলে") আপনার গেমের জন্য নির্ধারিত কন্ট্রোলগুলো প্রদর্শন করে। ব্যবহারকারীরা Shift + Tab চেপে যেকোনো সময় ওভারলেটি অ্যাক্সেস করতে পারেন।

পিসিতে গুগল প্লে গেমসের ওভারলে।

কী বাইন্ডিং ডিজাইন করার সর্বোত্তম পদ্ধতি

আপনার কী বাইন্ডিং ডিজাইন করার সময় নিম্নলিখিত সর্বোত্তম অনুশীলনগুলি বিবেচনা করুন:

  • গেম খেলার সময় কন্ট্রোলগুলো সহজে খুঁজে পেতে ও নেভিগেট করতে আপনার InputActions যৌক্তিকভাবে সম্পর্কিত InputGroups ভাগ করুন।
  • প্রতিটি InputGroup সর্বাধিক একটি InputContext সাথে যুক্ত করুন। একটি সূক্ষ্মভাবে বিভক্ত InputMap ওভারলেতে আপনার কন্ট্রোলগুলো নেভিগেট করার ক্ষেত্রে আরও ভালো অভিজ্ঞতা প্রদান করে।
  • আপনার গেমের প্রতিটি ভিন্ন ধরনের দৃশ্যের জন্য একটি করে InputContext তৈরি করুন। সাধারণত, আপনি আপনার সমস্ত "মেনু-সদৃশ" দৃশ্যের জন্য একটিমাত্র InputContext ব্যবহার করতে পারেন। আপনার গেমের যেকোনো মিনিগেমের জন্য অথবা কোনো একটি দৃশ্যের বিকল্প নিয়ন্ত্রণের জন্য ভিন্ন ভিন্ন InputContexts ব্যবহার করুন।
  • যদি একই InputContext অধীনে একই কী ব্যবহার করার জন্য দুটি অ্যাকশন ডিজাইন করা হয়, তাহলে "Interact / Fire"-এর মতো লেবেল স্ট্রিং ব্যবহার করুন।
  • যদি দুটি কী একই InputAction সাথে যুক্ত করার জন্য ডিজাইন করা হয়, তাহলে আপনার গেমে একই কাজ সম্পাদন করে এমন দুটি ভিন্ন InputActions ব্যবহার করুন। আপনি উভয় InputActions জন্য একই লেবেল স্ট্রিং ব্যবহার করতে পারেন, কিন্তু এর আইডি অবশ্যই ভিন্ন হতে হবে।
  • যদি একাধিক কী-এর একটি সেটে কোনো মডিফায়ার কী প্রয়োগ করা হয়, তবে সেই মডিফায়ার কী-টিকে একত্রিত করে একাধিক InputActions ব্যবহার না করে, শুধুমাত্র সেই কী-টি সহ একটি একক InputAction ব্যবহার করার কথা বিবেচনা করুন (উদাহরণস্বরূপ: Shift + W, Shift + A, Shift + S, Shift + D-এর পরিবর্তে Shift এবং W, A, S, D ব্যবহার করুন)।
  • ব্যবহারকারী যখন টেক্সট ফিল্ডে কিছু লেখে, তখন ইনপুট রিম্যাপিং স্বয়ংক্রিয়ভাবে নিষ্ক্রিয় হয়ে যায়। অ্যান্ড্রয়েড টেক্সট ফিল্ড বাস্তবায়নের জন্য সেরা পদ্ধতিগুলো অনুসরণ করুন, যাতে অ্যান্ড্রয়েড আপনার গেমের টেক্সট ফিল্ডগুলো শনাক্ত করতে পারে এবং রিম্যাপ করা কী-গুলো সেগুলোর কাজে হস্তক্ষেপ করতে না পারে। যদি আপনার গেমে অপ্রচলিত টেক্সট ফিল্ড ব্যবহার করতেই হয়, তবে আপনি ম্যানুয়ালি রিম্যাপিং নিষ্ক্রিয় করার জন্য setInputContext() ফাংশনের সাথে InputContext ব্যবহার করতে পারেন, যেখানে InputGroups এর একটি খালি তালিকা থাকবে।
  • আপনার গেমে রিম্যাপিং সমর্থিত হলে, কী বাইন্ডিং আপডেট করাকে একটি সংবেদনশীল কাজ হিসেবে বিবেচনা করুন, কারণ এটি ব্যবহারকারীর সংরক্ষিত সংস্করণের সাথে সাংঘর্ষিক হতে পারে। সম্ভব হলে বিদ্যমান কন্ট্রোলগুলোর আইডি পরিবর্তন করা থেকে বিরত থাকুন।

রিম্যাপিং বৈশিষ্ট্য

পিসিতে গুগল প্লে গেমস, ইনপুট এসডিকে (Input SDK) ব্যবহার করে আপনার গেমের দেওয়া কী বাইন্ডিংয়ের (key bindings) উপর ভিত্তি করে কিবোর্ড কন্ট্রোল রিম্যাপিং সমর্থন করে। এটি ঐচ্ছিক এবং সম্পূর্ণরূপে নিষ্ক্রিয় করা যেতে পারে। উদাহরণস্বরূপ, আপনি আপনার নিজস্ব কিবোর্ড রিম্যাপিং ইন্টারফেস প্রদান করতে চাইতে পারেন। আপনার গেমের জন্য রিম্যাপিং নিষ্ক্রিয় করতে, আপনাকে কেবল আপনার InputMap জন্য রিম্যাপিং অপশনটি নিষ্ক্রিয় (disabled) করে দিতে হবে (আরও তথ্যের জন্য 'বিল্ড অ্যান ইনপুটম্যাপ' দেখুন)।

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

কী-টি পুনরায় ম্যাপ করার চেষ্টা করুন

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

আপনার গেমে রিম্যাপিং বৈশিষ্ট্যটি সমর্থন করতে, নিম্নলিখিত সীমাবদ্ধতাগুলি এড়িয়ে চলুন:

পুনঃম্যাপিংয়ের সীমাবদ্ধতা

আপনার গেমে রিম্যাপিং বৈশিষ্ট্যগুলি নিষ্ক্রিয় করা যেতে পারে যদি কী বাইন্ডিংগুলিতে নিম্নলিখিত ক্ষেত্রগুলির মধ্যে কোনওটি থাকে:

  • একাধিক কী-এর InputActions যা একটি মডিফায়ার কী এবং একটি নন-মডিফায়ার কী দ্বারা গঠিত নয়। উদাহরণস্বরূপ, Shift + A বৈধ কিন্তু A + B , Ctrl + Alt বা Shift + A + Tab বৈধ নয়।
  • InputMap পুনরাবৃত্ত অনন্য আইডি সহ InputActions , InputGroups বা InputContexts থাকে।

পুনঃমানচিত্রায়নের সীমাবদ্ধতা

রিম্যাপিংয়ের জন্য আপনার কী বাইন্ডিং ডিজাইন করার সময় নিম্নলিখিত সীমাবদ্ধতাগুলো বিবেচনা করুন:

  • কী-কম্বিনেশন রিম্যাপ করা সমর্থিত নয়। উদাহরণস্বরূপ, ব্যবহারকারীরা Shift + A- কে Ctrl + B- তে অথবা A- কে Shift + A- তে রিম্যাপ করতে পারবেন না।
  • মাউস বাটন-ভিত্তিক InputActions জন্য রিম্যাপিং সমর্থিত নয়। উদাহরণস্বরূপ, Shift + Right-click রিম্যাপ করা যায় না।

পিসি এমুলেটরে গুগল প্লে গেমসে কী রিম্যাপিং পরীক্ষা করুন

নিম্নলিখিত adb কমান্ডটি ব্যবহার করে আপনি যেকোনো সময় পিসি এমুলেটরে গুগল প্লে গেমসের রিম্যাপিং ফিচারটি চালু করতে পারেন:

adb shell dumpsys input_mapping_service --set RemappingFlagValue true

নিচের ছবিতে দেখানো অনুযায়ী ওভারলেটি পরিবর্তিত হয়:

কী রিম্যাপিং সক্রিয় করা ওভারলে।

SDK যোগ করুন

আপনার ডেভেলপমেন্ট প্ল্যাটফর্ম অনুযায়ী ইনপুট এসডিকে ইনস্টল করুন।

জাভা এবং কোটলিন

আপনার মডিউল-স্তরের build.gradle ফাইলে একটি ডিপেন্ডেন্সি যোগ করে Java বা Kotlin-এর জন্য Input SDK সংগ্রহ করুন:

dependencies {
  implementation 'com.google.android.libraries.play.games:inputmapping:1.1.1-beta'
  ...
}

ঐক্য

ইনপুট এসডিকে হলো বেশ কিছু নির্ভরতা সহ একটি স্ট্যান্ডার্ড ইউনিটি প্যাকেজ।

সমস্ত নির্ভরতা সহ প্যাকেজটি ইনস্টল করা আবশ্যক। প্যাকেজগুলো ইনস্টল করার বিভিন্ন উপায় রয়েছে।

.unitypackage ইনস্টল করুন

এর সমস্ত নির্ভরতা সহ ইনপুট SDK unitypackage ফাইলটি ডাউনলোড করুনআপনি Assets > Import package > Custom Package নির্বাচন করে এবং আপনার ডাউনলোড করা ফাইলটি খুঁজে বের করে .unitypackage ফাইলটি ইনস্টল করতে পারেন।

UPM ব্যবহার করে ইনস্টল করুন

বিকল্পভাবে, আপনি ইউনিটি প্যাকেজ ম্যানেজার ব্যবহার করে .tgz ফাইলটি ডাউনলোড করে এবং এর নির্ভরতাগুলি ইনস্টল করে প্যাকেজটি ইনস্টল করতে পারেন:

OpenUPM ব্যবহার করে ইনস্টল করুন

আপনি OpenUPM ব্যবহার করে প্যাকেজটি ইনস্টল করতে পারেন।

$ openupm add com.google.android.libraries.play.games.inputmapping

নমুনা গেম

ইনপুট এসডিকে-এর সাথে কীভাবে ইন্টিগ্রেট করতে হয় তার উদাহরণের জন্য, কোটলিন বা জাভা গেমের জন্য এজিডিকে টানেল এবং ইউনিটি গেমের জন্য ট্রিভিয়াল কার্ট দেখুন।

আপনার কী বাইন্ডিং তৈরি করুন

একটি InputMap তৈরি করে এবং একটি InputMappingProvider মাধ্যমে সেটি রিটার্ন করে আপনার কী বাইন্ডিংগুলো রেজিস্টার করুন। নিম্নলিখিত উদাহরণে একটি InputMappingProvider রূপরেখা দেওয়া হলো:

কোটলিন

class InputSDKProvider : InputMappingProvider {
  override fun onProvideInputMap(): InputMap {
    TODO("Not yet implemented")
  }
}

জাভা

public class InputSDKProvider implements InputMappingProvider {
    private static final String INPUTMAP_VERSION = "1.0.0";

    @Override
    @NonNull
    public InputMap onProvideInputMap() {
        // TODO: return an InputMap
    }
}

সি#

#if PLAY_GAMES_PC
using Java.Lang;
using Java.Util;
using Google.Android.Libraries.Play.Games.Inputmapping;
using Google.Android.Libraries.Play.Games.Inputmapping.Datamodel;

public class InputSDKProvider : InputMappingProviderCallbackHelper
{
    public static readonly string INPUT_MAP_VERSION = "1.0.0";

    public override InputMap OnProvideInputMap()
    {
        // TODO: return an InputMap
    }
}
#endif

আপনার ইনপুট ক্রিয়াগুলি সংজ্ঞায়িত করুন

InputAction ক্লাসটি কোনো কী বা কী-কম্বিনেশনকে একটি গেম অ্যাকশনের সাথে যুক্ত করতে ব্যবহৃত হয়। সমস্ত InputActions InputActions অনন্য আইডি থাকতে হবে।

আপনি যদি রিম্যাপিং সমর্থন করেন, তবে কোন InputActions রিম্যাপ করা যাবে তা আপনি নির্ধারণ করতে পারেন। যদি আপনার গেম রিম্যাপিং সমর্থন না করে, তবে আপনার সমস্ত InputActions জন্য রিম্যাপিং অপশনটি নিষ্ক্রিয় করে রাখা উচিত, কিন্তু আপনি যদি আপনার InputMap এটি সমর্থন না করেন, তবে ইনপুট এসডিকে স্বয়ংক্রিয়ভাবে রিম্যাপিং বন্ধ করে দেওয়ার মতো যথেষ্ট বুদ্ধিমান।

এই উদাহরণটি ম্যাপ করে স্থান ড্রাইভ অ্যাকশনের চাবিকাঠি।

কোটলিন

companion object {
  private val driveInputAction = InputAction.create(
    "Drive",
    InputActionsIds.DRIVE.ordinal.toLong(),
    InputControls.create(listOf(KeyEvent.KEYCODE_SPACE), emptyList()),
    InputEnums.REMAP_OPTION_ENABLED)
}

জাভা

private static final InputAction driveInputAction = InputAction.create(
    "Drive",
    InputEventIds.DRIVE.ordinal(),
    InputControls.create(
            Collections.singletonList(KeyEvent.KEYCODE_SPACE),
            Collections.emptyList()),
    InputEnums.REMAP_OPTION_ENABLED
);

সি#

private static readonly InputAction driveInputAction = InputAction.Create(
    "Drive",
    (long)InputEventIds.DRIVE,
    InputControls.Create(
        new[] { new Integer(AndroidKeyCode.KEYCODE_SPACE) }.ToJavaList(),
        new ArrayList<Integer>()),
    InputEnums.REMAP_OPTION_ENABLED
);

ওভারলেতে প্রদর্শিত একক কী ইনপুট অ্যাকশন।

অ্যাকশনগুলো মাউস ইনপুটও উপস্থাপন করতে পারে। এই উদাহরণে লেফট-ক্লিককে মুভ অ্যাকশন হিসেবে সেট করা হয়েছে:

কোটলিন

companion object {
  private val mouseInputAction = InputAction.create(
    "Move",
    InputActionsIds.MOUSE_MOVEMENT.ordinal.toLong(),
    InputControls.create(emptyList(), listOf(InputControls.MOUSE_LEFT_CLICK)),
    InputEnums.REMAP_OPTION_DISABLED)
}

জাভা

private static final InputAction mouseInputAction = InputAction.create(
    "Move",
    InputActionsIds.MOUSE_MOVEMENT.ordinal(),
    InputControls.create(
            Collections.emptyList(),
            Collections.singletonList(InputControls.MOUSE_LEFT_CLICK)
    ),
    InputEnums.REMAP_OPTION_DISABLED
);

সি#

private static readonly InputAction mouseInputAction = InputAction.Create(
    "Move",
    (long)InputEventIds.MOUSE_MOVEMENT,
    InputControls.Create(
        new ArrayList<Integer>(),
        new[] { new Integer((int)PlayMouseAction.MouseLeftClick) }.ToJavaList()
    ),
    InputEnums.REMAP_OPTION_DISABLED
);

ওভারলেতে মাউস ইনপুট অ্যাকশন প্রদর্শিত হয়।

আপনার InputAction এ একাধিক কী কোড পাস করার মাধ্যমে কী-কম্বিনেশন নির্দিষ্ট করা হয়। এই উদাহরণে স্থান + স্থানান্তর টার্বো অ্যাকশনের সাথে ম্যাপ করা আছে, যা এমনকি যখন কাজ করে মহাকাশ ড্রাইভ -এ ম্যাপ করা আছে।

কোটলিন

companion object {
  private val turboInputAction = InputAction.create(
    "Turbo",
    InputActionsIds.TURBO.ordinal.toLong(),
    InputControls.create(
      listOf(KeyEvent.KEYCODE_SHIFT_LEFT, KeyEvent.KEYCODE_SPACE),
      emptyList()),
    InputEnums.REMAP_OPTION_ENABLED)
}

জাভা

private static final InputAction turboInputAction = InputAction.create(
    "Turbo",
    InputActionsIds.TURBO.ordinal(),
    InputControls.create(
            Arrays.asList(KeyEvent.KEYCODE_SHIFT_LEFT, KeyEvent.KEYCODE_SPACE),
            Collections.emptyList()
    ),
    InputEnums.REMAP_OPTION_ENABLED
);

সি#

private static readonly InputAction turboInputAction = InputAction.Create(
    "Turbo",
    (long)InputEventIds.TURBO,
    InputControls.Create(
        new[]
        {
            new Integer(AndroidKeyCode.KEYCODE_SHIFT_LEFT),
            new Integer(AndroidKeyCode.KEYCODE_SPACE)
        }.ToJavaList(),
        new ArrayList<Integer>()),
    InputEnums.REMAP_OPTION_ENABLED
);

ওভারলেতে একাধিক কী-এর ইনপুট অ্যাকশন প্রদর্শিত হয়েছে।

ইনপুট এসডিকে আপনাকে একটিমাত্র কাজের জন্য মাউস এবং কী-বোর্ডের বাটন একসাথে ব্যবহার করার সুযোগ দেয়। এই উদাহরণটি তা নির্দেশ করে। শিফট এবং এই নমুনা গেমটিতে একসাথে রাইট-ক্লিক চাপলে একটি ওয়েপয়েন্ট যুক্ত হয়:

কোটলিন

companion object {
  private val addWaypointInputAction = InputAction.create(
    "Add waypoint",
    InputActionsIds.ADD_WAYPOINT.ordinal.toLong(),
    InputControls.create(
      listOf(KeyEvent.KeyEvent.KEYCODE_TAB),
      listOf(InputControls.MOUSE_RIGHT_CLICK)),
    InputEnums.REMAP_OPTION_DISABLED)
}

জাভা

private static final InputAction addWaypointInputAction = InputAction.create(
    "Add waypoint",
    InputActionsIds.ADD_WAYPOINT.ordinal(),
    InputControls.create(
            Collections.singletonList(KeyEvent.KEYCODE_TAB),
            Collections.singletonList(InputControls.MOUSE_RIGHT_CLICK)
    ),
    InputEnums.REMAP_OPTION_DISABLED
);

সি#

private static readonly InputAction addWaypointInputAction = InputAction.Create(
    "Add waypoint",
    (long)InputEventIds.ADD_WAYPOINT,
    InputControls.Create(
        new[] { new Integer(AndroidKeyCode.KEYCODE_SPACE) }.ToJavaList(),
        new[] { new Integer((int)PlayMouseAction.MouseRightClick) }.ToJavaList()
    ),
    InputEnums.REMAP_OPTION_DISABLED
);

কী-বোর্ড ও মাউসের সম্মিলিত ইনপুট অ্যাকশন ওভারলে-তে প্রদর্শিত হয়।

InputAction-এর নিম্নলিখিত ফিল্ডগুলো রয়েছে:

  • ActionLabel : এই অ্যাকশনটি বোঝানোর জন্য UI-তে প্রদর্শিত স্ট্রিং। স্থানীয়করণ স্বয়ংক্রিয়ভাবে করা হয় না, তাই আগে থেকেই যেকোনো স্থানীয়করণ করে নিন।
  • InputControls : এই অ্যাকশনটি যে ইনপুট কন্ট্রোলগুলো ব্যবহার করে তা নির্ধারণ করে। কন্ট্রোলগুলো ওভারলে-এর সামঞ্জস্যপূর্ণ গ্লিফগুলোর সাথে ম্যাপ করা থাকে।
  • InputActionId : InputIdentifier অবজেক্ট যা InputAction এর নম্বর আইডি এবং ভার্সন সংরক্ষণ করে (আরও তথ্যের জন্য ট্র্যাকিং কী আইডি দেখুন)।
  • InputRemappingOption : InputEnums.REMAP_OPTION_ENABLED অথবা InputEnums.REMAP_OPTION_DISABLED এর মধ্যে একটি। এটি নির্ধারণ করে যে অ্যাকশনটি রিম্যাপ করার জন্য সক্ষম কিনা। যদি আপনার গেম রিম্যাপিং সমর্থন না করে, তবে আপনি এই ফিল্ডটি এড়িয়ে যেতে পারেন অথবা এটিকে কেবল 'disabled' সেট করতে পারেন।
  • RemappedInputControls : একটি পঠনযোগ্য (read-only) InputControls অবজেক্ট, যা রিম্যাপিং ইভেন্টের সময় ব্যবহারকারী কর্তৃক সেট করা রিম্যাপ করা কী (key) পড়ার জন্য ব্যবহৃত হয় ( রিম্যাপিং ইভেন্টের বিজ্ঞপ্তি পাওয়ার জন্য এটি ব্যবহৃত হয়)।

InputControls কোনো একটি অ্যাকশনের সাথে সংশ্লিষ্ট ইনপুটগুলোকে উপস্থাপন করে এবং এতে নিম্নলিখিত ফিল্ডগুলো থাকে:

  • AndroidKeycodes : হলো পূর্ণসংখ্যার একটি তালিকা যা কোনো একটি অ্যাকশনের সাথে যুক্ত কীবোর্ড ইনপুটগুলোকে নির্দেশ করে। এগুলো KeyEvent ক্লাসে অথবা ইউনিটির জন্য AndroidKeycode ক্লাসে সংজ্ঞায়িত করা হয়।
  • MouseActions : হলো MouseAction ভ্যালুগুলোর একটি তালিকা, যা এই অ্যাকশনের সাথে সংশ্লিষ্ট মাউস ইনপুটগুলোকে উপস্থাপন করে।

আপনার ইনপুট গ্রুপগুলি সংজ্ঞায়িত করুন

ওভারলেতে নেভিগেশন এবং কন্ট্রোলগুলো সহজে খুঁজে পাওয়ার সুবিধা বাড়াতে, InputActions InputGroups ব্যবহার করে যৌক্তিকভাবে সম্পর্কিত অ্যাকশনগুলোর সাথে দলবদ্ধ করা হয়। আপনার গেমের সমস্ত InputGroups মধ্যে প্রতিটি InputGroup আইডি অবশ্যই অনন্য হতে হবে।

আপনার ইনপুট অ্যাকশনগুলোকে বিভিন্ন গ্রুপে ভাগ করার মাধ্যমে আপনি একজন খেলোয়াড়ের জন্য তার বর্তমান পরিস্থিতির জন্য সঠিক কী বাইন্ডিং খুঁজে পাওয়া সহজ করে তোলেন।

আপনি যদি রিম্যাপিং সমর্থন করেন, তবে কোন InputGroups রিম্যাপ করা যাবে তা আপনি নির্ধারণ করতে পারেন। যদি আপনার গেম রিম্যাপিং সমর্থন না করে, তবে আপনার সমস্ত InputGroups জন্য রিম্যাপিং অপশনটি নিষ্ক্রিয় করে রাখা উচিত, কিন্তু আপনি যদি আপনার InputMap এটি সমর্থন না করেন, তবে ইনপুট এসডিকে স্বয়ংক্রিয়ভাবে রিম্যাপিং বন্ধ করে দেওয়ার জন্য যথেষ্ট বুদ্ধিমান।

কোটলিন

companion object {
  private val menuInputGroup = InputGroup.create(
    "Menu keys",
    listOf(
      navigateUpInputAction,
      navigateLeftInputAction,
      navigateDownInputAction,
      navigateRightInputAction,
      openMenuInputAction,
      returnMenuInputAction),
    InputGroupsIds.MENU_ACTION_KEYS.ordinal.toLong(),
    InputEnums.REMAP_OPTION_ENABLED
  )
}

জাভা

private static final InputGroup menuInputGroup = InputGroup.create(
    "Menu keys",
    Arrays.asList(
           navigateUpInputAction,
           navigateLeftInputAction,
           navigateDownInputAction,
           navigateRightInputAction,
           openMenuInputAction,
           returnMenuInputAction),
    InputGroupsIds.MENU_ACTION_KEYS.ordinal(),
    REMAP_OPTION_ENABLED
);

সি#

private static readonly InputGroup menuInputGroup = InputGroup.Create(
    "Menu keys",
    new[]
    {
        navigateUpInputAction,
        navigateLeftInputAction,
        navigateDownInputAction,
        navigateRightInputAction,
        openMenuInputAction,
        returnMenuInputAction,
    }.ToJavaList(),
    (long)InputGroupsIds.MENU_ACTION_KEYS,
    InputEnums.REMAP_OPTION_ENABLED
);

নিম্নলিখিত উদাহরণটি ওভারলেতে রোড কন্ট্রোল এবং মেনু কন্ট্রোল ইনপুট গ্রুপগুলি প্রদর্শন করে:

ওভারলেটি একটি ইনপুটম্যাপ প্রদর্শন করছে, যেটিতে রোড কন্ট্রোল এবং মেনু কন্ট্রোল ইনপুট গ্রুপগুলো রয়েছে।

InputGroup নিম্নলিখিত ফিল্ডগুলো রয়েছে:

  • GroupLabel : ওভারলেতে প্রদর্শিত হওয়ার জন্য একটি স্ট্রিং, যা একাধিক অ্যাকশনকে যৌক্তিকভাবে শ্রেণিবদ্ধ করতে ব্যবহার করা যেতে পারে। এই স্ট্রিংটি স্বয়ংক্রিয়ভাবে স্থানীয়করণ করা হয় না।
  • InputActions : পূর্ববর্তী ধাপে আপনার সংজ্ঞায়িত করা InputAction অবজেক্টগুলোর একটি তালিকা। এই সমস্ত অ্যাকশন গ্রুপ হেডিং-এর অধীনে দৃশ্যমানভাবে প্রদর্শিত হয়।
  • InputGroupId : InputIdentifier অবজেক্ট যা InputGroup এর নম্বর আইডি এবং সংস্করণ সংরক্ষণ করে। আরও তথ্যের জন্য ট্র্যাকিং কী আইডি (Tracking Key IDs) দেখুন।
  • InputRemappingOption : InputEnums.REMAP_OPTION_ENABLED অথবা InputEnums.REMAP_OPTION_DISABLED এর মধ্যে একটি। যদি এটি ডিজেবল করা থাকে, তবে এই গ্রুপের অন্তর্গত সমস্ত InputAction অবজেক্টের রিম্যাপিং ডিজেবল থাকবে, এমনকি যদি তারা তাদের রিম্যাপিং অপশন এনেবল করা আছে বলেও উল্লেখ করে। যদি এটি এনেবল করা থাকে, তবে এই গ্রুপের অন্তর্গত সমস্ত অ্যাকশন রিম্যাপযোগ্য হবে, যদি না স্বতন্ত্র অ্যাকশনগুলো দ্বারা ডিজেবল করা আছে বলে উল্লেখ করা হয়।

আপনার ইনপুট প্রসঙ্গগুলি সংজ্ঞায়িত করুন

InputContexts আপনার গেমকে বিভিন্ন দৃশ্যের জন্য ভিন্ন ভিন্ন কিবোর্ড নিয়ন্ত্রণ ব্যবহার করার সুযোগ দেয়। উদাহরণস্বরূপ:

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

InputContexts ব্যবহার করার সময়, ওভারলে প্রথমে ব্যবহৃত কনটেক্সটের গ্রুপগুলো দেখায়। এই আচরণটি সক্রিয় করতে, যখনই আপনার গেম একটি ভিন্ন দৃশ্যে প্রবেশ করে, তখন কনটেক্সট সেট করার জন্য setInputContext() কল করুন। নিম্নলিখিত চিত্রটি এই আচরণটি প্রদর্শন করে: 'ড্রাইভিং' দৃশ্যে, রোড কন্ট্রোল অ্যাকশনগুলো ওভারলের শীর্ষে দেখানো হয়। 'স্টোর' মেনু খোলার সময়, 'মেনু কন্ট্রোল' অ্যাকশনগুলো ওভারলের শীর্ষে প্রদর্শিত হয়।

ওভারলেতে ইনপুটকনটেক্সট গ্রুপগুলোকে সাজাচ্ছে।

আপনার গেমের বিভিন্ন পর্যায়ে ভিন্ন ভিন্ন InputContext সেট করার মাধ্যমে এই ওভারলে আপডেটগুলো সম্পন্ন করা হয়। এটি করতে:

  1. InputGroups ব্যবহার করে আপনার InputActions যৌক্তিকভাবে সম্পর্কিত অ্যাকশনগুলোর সাথে গ্রুপ করুন।
  2. আপনার গেমের বিভিন্ন অংশের জন্য এই InputGroups একটি InputContext এ বরাদ্দ করুন।

একই InputContext অন্তর্গত InputGroups গুলিতে একই key ব্যবহৃত হলে পরস্পরবিরোধী InputActions থাকতে পারে না। প্রতিটি InputGroup একটিমাত্র InputContext এ বরাদ্দ করা একটি উত্তম অভ্যাস।

নিম্নলিখিত নমুনা কোডটি InputContext লজিক প্রদর্শন করে:

কোটলিন

companion object {
  val menuSceneInputContext = InputContext.create(
    "Menu",
    InputIdentifier.create(
      INPUTMAP_VERSION,
      InputContextIds.MENU_SCENE.ordinal.toLong()),
    listOf(basicMenuNavigationInputGroup, menuActionsInputGroup))

  val gameSceneInputContext = InputContext.create(
    "Game",
    InputIdentifier.create(
      INPUTMAP_VERSION,
      InputContextIds.GAME_SCENE.ordinal.toLong()),
    listOf(
      movementInputGroup,
      mouseActionsInputGroup,
      emojisInputGroup,
      gameActionsInputGroup))
}

জাভা

public static final InputContext menuSceneInputContext = InputContext.create(
        "Menu",
        InputIdentifier.create(
                INPUTMAP_VERSION,
                InputContextIds.MENU_SCENE.ordinal()),
        Arrays.asList(
                basicMenuNavigationInputGroup,
                menuActionsInputGroup
        )
);

public static final InputContext gameSceneInputContext = InputContext.create(
        "Game",
        InputIdentifier.create(
                INPUTMAP_VERSION,
                InputContextIds.GAME_SCENE.ordinal()),
        Arrays.asList(
                movementInputGroup,
                mouseActionsInputGroup,
                emojisInputGroup,
                gameActionsInputGroup
        )
);

সি#

public static readonly InputContext menuSceneInputContext = InputContext.Create(
    "Menu",
    InputIdentifier.Create(
        INPUT_MAP_VERSION,
        (long)InputContextsIds.MENU_SCENE),
    new[]
    {
        basicMenuNavigationInputGroup,
        menuActionsInputGroup
    }.ToJavaList()
);

public static readonly InputContext gameSceneInputContext = InputContext.Create(
    "Game",
    InputIdentifier.Create(
        INPUT_MAP_VERSION,
        (long)InputContextsIds.GAME_SCENE),
    new[]
    {
        movementInputGroup,
        mouseActionsInputGroup,
        emojisInputGroup,
        gameActionsInputGroup
    }.ToJavaList()
);

InputContext নিম্নলিখিত ফিল্ডগুলো রয়েছে:

  • LocalizedContextLabel : একটি স্ট্রিং যা কনটেক্সটের অন্তর্গত গ্রুপগুলোকে বর্ণনা করে।
  • InputContextId : InputIdentifier অবজেক্ট যা InputContext এর নম্বর আইডি এবং সংস্করণ সংরক্ষণ করে (আরও তথ্যের জন্য ট্র্যাকিং কী আইডি দেখুন)।
  • ActiveGroups : InputGroups একটি তালিকা, যা এই কনটেক্সটটি সক্রিয় থাকাকালীন ওভারলে-র শীর্ষে ব্যবহৃত ও প্রদর্শিত হবে।

একটি ইনপুট ম্যাপ তৈরি করুন

একটি InputMap হলো একটি গেমে উপলব্ধ সমস্ত InputGroup অবজেক্টের একটি সংগ্রহ, এবং সেই সূত্রে একজন খেলোয়াড় যে সমস্ত InputAction অবজেক্ট সম্পাদন করতে পারে, তার সবই এর অন্তর্ভুক্ত।

আপনার কী বাইন্ডিংগুলো রিপোর্ট করার সময়, আপনি আপনার গেমে ব্যবহৃত সমস্ত InputGroups দিয়ে একটি InputMap তৈরি করেন।

যদি আপনার গেমে রিম্যাপিং সাপোর্ট না থাকে, তাহলে রিম্যাপিং অপশনটি ডিজেবল করে দিন এবং সংরক্ষিত কী-গুলো খালি রাখুন।

নিম্নলিখিত উদাহরণটি InputGroups এর একটি সংগ্রহ রিপোর্ট করার জন্য ব্যবহৃত একটি InputMap তৈরি করে।

কোটলিন

companion object {
  val gameInputMap = InputMap.create(
    listOf(
      basicMenuNavigationInputGroup,
      menuActionKeysInputGroup,
      movementInputGroup,
      mouseMovementInputGroup,
      pauseMenuInputGroup),
    MouseSettings.create(true, false),
    InputIdentifier.create(INPUTMAP_VERSION, INPUT_MAP_ID.toLong()),
    InputEnums.REMAP_OPTION_ENABLED,
    // Use ESCAPE as reserved remapping key
    listof(InputControls.create(listOf(KeyEvent.KEYCODE_ESCAPE), emptyList()))
  )
}

জাভা

public static final InputMap gameInputMap = InputMap.create(
        Arrays.asList(
                basicMenuNavigationInputGroup,
                menuActionKeysInputGroup,
                movementInputGroup,
                mouseMovementInputGroup,
                pauseMenuInputGroup),
        MouseSettings.create(true, false),
        InputIdentifier.create(INPUTMAP_VERSION, INPUT_MAP_ID),
        REMAP_OPTION_ENABLED,
        // Use ESCAPE as reserved remapping key
        Arrays.asList(
                InputControls.create(
                        Collections.singletonList(KeyEvent.KEYCODE_ESCAPE),
                        Collections.emptyList()
                )
        )
);

সি#

public static readonly InputMap gameInputMap = InputMap.Create(
    new[]
    {
        basicMenuNavigationInputGroup,
        menuActionKeysInputGroup,
        movementInputGroup,
        mouseMovementInputGroup,
        pauseMenuInputGroup,
    }.ToJavaList(),
    MouseSettings.Create(true, false),
    InputIdentifier.Create(INPUT_MAP_VERSION, INPUT_MAP_ID),
    InputEnums.REMAP_OPTION_ENABLED,
    // Use ESCAPE as reserved remapping key
    new[]
    {
        InputControls.Create(
            New[] {
            new Integer(AndroidKeyCode.KEYCODE_ESCAPE)
        }.ToJavaList(),
        new ArrayList<Integer>())
    }.ToJavaList()
);

InputMap নিম্নলিখিত ফিল্ডগুলো রয়েছে:

  • InputGroups : আপনার গেম দ্বারা রিপোর্ট করা ইনপুটগ্রুপসমূহ। গ্রুপগুলো ওভারলেতে ক্রমানুসারে প্রদর্শিত হয়, যদি না setInputContext() কল করে বর্তমানে ব্যবহৃত গ্রুপগুলো নির্দিষ্ট করা হয়।
  • MouseSettings : MouseSettings অবজেক্টটি নির্দেশ করে যে মাউসের সংবেদনশীলতা সামঞ্জস্য করা যেতে পারে এবং মাউসটি y-অক্ষে উল্টানো থাকে।
  • InputMapId : InputIdentifier অবজেক্ট যা InputMap এর নম্বর আইডি এবং সংস্করণ সংরক্ষণ করে (আরও তথ্যের জন্য ট্র্যাকিং কী আইডি দেখুন)।
  • InputRemappingOption : InputEnums.REMAP_OPTION_ENABLED অথবা InputEnums.REMAP_OPTION_DISABLED এর মধ্যে একটি। এটি নির্ধারণ করে যে রিম্যাপিং বৈশিষ্ট্যটি সক্রিয় আছে কিনা।
  • ReservedControls : InputControls একটি তালিকা যেগুলোতে ব্যবহারকারীরা রিম্যাপ করতে পারবেন না।

কী আইডি ট্র্যাক করুন

InputAction , InputGroup , InputContext এবং InputMap অবজেক্টগুলিতে একটি InputIdentifier অবজেক্ট থাকে যা একটি অনন্য সংখ্যাসূচক আইডি এবং একটি স্ট্রিং সংস্করণ আইডি সংরক্ষণ করে। আপনার অবজেক্টগুলির স্ট্রিং সংস্করণ ট্র্যাক করা ঐচ্ছিক, তবে আপনার InputMap এর সংস্করণগুলি ট্র্যাক করার জন্য এটি সুপারিশ করা হয়। যদি স্ট্রিং সংস্করণ প্রদান করা না হয়, তবে স্ট্রিংটি খালি থাকে। InputMap অবজেক্টের জন্য একটি স্ট্রিং সংস্করণ আবশ্যক।

নিম্নলিখিত উদাহরণটি InputActions বা InputGroups এ একটি স্ট্রিং সংস্করণ নির্ধারণ করে:

কোটলিন

class InputSDKProviderKotlin : InputMappingProvider {
  companion object {
    const val INPUTMAP_VERSION = "1.0.0"
    private val enterMenuInputAction = InputAction.create(
      "Enter menu",
      InputControls.create(listOf(KeyEvent.KEYCODE_ENTER), emptyList()),
      InputIdentifier.create(
        INPUTMAP_VERSION, InputActionsIds.ENTER_MENU.ordinal.toLong()),
      InputEnums.REMAP_OPTION_ENABLED
    )

    private val movementInputGroup  = InputGroup.create(
      "Basic movement",
      listOf(
        moveUpInputAction,
        moveLeftInputAction,
        moveDownInputAction,
        mouseGameInputAction),
      InputIdentifier.create(
        INPUTMAP_VERSION, InputGroupsIds.BASIC_MOVEMENT.ordinal.toLong()),
      InputEnums.REMAP_OPTION_ENABLED)
  }
}

জাভা

public class InputSDKProvider implements InputMappingProvider {
    public static final String INPUTMAP_VERSION = "1.0.0";

    private static final InputAction enterMenuInputAction = InputAction.create(
            "Enter menu",
            InputControls.create(
                    Collections.singletonList(KeyEvent.KEYCODE_ENTER),
                    Collections.emptyList()),
            InputIdentifier.create(
                    INPUTMAP_VERSION, InputActionsIds.ENTER_MENU.ordinal()),
            InputEnums.REMAP_OPTION_ENABLED
    );

    private static final InputGroup movementInputGroup = InputGroup.create(
            "Basic movement",
            Arrays.asList(
                    moveUpInputAction,
                    moveLeftInputAction,
                    moveDownInputAction,
                    moveRightInputAction,
                    mouseGameInputAction
            ),
            InputIdentifier.create(
                    INPUTMAP_VERSION, InputGroupsIds.BASIC_MOVEMENT.ordinal()),
            InputEnums.REMAP_OPTION_ENABLED
    );
}

সি#

#if PLAY_GAMES_PC

using Java.Lang;
using Java.Util;
using Google.Android.Libraries.Play.Games.Inputmapping;
using Google.Android.Libraries.Play.Games.Inputmapping.Datamodel;

public class InputSDKMappingProvider : InputMappingProviderCallbackHelper
{
    public static readonly string INPUT_MAP_VERSION = "1.0.0";

    private static readonly InputAction enterMenuInputAction =
        InputAction.Create(
            "Enter menu",
            InputControls.Create(
                new[] { new Integer(AndroidKeyCode.KEYCODE_SPACE)}.ToJavaList(),
                new ArrayList<Integer>()),
            InputIdentifier.Create(
                INPUT_MAP_VERSION,
                (long)InputEventIds.ENTER_MENU),
            InputEnums.REMAP_OPTION_ENABLED
        );

    private static readonly InputGroup movementInputGroup = InputGroup.Create(
        "Basic movement",
        new[]
        {
            moveUpInputAction,
            moveLeftInputAction,
            moveDownInputAction,
            moveRightInputAction,
            mouseGameInputAction
        }.ToJavaList(),
        InputIdentifier.Create(
            INPUT_MAP_VERSION,
            (long)InputGroupsIds.BASIC_MOVEMENT),
        InputEnums.REMAP_OPTION_ENABLED
    );
}
#endif

আপনার InputMap সমস্ত InputActions মধ্যে InputAction অবজেক্টের নম্বর আইডি অবশ্যই অনন্য হতে হবে। একইভাবে, একটি InputMap এর সমস্ত InputGroups মধ্যে InputGroup অবজেক্টের আইডি অবশ্যই অনন্য হতে হবে। নিম্নলিখিত নমুনাটি দেখায় কিভাবে আপনার অবজেক্টের অনন্য আইডিগুলি ট্র্যাক করতে একটি enum ব্যবহার করতে হয়:

কোটলিন

enum class InputActionsIds {
    NAVIGATE_UP,
    NAVIGATE_DOWN,
    ENTER_MENU,
    EXIT_MENU,
    // ...
    JUMP,
    RUN,
    EMOJI_1,
    EMOJI_2,
    // ...
}

enum class InputGroupsIds {
    // Main menu scene
    BASIC_NAVIGATION, // WASD, Enter, Backspace
    MENU_ACTIONS, // C: chat, Space: quick game, S: store
    // Gameplay scene
    BASIC_MOVEMENT, // WASD, space: jump, Shift: run
    MOUSE_ACTIONS, // Left click: shoot, Right click: aim
    EMOJIS, // Emojis with keys 1,2,3,4 and 5
    GAME_ACTIONS, // M: map, P: pause, R: reload
}

enum class InputContextIds {
    MENU_SCENE, // Basic menu navigation, menu actions
    GAME_SCENE, // Basic movement, mouse actions, emojis, game actions
}

const val INPUT_MAP_ID = 0

জাভা

public enum InputActionsIds {
    NAVIGATE_UP,
    NAVIGATE_DOWN,
    ENTER_MENU,
    EXIT_MENU,
    // ...
    JUMP,
    RUN,
    EMOJI_1,
    EMOJI_2,
    // ...
}

public enum InputGroupsIds {
    // Main menu scene
    BASIC_NAVIGATION, // WASD, Enter, Backspace
    MENU_ACTIONS, // C: chat, Space: quick game, S: store
    // Gameplay scene
    BASIC_MOVEMENT, // WASD, space: jump, Shift: run
    MOUSE_ACTIONS, // Left click: shoot, Right click: aim
    EMOJIS, // Emojis with keys 1,2,3,4 and 5
    GAME_ACTIONS, // M: map, P: pause, R: reload
}

public enum InputContextIds {
    MENU_SCENE, // Basic navigation, menu actions
    GAME_SCENE, // Basic movement, mouse actions, emojis, game actions
}

public static final long INPUT_MAP_ID = 0;

সি#

public enum InputActionsIds
{
    NAVIGATE_UP,
    NAVIGATE_DOWN,
    ENTER_MENU,
    EXIT_MENU,
    // ...
    JUMP,
    RUN,
    EMOJI_1,
    EMOJI_2,
    // ...
}

public enum InputGroupsIds
{
    // Main menu scene
    BASIC_NAVIGATION, // WASD, Enter, Backspace
    MENU_ACTIONS, // C: chat, Space: quick game, S: store
    // Gameplay scene
    BASIC_MOVEMENT, // WASD, space: jump, Shift: run
    MOUSE_ACTIONS, // Left click: shoot, Right click: aim
    EMOJIS, // Emojis with keys 1,2,3,4 and 5
    GAME_ACTIONS, // M: map, P: pause, R: reload
}

public enum InputContextIds
{
    MENU_SCENE, // Basic navigation, menu actions
    GAME_SCENE, // Basic movement, mouse actions, emojis, game actions
}

public static readonly long INPUT_MAP_ID = 0;

InputIdentifier নিম্নলিখিত ফিল্ডগুলো রয়েছে:

  • UniqueId : প্রদত্ত ইনপুট ডেটার কোনো সেটকে স্বতন্ত্রভাবে সুস্পষ্টভাবে শনাক্ত করার জন্য সেট করা একটি অনন্য সংখ্যাসূচক আইডি।
  • VersionString : ইনপুট ডেটার দুটি পরিবর্তনের সংস্করণের মধ্যে একটি সংস্করণ শনাক্ত করার জন্য সেট করা একটি পাঠযোগ্য সংস্করণ স্ট্রিং।

রিম্যাপিং ইভেন্ট সম্পর্কে বিজ্ঞপ্তি পান (ঐচ্ছিক)

আপনার গেমে ব্যবহৃত কী-গুলো সম্পর্কে জানতে রিম্যাপ ইভেন্টের নোটিফিকেশন গ্রহণ করুন। এর ফলে, অ্যাকশন কন্ট্রোল প্রদর্শনের জন্য ব্যবহৃত গেম স্ক্রিনের অ্যাসেটগুলো আপনার গেম আপডেট করতে পারে।

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

InputRemappingListener কলব্যাক ব্যবহার করে UI রিম্যাপিং ইভেন্টগুলিতে প্রতিক্রিয়া জানাচ্ছে।

এই কার্যকারিতাটি একটি InputRemappingListener কলব্যাক নিবন্ধন করার মাধ্যমে সম্পন্ন করা হয়। এই বৈশিষ্ট্যটি বাস্তবায়ন করতে, প্রথমে একটি InputRemappingListener ইনস্ট্যান্স নিবন্ধন করুন:

কোটলিন

class InputSDKRemappingListener : InputRemappingListener {
  override fun onInputMapChanged(inputMap: InputMap) {
    Log.i(TAG, "Received update on input map changed.")
    if (inputMap.inputRemappingOption() == InputEnums.REMAP_OPTION_DISABLED) {
      return
    }
    for (inputGroup in inputMap.inputGroups()) {
      if (inputGroup.inputRemappingOption() == InputEnums.REMAP_OPTION_DISABLED) {
        continue
      }
      for (inputAction in inputGroup.inputActions()) {
        if (inputAction.inputRemappingOption() != InputEnums.REMAP_OPTION_DISABLED) {
          // Found InputAction remapped by user
          processRemappedAction(inputAction)
        }
      }
    }
  }

  private fun processRemappedAction(remappedInputAction: InputAction) {
    // Get remapped action info
    val remappedControls = remappedInputAction.remappedInputControls()
    val remappedKeyCodes = remappedControls.keycodes()
    val mouseActions = remappedControls.mouseActions()
    val version = remappedInputAction.inputActionId().versionString()
    val remappedActionId = remappedInputAction.inputActionId().uniqueId()
    val currentInputAction: Optional<InputAction>
    currentInputAction = if (version == null || version.isEmpty()
      || version == InputSDKProvider.INPUTMAP_VERSION
    ) {
      getCurrentVersionInputAction(remappedActionId)
    } else {
      Log.i(TAG,
            "Detected version of user-saved input action defers from current version")
      getCurrentVersionInputActionFromPreviousVersion(
        remappedActionId, version)
    }
    if (!currentInputAction.isPresent) {
      Log.e(TAG, String.format(
        "can't find remapped input action with id %d and version %s",
        remappedActionId, if (version == null || version.isEmpty()) "UNKNOWN" else version))
      return
    }
    val originalControls = currentInputAction.get().inputControls()
    val originalKeyCodes = originalControls.keycodes()
    Log.i(TAG, String.format(
      "Found input action with id %d remapped from key %s to key %s",
      remappedActionId,
      keyCodesToString(originalKeyCodes),
      keyCodesToString(remappedKeyCodes)))

    // TODO: make display changes to match controls used by the user
  }

  private fun getCurrentVersionInputAction(inputActionId: Long): Optional<InputAction> {
    for (inputGroup in InputSDKProvider.gameInputMap.inputGroups()) {
      for (inputAction in inputGroup.inputActions()) {
        if (inputAction.inputActionId().uniqueId() == inputActionId) {
          return Optional.of(inputAction)
        }
      }
    }
    return Optional.empty()
  }

  private fun getCurrentVersionInputActionFromPreviousVersion(
    inputActionId: Long, previousVersion: String
  ): Optional<InputAction7gt; {
    // TODO: add logic to this method considering the diff between the current and previous
    //  InputMap.
    return Optional.empty()
  }

  private fun keyCodesToString(keyCodes: List<Int>): String {
    val builder = StringBuilder()
    for (keyCode in keyCodes) {
      if (!builder.toString().isEmpty()) {
        builder.append(" + ")
      }
      builder.append(keyCode)
    }
    return String.format("(%s)", builder)
  }

  companion object {
    private const val TAG = "InputSDKRemappingListener"
  }
}

জাভা

public class InputSDKRemappingListener implements InputRemappingListener {

    private static final String TAG = "InputSDKRemappingListener";

    @Override
    public void onInputMapChanged(InputMap inputMap) {
        Log.i(TAG, "Received update on input map changed.");
        if (inputMap.inputRemappingOption() ==
                InputEnums.REMAP_OPTION_DISABLED) {
            return;
        }
        for (InputGroup inputGroup : inputMap.inputGroups()) {
            if (inputGroup.inputRemappingOption() ==
                    InputEnums.REMAP_OPTION_DISABLED) {
                continue;
            }
            for (InputAction inputAction : inputGroup.inputActions()) {
                if (inputAction.inputRemappingOption() !=
                        InputEnums.REMAP_OPTION_DISABLED) {
                    // Found InputAction remapped by user
                    processRemappedAction(inputAction);
                }
            }
        }
    }

    private void processRemappedAction(InputAction remappedInputAction) {
        // Get remapped action info
        InputControls remappedControls =
            remappedInputAction.remappedInputControls();
        List<Integer> remappedKeyCodes = remappedControls.keycodes();
        List<Integer> mouseActions = remappedControls.mouseActions();
        String version = remappedInputAction.inputActionId().versionString();
        long remappedActionId = remappedInputAction.inputActionId().uniqueId();
        Optional<InputAction> currentInputAction;
        if (version == null || version.isEmpty()
                    || version.equals(InputSDKProvider.INPUTMAP_VERSION)) {
            currentInputAction = getCurrentVersionInputAction(remappedActionId);
        } else {
            Log.i(TAG, "Detected version of user-saved input action defers " +
                    "from current version");
            currentInputAction =
                    getCurrentVersionInputActionFromPreviousVersion(
                            remappedActionId, version);
        }
        if (!currentInputAction.isPresent()) {
            Log.e(TAG, String.format(
                    "input action with id %d and version %s not found",
                    remappedActionId, version == null || version.isEmpty() ?
                            "UNKNOWN" : version));
            return;
        }
        InputControls originalControls =
                currentInputAction.get().inputControls();
        List<Integer> originalKeyCodes = originalControls.keycodes();

        Log.i(TAG, String.format(
                "Found input action with id %d remapped from key %s to key %s",
                remappedActionId,
                keyCodesToString(originalKeyCodes),
                keyCodesToString(remappedKeyCodes)));

        // TODO: make display changes to match controls used by the user
    }

    private Optional<InputAction> getCurrentVersionInputAction(
            long inputActionId) {
        for (InputGroup inputGroup :
                    InputSDKProvider.gameInputMap.inputGroups()) {
            for (InputAction inputAction : inputGroup.inputActions()) {
                if (inputAction.inputActionId().uniqueId() == inputActionId) {
                    return Optional.of(inputAction);
                }
            }
        }
        return Optional.empty();
    }

    private Optional<InputAction>
            getCurrentVersionInputActionFromPreviousVersion(
                    long inputActionId, String previousVersion) {
        // TODO: add logic to this method considering the diff between your
        // current and previous InputMap.
        return Optional.empty();
    }

    private String keyCodesToString(List<Integer> keyCodes) {
        StringBuilder builder = new StringBuilder();
        for (Integer keyCode : keyCodes) {
            if (!builder.toString().isEmpty()) {
                builder.append(" + ");
            }
            builder.append(keyCode);
        }
        return String.format("(%s)", builder);
    }
}

সি#

#if PLAY_GAMES_PC

using System.Text;
using Java.Lang;
using Java.Util;
using Google.Android.Libraries.Play.Games.Inputmapping;
using Google.Android.Libraries.Play.Games.Inputmapping.Datamodel;
using UnityEngine;

public class InputSDKRemappingListener : InputRemappingListenerCallbackHelper
{
    public override void OnInputMapChanged(InputMap inputMap)
    {
        Debug.Log("Received update on remapped controls.");
        if (inputMap.InputRemappingOption() == InputEnums.REMAP_OPTION_DISABLED)
        {
            return;
        }
        List<InputGroup> inputGroups = inputMap.InputGroups();
        for (int i = 0; i < inputGroups.Size(); i ++)
        {
            InputGroup inputGroup = inputGroups.Get(i);
            if (inputGroup.InputRemappingOption()
                    == InputEnums.REMAP_OPTION_DISABLED)
            {
                continue;
            }
            List<InputAction> inputActions = inputGroup.InputActions();
            for (int j = 0; j < inputActions.Size(); j ++)
            {
                InputAction inputAction = inputActions.Get(j);
                if (inputAction.InputRemappingOption()
                        != InputEnums.REMAP_OPTION_DISABLED)
                {
                    // Found action remapped by user
                    ProcessRemappedAction(inputAction);
                }
            }
        }
    }

    private void ProcessRemappedAction(InputAction remappedInputAction)
    {
        InputControls remappedInputControls =
                remappedInputAction.RemappedInputControls();
        List<Integer> remappedKeycodes = remappedInputControls.Keycodes();
        List<Integer> mouseActions = remappedInputControls.MouseActions();
        string version = remappedInputAction.InputActionId().VersionString();
        long remappedActionId = remappedInputAction.InputActionId().UniqueId();
        InputAction currentInputAction;
        if (string.IsNullOrEmpty(version)
                || string.Equals(
                version, InputSDKMappingProvider.INPUT_MAP_VERSION))
        {
            currentInputAction = GetCurrentVersionInputAction(remappedActionId);
        }
        else
        {
            Debug.Log("Detected version of used-saved input action defers" +
                " from current version");
            currentInputAction =
                GetCurrentVersionInputActionFromPreviousVersion(
                    remappedActionId, version);
        }
        if (currentInputAction == null)
        {
            Debug.LogError(string.Format(
                "Input Action with id {0} and version {1} not found",
                remappedActionId,
                string.IsNullOrEmpty(version) ? "UNKNOWN" : version));
            return;
        }
        InputControls originalControls = currentInputAction.InputControls();
        List<Integer> originalKeycodes = originalControls.Keycodes();

        Debug.Log(string.Format(
            "Found Input Action with id {0} remapped from key {1} to key {2}",
            remappedActionId,
            KeyCodesToString(originalKeycodes),
            KeyCodesToString(remappedKeycodes)));
        // TODO: update HUD according to the controls of the user
    }

    private InputAction GetCurrentVersionInputAction(
            long inputActionId)
    {
        List<InputGroup> inputGroups =
            InputSDKMappingProvider.gameInputMap.InputGroups();
        for (int i = 0; i < inputGroups.Size(); i++)
        {
            InputGroup inputGroup = inputGroups.Get(i);
            List<InputAction> inputActions = inputGroup.InputActions();
            for (int j = 0; j < inputActions.Size(); j++)
            {
                InputAction inputAction = inputActions.Get(j);
                if (inputAction.InputActionId().UniqueId() == inputActionId)
                {
                    return inputAction;
                }
            }
        }
        return null;
    }

    private InputAction GetCurrentVersionInputActionFromPreviousVersion(
            long inputActionId, string version)
    {
        // TODO: add logic to this method considering the diff between your
        // current and previous InputMap.
        return null;
    }

    private string KeyCodesToString(List<Integer> keycodes)
    {
        StringBuilder builder = new StringBuilder();
        for (int i = 0; i < keycodes.Size(); i ++)
        {
            Integer keycode = keycodes.Get(i);
            if (builder.Length > 0)
            {
                builder.Append(" + ");
            }
            builder.Append(keycode.IntValue());
        }
        return string.Format("({0})", builder.ToString());
    }
}
#endif

ব্যবহারকারীর সংরক্ষিত রিম্যাপ করা কন্ট্রোলগুলো লোড করার পর, এবং প্রতিবার ব্যবহারকারী তার কী-গুলো রিম্যাপ করার পর, প্রোগ্রামটি চালু হওয়ার সময়ে InputRemappingListener অবহিত করা হয়।

প্রারম্ভিকীকরণ

আপনি যদি InputContexts ব্যবহার করেন, তাহলে আপনার প্রাথমিক দৃশ্যের জন্য ব্যবহৃত প্রথম কনটেক্সট সহ, প্রতিটি নতুন দৃশ্যে স্থানান্তরের সময় কনটেক্সটটি সেট করুন। আপনার InputMap নিবন্ধন করার পরেই InputContext সেট করতে হবে।

যদি আপনি রিম্যাপিং ইভেন্টের নোটিফিকেশন পেতে InputRemappingListeners ব্যবহার করেন, তাহলে আপনার InputMappingProvider রেজিস্টার করার আগে InputRemappingListener রেজিস্টার করুন, অন্যথায় আপনার গেম চালু হওয়ার সময় গুরুত্বপূর্ণ ইভেন্টগুলো মিস করতে পারে।

নিম্নলিখিত নমুনাটি দেখায় কিভাবে API ইনিশিয়ালাইজ করতে হয়:

কোটলিন

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    if (isGooglePlayGamesOnPC()) {
        val inputMappingClient = Input.getInputMappingClient(this)
        // Register listener before registering the provider
        inputMappingClient.registerRemappingListener(InputSDKRemappingListener())
        inputMappingClient.setInputMappingProvider(
                InputSDKProvider())
        // Set the context after you have registered the provider.
        inputMappingClient.setInputContext(InputSDKProvider.menuSceneInputContext)
    }
}

জাভা

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    if (isGooglePlayGamesOnPC()) {
        InputMappingClient inputMappingClient =
                Input.getInputMappingClient(this);
        // Register listener before registering the provider
        inputMappingClient.registerRemappingListener(
                new InputSDKRemappingListener());
        inputMappingClient.setInputMappingProvider(
                new InputSDKProvider());
        // Set the context after you have registered the provider
        inputMappingClient.setInputContext(InputSDKProvider.menuSceneInputContext);
    }
}

সি#

#if PLAY_GAMES_PC
using Google.Android.Libraries.Play.Games.Inputmapping;
using Google.Android.Libraries.Play.Games.InputMapping.ExternalType.Android.Content;
using Google.LibraryWrapper.Java;
#endif

public class GameManager : MonoBehaviour
{
#if PLAY_GAMES_PC
    private InputSDKMappingProvider _inputMapProvider =
        new InputSDKMappingProvider();
    private InputMappingClient _inputMappingClient;
#endif

    public void Awake()
    {
#if PLAY_GAMES_PC
        Context context = (Context)Utils.GetUnityActivity().GetRawObject();
        _inputMappingClient = Google.Android.Libraries.Play.Games.Inputmapping
            .Input.GetInputMappingClient(context);
        // Register listener before registering the provider.
        _inputMappingClient.RegisterRemappingListener(
            new InputSDKRemappingListener());
        _inputMappingClient.SetInputMappingProvider(_inputMapProvider);
        // Register context after you have registered the provider.
       _inputMappingClient.SetInputContext(
           InputSDKMappingProvider.menuSceneInputContext);
#endif
    }
}

পরিষ্কার করা

আপনার গেম বন্ধ করার সময় আপনার InputMappingProvider ইনস্ট্যান্স এবং যেকোনো InputRemappingListener ইনস্ট্যান্স আনরেজিস্টার করুন, যদিও আপনি তা না করলেও ইনপুট এসডিকে রিসোর্স লিক হওয়া এড়ানোর জন্য যথেষ্ট স্মার্ট:

কোটলিন

override fun onDestroy() {
    if (isGooglePlayGamesOnPC()) {
        val inputMappingClient = Input.getInputMappingClient(this)
        inputMappingClient.clearInputMappingProvider()
        inputMappingClient.clearRemappingListener()
    }

    super.onDestroy()
}

জাভা

@Override
protected void onDestroy() {
    if (isGooglePlayGamesOnPC()) {
        InputMappingClient inputMappingClient =
                Input.getInputMappingClient(this);
        inputMappingClient.clearInputMappingProvider();
        inputMappingClient.clearRemappingListener();
    }

    super.onDestroy();
}

সি#

public class GameManager : MonoBehaviour
{
    private void OnDestroy()
    {
#if PLAY_GAMES_PC
        _inputMappingClient.ClearInputMappingProvider();
        _inputMappingClient.ClearRemappingListener();
#endif
    }
}

পরীক্ষা

আপনি ম্যানুয়ালি ওভারলে খুলে প্লেয়ারের অভিজ্ঞতা দেখে, অথবা স্বয়ংক্রিয় পরীক্ষা ও যাচাইকরণের জন্য adb shell ব্যবহার করে আপনার ইনপুট SDK ইমপ্লিমেন্টেশন পরীক্ষা করতে পারেন।

পিসিতে গুগল প্লে গেমস এমুলেটরটি সাধারণ ত্রুটিগুলোর বিপরীতে আপনার ইনপুট ম্যাপের সঠিকতা যাচাই করে। ডুপ্লিকেট ইউনিক আইডি, ভিন্ন ভিন্ন ইনপুট ম্যাপ ব্যবহার করা বা রিম্যাপিং নিয়মগুলো অনুসরণ করতে ব্যর্থ হওয়ার মতো পরিস্থিতিতে (যদি রিম্যাপিং চালু থাকে), ওভারলেটি নীচের মতো একটি ত্রুটি বার্তা দেখায়: ইনপুট এসডিকে ওভারলে।

কমান্ড লাইনে adb ব্যবহার করে আপনার ইনপুট SDK ইমপ্লিমেন্টেশন যাচাই করুন। বর্তমান ইনপুট ম্যাপ পেতে, নিম্নলিখিত adb shell কমান্ডটি ব্যবহার করুন ( MY.PACKAGE.NAME এর জায়গায় আপনার গেমের নামটি বসান):

adb shell dumpsys input_mapping_service --get MY.PACKAGE.NAME

আপনি যদি আপনার InputMap সফলভাবে রেজিস্টার করে থাকেন, তাহলে এর অনুরূপ আউটপুট দেখতে পাবেন:

Getting input map for com.example.inputsample...
Successfully received the following inputmap:
# com.google.android.libraries.play.games.InputMap@d73526e1
input_groups {
  group_label: "Basic Movement"
  input_actions {
    action_label: "Jump"
    input_controls {
      keycodes: 51
      keycodes: 19
    }
    unique_id: 0
  }
  input_actions {
    action_label: "Left"
    input_controls {
      keycodes: 29
      keycodes: 21
    }
    unique_id: 1
  }
  input_actions {
    action_label: "Right"
    input_controls {
      keycodes: 32
      keycodes: 22
    }
    unique_id: 2
  }
  input_actions {
    action_label: "Use"
    input_controls {
      keycodes: 33
      keycodes: 66
      mouse_actions: MOUSE_LEFT_CLICK
      mouse_actions_value: 0
    }
    unique_id: 3
  }
}
input_groups {
  group_label: "Special Input"
  input_actions {
    action_label: "Jump"
    input_controls {
      keycodes: 51
      keycodes: 19
      keycodes: 62
      mouse_actions: MOUSE_LEFT_CLICK
      mouse_actions_value: 0
    }
    unique_id: 4
  }
  input_actions {
    action_label: "Duck"
    input_controls {
      keycodes: 47
      keycodes: 20
      keycodes: 113
      mouse_actions: MOUSE_RIGHT_CLICK
      mouse_actions_value: 1
    }
    unique_id: 5
  }
}
mouse_settings {
  allow_mouse_sensitivity_adjustment: true
  invert_mouse_movement: true
}

স্থানীয়করণ

ইনপুট এসডিকে অ্যান্ড্রয়েডের লোকালাইজেশন সিস্টেম ব্যবহার করে না। ফলে, একটি InputMap জমা দেওয়ার সময় আপনাকে অবশ্যই স্থানীয় ভাষার স্ট্রিং প্রদান করতে হবে। আপনি আপনার গেম ইঞ্জিনের লোকালাইজেশন সিস্টেমও ব্যবহার করতে পারেন।

প্রোগার্ড

আপনার গেম মিনিফাই করার জন্য Proguard ব্যবহার করার সময়, আপনার চূড়ান্ত প্যাকেজ থেকে SDK যাতে বাদ না পড়ে তা নিশ্চিত করতে আপনার proguard কনফিগারেশন ফাইলে নিম্নলিখিত নিয়মগুলি যোগ করুন:

-keep class com.google.android.libraries.play.hpe.** { *; }
-keep class com.google.android.libraries.play.games.inputmapping.** { *; }

এরপর কী?

আপনার গেমে ইনপুট এসডিকে (Input SDK) যুক্ত করার পর, আপনি পিসিতে গুগল প্লে গেমসের (Google Play Games on PC) বাকি প্রয়োজনীয়তাগুলো পূরণ করে কাজ চালিয়ে যেতে পারেন। আরও তথ্যের জন্য, পিসিতে গুগল প্লে গেমস দিয়ে শুরু করুন (Get started with Google Play Games on PC) দেখুন।