با Input SDK شروع کنید

این سند نحوه تنظیم و نمایش Input SDK را در بازی‌هایی که از Google Play Games در رایانه شخصی پشتیبانی می‌کنند، شرح می‌دهد. وظایف شامل اضافه کردن SDK به بازی شما و ایجاد یک نقشه ورودی است که شامل تکالیف ورودی کاربر از اقدامات بازی است.

قبل از اینکه شروع کنید

قبل از اینکه Input SDK را به بازی خود اضافه کنید، باید با استفاده از سیستم ورودی موتور بازی خود، از ورودی صفحه کلید و ماوس پشتیبانی کنید.

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

هر کنترل یک InputAction است (مثلاً "J" برای "پرش") و شما InputActions خود را در InputGroups سازماندهی می‌کنید. یک InputGroup ممکن است حالت متفاوتی را در بازی شما نشان دهد، مانند "رانندگی" یا "پیاده‌روی" یا "منوی اصلی". همچنین می‌توانید InputContexts برای نشان دادن گروه‌های فعال در نقاط مختلف بازی استفاده کنید.

می‌توانید تنظیم خودکار تغییر نقشه صفحه‌کلید را فعال کنید، اما اگر ترجیح می‌دهید رابط تغییر نقشه کنترل خودتان را داشته باشید، می‌توانید تغییر نقشه ورودی SDK را غیرفعال کنید.

نمودار توالی زیر نحوه عملکرد API مربوط به Input SDK را شرح می‌دهد:

نمودار توالی پیاده‌سازی یک بازی که API مربوط به Input SDK را فراخوانی می‌کند و تعامل آن با دستگاه اندروید.

وقتی بازی شما Input SDK را پیاده‌سازی می‌کند، کنترل‌های شما در صفحه بازی‌های گوگل پلی روی کامپیوتر نمایش داده می‌شوند.

پوشش بازی‌های گوگل پلی روی کامپیوتر

پوشش Google Play Games on PC ("پوشش") کنترل‌های تعریف‌شده توسط بازی شما را نمایش می‌دهد. کاربران در هر زمان با فشار دادن Shift + Tab به این پوشش دسترسی پیدا می‌کنند.

پوشش بازی‌های گوگل پلی روی کامپیوتر.

بهترین شیوه‌ها برای طراحی اتصال کلید

هنگام طراحی کلیدهای اتصال خود، بهترین شیوه‌های زیر را در نظر بگیرید:

  • InputActions خود را در InputGroups منطقی مرتبط گروه‌بندی کنید تا پیمایش و قابلیت کشف کنترل‌ها در حین بازی بهبود یابد.
  • هر InputGroup حداکثر به یک InputContext اختصاص دهید. یک InputMap با جزئیات دقیق، تجربه بهتری را برای پیمایش کنترل‌های شما در لایه پوششی ایجاد می‌کند.
  • برای هر نوع صحنه‌ی مختلف از بازی خود، یک InputContext ایجاد کنید. معمولاً می‌توانید از یک InputContext برای تمام صحنه‌های «منو مانند» خود استفاده کنید. برای هر مینی‌گیم در بازی خود یا برای کنترل‌های جایگزین برای یک صحنه، InputContexts مختلف استفاده کنید.
  • اگر دو اکشن طوری طراحی شده‌اند که از یک کلید تحت یک InputContext استفاده کنند، از رشته برچسب مانند "Interact / Fire" استفاده کنید.
  • اگر دو کلید برای اتصال به یک InputAction یکسان طراحی شده‌اند، از دو InputActions مختلف استفاده کنید که عمل یکسانی را در بازی شما انجام می‌دهند. می‌توانید از یک رشته برچسب یکسان برای هر دو InputActions استفاده کنید، اما شناسه (ID) آن باید متفاوت باشد.
  • اگر یک کلید اصلاح‌کننده برای مجموعه‌ای از کلیدها اعمال می‌شود، به جای چندین InputActions که کلید اصلاح‌کننده را ترکیب می‌کنند، داشتن یک InputAction واحد با کلید اصلاح‌کننده را در نظر بگیرید (مثال: به جای Shift + W، Shift + A، Shift + S، Shift + D از Shift و W ، A، S، D استفاده کنید).
  • تغییر نگاشت ورودی به طور خودکار زمانی که کاربر در فیلدهای متنی می‌نویسد، غیرفعال می‌شود. برای اطمینان از اینکه اندروید می‌تواند فیلدهای متنی را در بازی شما شناسایی کند و از تداخل کلیدهای تغییر نگاشت شده با آنها جلوگیری کند، از بهترین شیوه‌های پیاده‌سازی فیلدهای متنی اندروید پیروی کنید. اگر بازی شما مجبور است از فیلدهای متنی غیرمتعارف استفاده کند، می‌توانید setInputContext() با InputContext حاوی لیست خالی از InputGroups برای غیرفعال کردن تغییر نگاشت دستی استفاده کنید.
  • اگر بازی شما از تغییر نگاشت پشتیبانی می‌کند، به‌روزرسانی اتصالات کلید خود را در نظر بگیرید، عملیاتی حساس که می‌تواند با نسخه‌های ذخیره شده توسط کاربر تداخل داشته باشد. در صورت امکان از تغییر شناسه کنترل‌های موجود خودداری کنید.

ویژگی بازنگاشت

بازی‌های گوگل پلی روی کامپیوتر از بازنگاشت کنترل صفحه‌کلید بر اساس کلیدهای اتصالی که بازی شما با استفاده از Input SDK ارائه می‌دهد، پشتیبانی می‌کند. این گزینه اختیاری است و می‌تواند کاملاً غیرفعال شود. به عنوان مثال، ممکن است بخواهید رابط بازنگاشت صفحه‌کلید خود را ارائه دهید. برای غیرفعال کردن بازنگاشت برای بازی خود، فقط باید گزینه بازنگاشت را برای InputMap خود غیرفعال کنید (برای اطلاعات بیشتر به Build an InputMap مراجعه کنید).

برای دسترسی به این ویژگی، کاربران باید صفحه پوششی را باز کرده و سپس روی عملی که می‌خواهند تغییر مکان دهند کلیک کنند. پس از هر رویداد تغییر مکان، Google Play Games روی کامپیوتر، هر کنترل تغییر مکان داده شده توسط کاربر را به کنترل‌های پیش‌فرضی که بازی شما انتظار دریافت آنها را دارد، تغییر مکان می‌دهد، بنابراین بازی شما نیازی به آگاهی از تغییر مکان بازیکن ندارد. می‌توانید به صورت اختیاری، با اضافه کردن یک فراخوانی برای رویدادهای تغییر مکان، دارایی‌های مورد استفاده برای نمایش کنترل‌های صفحه کلید در بازی خود را به‌روزرسانی کنید.

تلاش برای تغییر نقشه کلید

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

برای پشتیبانی از قابلیت remapping در بازی خود، از محدودیت‌های زیر اجتناب کنید:

محدودیت‌های بازنگاشت

اگر کلیدهای ترکیبی شامل هر یک از موارد زیر باشند، می‌توان ویژگی‌های بازنگاشت را در بازی شما غیرفعال کرد:

  • InputActions چندکلیدی که از یک کلید تغییردهنده + یک کلید غیر تغییردهنده تشکیل نشده‌اند. برای مثال، Shift + A معتبر است اما A + B ، Ctrl + Alt یا Shift + A + Tab معتبر نیستند.
  • InputMap شامل InputActions ، InputGroups یا InputContexts با شناسه‌های منحصر به فرد مکرر است.

محدودیت‌های بازنگاشت

هنگام طراحی اتصالات کلید خود برای نگاشت مجدد، محدودیت‌های زیر را در نظر بگیرید:

  • تغییر ترکیب کلیدها پشتیبانی نمی‌شود. برای مثال، کاربران نمی‌توانند Shift + A را به Ctrl + B یا A را به Shift + A تغییر دهند.
  • تغییر نگاشت برای InputActions با دکمه‌های ماوس پشتیبانی نمی‌شود. برای مثال، Shift + کلیک راست را نمی‌توان تغییر نگاشت کرد.

تست تغییر کلید در Google Play Games روی شبیه‌ساز کامپیوتر

شما می‌توانید قابلیت remapping را در Google Play Games on PC Emulator در هر زمانی با اجرای دستور adb زیر فعال کنید:

adb shell dumpsys input_mapping_service --set RemappingFlagValue true

تغییر لایه پوششی مانند تصویر زیر:

پوشش با قابلیت تغییر مکان کلید فعال شده است.

اضافه کردن SDK

Input SDK را مطابق با پلتفرم توسعه خود نصب کنید.

جاوا و کاتلین

با اضافه کردن یک وابستگی به فایل build.gradle در سطح ماژول، Input SDK مربوط به جاوا یا کاتلین را دریافت کنید:

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

وحدت

کیت توسعه نرم‌افزار ورودی (Input SDK) یک بسته استاندارد یونیتی با چندین وابستگی است.

نصب بسته به همراه تمام وابستگی‌ها الزامی است. روش‌های مختلفی برای نصب بسته‌ها وجود دارد.

نصب پکیج .unitypackage

فایل پکیج یونیتی Input SDK را به همراه تمام وابستگی‌هایش دانلود کنید . می‌توانید با انتخاب Assets > Import package > Custom Package و پیدا کردن فایلی که دانلود کرده‌اید، پکیج .unitypackage را نصب کنید.

نصب با استفاده از UPM

روش دیگر این است که با دانلود فایل .tgz و نصب وابستگی‌های آن، بسته را با استفاده از Unity Package Manager نصب کنید:

نصب با استفاده از OpenUPM

شما می‌توانید این بسته را با استفاده از OpenUPM نصب کنید.

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

بازی‌های نمونه

برای مثال‌هایی از نحوه ادغام با Input SDK، به AGDK Tunnel برای بازی‌های Kotlin یا Java و Trivial Kart برای بازی‌های Unity مراجعه کنید.

اتصالات کلید خود را ایجاد کنید

با ساختن یک 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 دارای شناسه‌های منحصر به فرد باشد.

اگر از remapping پشتیبانی می‌کنید، می‌توانید تعریف کنید که کدام InputActions می‌توانند remapping شوند. اگر بازی شما از remapping پشتیبانی نمی‌کند، باید گزینه remapping را برای همه InputActions خود غیرفعال کنید، اما Input SDK به اندازه کافی هوشمند است که اگر remapping را در InputMap خود پشتیبانی نمی‌کنید، آن را غیرفعال کند.

این مثال، نقشه فضا کلید عملکرد Drive .

کاتلین

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

یک کلید InputAction در لایه‌ی پوششی نمایش داده می‌شود.

اکشن‌ها می‌توانند ورودی‌های ماوس را نیز نمایش دهند. این مثال ، کلیک چپ را به اکشن Move تبدیل می‌کند:

کاتلین

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 شما مشخص می‌شود. در این مثال فضا + شیفت به عملکرد توربو نگاشت شده است، که حتی زمانی که فضا به Drive نگاشت شده است.

کاتلین

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

ورودی چندکلیده‌ی اکشن (InputAction) در پوشش نمایش داده می‌شود.

کیت توسعه نرم‌افزار ورودی (Input SDK) به شما امکان می‌دهد دکمه‌های ماوس و کلید را برای یک عمل واحد با هم ترکیب کنید. این مثال نشان می‌دهد که شیفت و کلیک راست و فشردن همزمان آنها، در این بازی نمونه، یک نقطه مسیر اضافه می‌کند:

کاتلین

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) را در پوشش نمایش می‌دهد.

InputAction فیلدهای زیر را دارد:

  • ActionLabel : رشته‌ای که در رابط کاربری برای نمایش این عمل نمایش داده می‌شود. محلی‌سازی به صورت خودکار انجام نمی‌شود، بنابراین هرگونه محلی‌سازی را از قبل انجام دهید.
  • InputControls : کنترل‌های ورودی مورد استفاده این اکشن را تعریف می‌کند. این کنترل‌ها به گلیف‌های سازگار در پوشش نگاشت می‌شوند.
  • InputActionId : شیء InputIdentifier که شناسه شماره و نسخه InputAction را ذخیره می‌کند (برای اطلاعات بیشتر به Tracking Key IDs مراجعه کنید).
  • InputRemappingOption : یکی از InputEnums.REMAP_OPTION_ENABLED یا InputEnums.REMAP_OPTION_DISABLED . مشخص می‌کند که آیا عمل remap فعال است یا خیر. اگر بازی شما از remapping پشتیبانی نمی‌کند، می‌توانید از این فیلد صرف نظر کنید یا به سادگی آن را غیرفعال کنید.
  • RemappedInputControls : شیء InputControls فقط خواندنی که برای خواندن کلید تغییر یافته تنظیم شده توسط کاربر در رویدادهای تغییر مسیر (برای دریافت اعلان در مورد رویدادهای تغییر مسیر ) استفاده می‌شود.

InputControls ورودی‌های مرتبط با یک اکشن را نشان می‌دهد و شامل فیلدهای زیر است:

  • AndroidKeycodes : فهرستی از اعداد صحیح است که ورودی‌های صفحه‌کلید مرتبط با یک عمل را نشان می‌دهد. این اعداد در کلاس KeyEvent یا کلاس AndroidKeycode برای Unity تعریف شده‌اند.
  • MouseActions : فهرستی از مقادیر MouseAction است که ورودی‌های ماوس مرتبط با این عمل را نشان می‌دهد.

گروه‌های ورودی خود را تعریف کنید

InputActions با استفاده از InputGroups با اقدامات منطقی مرتبط گروه‌بندی می‌شوند تا قابلیت پیمایش و کنترل‌ها در پوشش بهبود یابد. هر شناسه InputGroup باید در تمام InputGroups بازی شما منحصر به فرد باشد.

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

اگر از remapping پشتیبانی می‌کنید، می‌توانید تعریف کنید که کدام InputGroups می‌توانند remapping شوند. اگر بازی شما از remapping پشتیبانی نمی‌کند، باید گزینه remapping را برای همه InputGroups خود غیرفعال کنید، اما Input SDK به اندازه کافی هوشمند است که اگر remapping را در 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
);

مثال زیر گروه‌های ورودی کنترل‌های جاده و کنترل‌های منو را در پوشش نمایش می‌دهد:

لایه پوششی که یک InputMap را نمایش می‌دهد که شامل گروه‌های ورودی کنترل‌های جاده و کنترل‌های منو است.

InputGroup فیلدهای زیر را دارد:

  • GroupLabel : رشته‌ای که در پوشش نمایش داده می‌شود و می‌تواند برای گروه‌بندی منطقی مجموعه‌ای از اقدامات استفاده شود. این رشته به طور خودکار محلی‌سازی نمی‌شود.
  • InputActions : فهرستی از اشیاء InputAction که در مرحله قبل تعریف کرده‌اید. همه این اقدامات به صورت بصری تحت عنوان گروه نمایش داده می‌شوند.
  • InputGroupId : شیء InputIdentifier که شناسه شماره و نسخه InputGroup را ذخیره می‌کند. برای اطلاعات بیشتر به Tracking Key IDs مراجعه کنید.
  • InputRemappingOption : یکی از InputEnums.REMAP_OPTION_ENABLED یا InputEnums.REMAP_OPTION_DISABLED . در صورت غیرفعال بودن، نگاشت مجدد تمام اشیاء InputAction متعلق به این گروه غیرفعال خواهد شد، حتی اگر گزینه نگاشت مجدد آن را فعال کرده باشند. در صورت فعال بودن، تمام اقدامات متعلق به این گروه قابل نگاشت مجدد هستند، مگر اینکه توسط اقدامات جداگانه غیرفعال شده باشد.

زمینه‌های ورودی خود را تعریف کنید

InputContexts به بازی شما اجازه می‌دهد تا از مجموعه متفاوتی از کنترل‌های صفحه‌کلید برای صحنه‌های مختلف بازی استفاده کند. برای مثال:

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

هنگام استفاده از InputContexts ، لایه رویی ابتدا گروه‌های زمینه مورد استفاده را نشان می‌دهد. برای فعال کردن این رفتار، تابع setInputContext() را فراخوانی کنید تا هر زمان که بازی شما وارد صحنه دیگری می‌شود، زمینه تنظیم شود. تصویر زیر این رفتار را نشان می‌دهد: در صحنه "رانندگی"، اکشن‌های کنترل جاده در بالای لایه رویی نشان داده می‌شوند. هنگام باز کردن منوی "فروشگاه"، اکشن‌های "کنترل‌های منو" در بالای لایه رویی نمایش داده می‌شوند.

InputContexts گروه‌ها را در لایه‌ی پوششی مرتب می‌کند.

این به‌روزرسانی‌های پوششی با تنظیم InputContext متفاوت در نقاط مختلف بازی شما انجام می‌شوند. برای انجام این کار:

  1. با استفاده از InputGroups InputActions خود را با اقدامات منطقی مرتبط گروه‌بندی کنید.
  2. این InputGroups برای بخش‌های مختلف بازی خود به InputContext اختصاص دهید.

InputGroups که به InputContext یکسانی تعلق دارند، نمی‌توانند 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 را ذخیره می‌کند (برای اطلاعات بیشتر به Tracking Key IDs مراجعه کنید).
  • ActiveGroups : فهرستی از InputGroups که هنگام فعال بودن این context در بالای overlay نمایش داده می‌شوند.

ساخت نقشه ورودی

یک InputMap مجموعه‌ای از تمام اشیاء InputGroup موجود در یک بازی است، و بنابراین تمام اشیاء InputAction که یک بازیکن می‌تواند انتظار انجام آنها را داشته باشد.

هنگام گزارش‌دهی اتصالات کلید خود، شما یک InputMap با تمام InputGroups مورد استفاده در بازی خود می‌سازید.

اگر بازی شما از remapping پشتیبانی نمی‌کند، گزینه remapping را غیرفعال و کلیدهای رزرو شده را خالی بگذارید.

مثال زیر یک InputMap می‌سازد که برای گزارش مجموعه‌ای از InputGroups استفاده می‌شود.

کاتلین

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 : InputGroups گزارش شده توسط بازی شما. گروه‌ها به ترتیب در پوشش نمایش داده می‌شوند، مگر اینکه گروه‌های فعلی مورد استفاده با فراخوانی setInputContext() مشخص شوند.
  • MouseSettings : شیء MouseSettings نشان می‌دهد که حساسیت ماوس قابل تنظیم است و ماوس روی محور y معکوس می‌شود.
  • InputMapId : شیء InputIdentifier که شناسه شماره و نسخه InputMap را ذخیره می‌کند (برای اطلاعات بیشتر به Tracking Key IDs مراجعه کنید).
  • InputRemappingOption : یکی از InputEnums.REMAP_OPTION_ENABLED یا InputEnums.REMAP_OPTION_DISABLED . مشخص می‌کند که آیا ویژگی remapping فعال است یا خیر.
  • 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

شناسه‌های شماره اشیاء InputAction باید در تمام InputActions در InputMap شما منحصر به فرد باشند. به طور مشابه، شناسه‌های اشیاء InputGroup باید در تمام InputGroups در یک InputMap منحصر به فرد باشند. نمونه زیر نحوه استفاده از یک 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 : یک رشته نسخه قابل خواندن توسط انسان که برای شناسایی یک نسخه از داده‌های ورودی بین دو نسخه از تغییرات داده‌های ورودی تنظیم شده است.

از رویدادهای تغییر نقشه مطلع شوید (اختیاری)

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

تصویر زیر نمونه‌ای از این رفتار را نشان می‌دهد که پس از تغییر مکان کلیدها جی ، پ و س به جی ، ایکس و تی به همین ترتیب، عناصر رابط کاربری بازی به‌روزرسانی می‌شوند تا کلیدهای تعیین‌شده توسط کاربر را نمایش دهند.

رابط کاربری با استفاده از تابع فراخوانی InputRemappingListener به رویدادهای نگاشت مجدد واکنش نشان می‌دهد.

این قابلیت با ثبت یک فراخوانی برگشتی 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 استفاده می‌کنید، در هر انتقال به صحنه جدید، context را تنظیم کنید، از جمله اولین context مورد استفاده برای صحنه اولیه‌تان. شما باید InputContext را پس از ثبت InputMap خود تنظیم کنید.

اگر از InputRemappingListeners برای اطلاع‌رسانی در مورد رویدادهای تغییر نقشه استفاده می‌کنید، InputRemappingListener خود را قبل از ثبت InputMappingProvider ثبت کنید، در غیر این صورت بازی شما ممکن است رویدادهای مهم را در زمان اجرا از دست بدهد.

مثال زیر نحوه مقداردهی اولیه 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 را از حالت ثبت خارج کنید، هرچند Input SDK به اندازه‌ی کافی هوشمند است که در صورت عدم انجام این کار، از نشت منابع جلوگیری کند:

کاتلین

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

تست

شما می‌توانید پیاده‌سازی Input SDK خود را با باز کردن دستی صفحه‌ی پوششی برای مشاهده‌ی تجربه‌ی پخش‌کننده، یا از طریق پوسته‌ی adb برای آزمایش و تأیید خودکار، آزمایش کنید.

شبیه‌ساز بازی‌های گوگل پلی روی کامپیوتر، صحت نقشه ورودی شما را در برابر خطاهای رایج بررسی می‌کند. برای سناریوهایی مانند شناسه‌های منحصر به فرد تکراری، استفاده از نقشه‌های ورودی مختلف یا عدم موفقیت در قوانین تغییر نقشه (در صورت فعال بودن تغییر نقشه)، پوشش، پیام خطایی مانند زیر را نشان می‌دهد:پوشش SDK ورودی.

پیاده‌سازی Input SDK خود را با استفاده از adb در خط فرمان تأیید کنید. برای دریافت نقشه ورودی فعلی، از دستور 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
}

محلی سازی

کیت توسعه نرم‌افزار ورودی (Input SDK) از سیستم محلی‌سازی اندروید استفاده نمی‌کند. در نتیجه، هنگام ارسال InputMap باید رشته‌های محلی‌سازی شده را ارائه دهید. همچنین می‌توانید از سیستم محلی‌سازی موتور بازی خود استفاده کنید.

پروگارد

هنگام استفاده از Proguard برای فشرده‌سازی بازی، قوانین زیر را به فایل پیکربندی proguard خود اضافه کنید تا مطمئن شوید SDK از بسته نهایی شما حذف نمی‌شود:

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

قدم بعدی چیست؟

پس از اینکه Input SDK را در بازی خود ادغام کردید، می‌توانید با هرگونه الزامات باقی‌مانده Google Play Games on PC ادامه دهید. برای اطلاعات بیشتر، به «شروع با Google Play Games on PC» مراجعه کنید.