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

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

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

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

Input SDK اطلاعاتی را در مورد کنترل‌هایی که بازی شما استفاده می‌کند در اختیار بازی‌های Google Play در رایانه شخصی قرار می‌دهد تا بتوان آنها را به کاربر نمایش داد. همچنین می تواند به صورت اختیاری امکان نقشه برداری مجدد صفحه کلید را برای کاربران فراهم کند.

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

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

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

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

وقتی بازی شما Input SDK را اجرا می‌کند، کنترل‌های شما در بازی‌های Google Play روی رایانه شخصی نمایش داده می‌شوند.

بازی‌های Google Play روی رایانه شخصی

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

بازی‌های Google Play روی رایانه شخصی.

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

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

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

ویژگی نقشه برداری مجدد

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

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

سعی کنید کلید را مجدداً ترسیم کنید

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

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

محدودیت های نقشه برداری مجدد

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

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

محدودیت های نقشه برداری مجدد

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

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

نقشه برداری مجدد کلید را در بازی های Google Play در شبیه ساز رایانه شخصی آزمایش کنید

با صدور دستور adb زیر می‌توانید ویژگی remapping را در بازی‌های Google Play در شبیه‌ساز رایانه شخصی در هر زمان فعال کنید:

adb shell dumpsys input_mapping_service --set RemappingFlagValue true

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

همپوشانی با نگاشت مجدد کلید فعال است.

SDK را اضافه کنید

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

جاوا و کاتلین

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

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

وحدت

Input SDK یک بسته استاندارد Unity با چندین وابستگی است.

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

.unitypackage را نصب کنید

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

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

همچنین می توانید بسته را با استفاده از Unity's Package Manager با دانلود .tgz و نصب وابستگی های آن نصب کنید:

با استفاده از 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 را برای همه InputActions خود غیرفعال کنید، اما Input SDK به اندازه کافی هوشمند است که اگر در InputMap خود از آن پشتیبانی نمی کنید، Remapping را خاموش کنید.

این مثال نقشه برداری می کند فضا کلید عمل 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
);

Mouse InputAction در روکش نمایش داده می شود.

ترکیب کلیدها با ارسال چندین کد کلید به InputAction شما مشخص می شود. در این مثال فضا + جابجایی به اکشن Turbo نگاشت شده است، که حتی زمانی که کار می کند فضا به 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 را ذخیره می کند (برای اطلاعات بیشتر به شناسه های کلید ردیابی مراجعه کنید).
  • InputRemappingOption : یکی از InputEnums.REMAP_OPTION_ENABLED یا InputEnums.REMAP_OPTION_DISABLED . مشخص می کند که آیا عملکرد برای نقشه برداری مجدد فعال است یا خیر. اگر بازی شما از نقشه‌برداری مجدد پشتیبانی نمی‌کند، می‌توانید این فیلد را نادیده بگیرید یا به سادگی آن را غیرفعال کنید.
  • RemappedInputControls : شیء InputControls فقط خواندنی که برای خواندن کلید remapped تنظیم شده توسط کاربر در نگاشت مجدد رویدادها استفاده می شود (برای دریافت اطلاع از رویدادهای نقشه برداری مجدد استفاده می شود).

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

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

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

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

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

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

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

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

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

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

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

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

InputContexts مرتب سازی گروه ها در پوشش.

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

  1. InputActions خود را با اقدامات منطقی مرتبط با استفاده از InputGroups گروه بندی کنید
  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 را ذخیره می کند (برای اطلاعات بیشتر به شناسه های کلید ردیابی مراجعه کنید).
  • ActiveGroups : لیستی از InputGroups که در زمانی که این زمینه فعال است استفاده می شود و در بالای همپوشانی نمایش داده می شود.

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

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 را ذخیره می کند (برای اطلاعات بیشتر به شناسه های کلید ردیابی مراجعه کنید).
  • 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 : یک رشته نسخه قابل خواندن توسط انسان برای شناسایی نسخه ای از داده های ورودی بین 2 نسخه از تغییرات داده های ورودی.

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

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

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

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

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

اجرای 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 از سیستم محلی سازی Android استفاده نمی کند. در نتیجه، هنگام ارسال 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 باقی‌مانده در رایانه شخصی خود ادامه دهید. برای اطلاعات بیشتر، به شروع با بازی‌های Google Play در رایانه شخصی مراجعه کنید.