เริ่มต้นใช้งาน Input SDK

เอกสารนี้อธิบายวิธีตั้งค่าและแสดง Input SDK ใน เกมที่รองรับ Google Play Games บน PC โดยงานต่างๆ ได้แก่ การเพิ่ม SDK ลงในเกมและการสร้างแผนที่อินพุต ซึ่งมี การกำหนดการดำเนินการในเกมกับอินพุตของผู้ใช้

ก่อนเริ่มต้นใช้งาน

ก่อนเพิ่ม Input SDK ลงในเกม คุณต้องรองรับอินพุตคีย์บอร์ดและเมาส์โดยใช้ระบบอินพุตของเอนจินเกม

Input SDK จะให้ข้อมูลแก่ Google Play Games บน PC เกี่ยวกับ ตัวควบคุมที่เกมของคุณใช้ เพื่อให้ระบบแสดงตัวควบคุมเหล่านั้นต่อผู้ใช้ได้ นอกจากนี้ ยัง อนุญาตให้ผู้ใช้เปลี่ยนการแมปแป้นพิมพ์ได้ด้วย (ไม่บังคับ)

แต่ละการควบคุมคือInputAction (เช่น "J" สำหรับ "ข้าม") และคุณจะจัดระเบียบInputActionsเป็นInputGroups InputGroup อาจแสดงถึงโหมดต่างๆ ในเกม เช่น "ขับรถ" "เดิน" หรือ "เมนูหลัก" นอกจากนี้ คุณยังใช้ InputContexts เพื่อระบุว่ากลุ่มใดที่ใช้งานอยู่ในช่วงต่างๆ ของเกมได้ด้วย

คุณสามารถเปิดใช้การเปลี่ยนแป้นพิมพ์เพื่อจัดการให้คุณโดยอัตโนมัติได้ แต่หากต้องการ ระบุอินเทอร์เฟซการเปลี่ยนการควบคุมของคุณเอง คุณก็ปิดใช้ การเปลี่ยน Input SDK ได้

แผนภาพลำดับต่อไปนี้อธิบายวิธีการทำงานของ API ของ Input SDK

ไดอะแกรมลำดับของการติดตั้งใช้งานเกมที่เรียกใช้ Input SDK API
และการโต้ตอบกับอุปกรณ์ Android

เมื่อเกมของคุณใช้ SDK อินพุต ระบบจะแสดงตัวควบคุมใน ภาพซ้อนทับของ Google Play Games บน PC

การซ้อนทับของ Google Play Games บน PC

การวางซ้อนของ Google Play Games บน PC ("การวางซ้อน") จะแสดงการควบคุม ที่เกมของคุณกำหนด ผู้ใช้เข้าถึงการซ้อนทับได้ทุกเมื่อโดย กด Shift + Tab

ภาพซ้อนทับของ Google Play Games บน PC

แนวทางปฏิบัติแนะนำในการออกแบบการเชื่อมโยงคีย์

เมื่อออกแบบการเชื่อมโยงคีย์ ให้พิจารณาแนวทางปฏิบัติแนะนำต่อไปนี้

  • จัดกลุ่ม InputActions เป็น InputGroups ที่เกี่ยวข้องอย่างมีตรรกะเพื่อปรับปรุง การไปยังส่วนต่างๆ และการค้นพบตัวควบคุมในระหว่างการเล่นเกม
  • กำหนด InputGroup แต่ละรายการให้กับ InputContext อย่างน้อย 1 รายการ การปรับอย่างละเอียด InputMap จะช่วยให้คุณได้รับประสบการณ์การใช้งานที่ดียิ่งขึ้นในการไปยังส่วนควบคุมใน การซ้อนทับ
  • สร้าง InputContext สำหรับฉากประเภทต่างๆ ของเกม โดยปกติแล้ว คุณจะใช้ InputContext เดียวสำหรับฉาก "เหมือนเมนู" ทั้งหมดได้ ใช้ InputContexts ที่แตกต่างกันสำหรับมินิเกมในเกมหรือสำหรับ การควบคุมทางเลือกสำหรับฉากเดียว
  • หากการดำเนินการ 2 อย่างออกแบบมาให้ใช้คีย์เดียวกันภายใต้InputContextเดียวกัน ให้ใช้สตริงป้ายกำกับ เช่น "โต้ตอบ / ยิง"
  • หากออกแบบคีย์ 2 คีย์ให้ผูกกับ InputAction เดียวกัน ให้ใช้ InputActions 2 อันที่แตกต่างกันซึ่งทําการกระทําเดียวกันในเกม คุณอาจใช้สตริงป้ายกำกับเดียวกันสำหรับทั้ง InputActions แต่รหัสต้องแตกต่างกัน
  • หากใช้แป้นกดร่วมกับชุดแป้น ให้ใช้ InputAction เดียวที่มีแป้นกดร่วมแทน InputActions หลายรายการที่รวมแป้นกดร่วม (เช่น ใช้ Shift และ W, A, S, D แทน Shift + W, Shift + A, Shift + S, Shift + D)
  • การแมปอินพุตใหม่จะปิดใช้โดยอัตโนมัติเมื่อผู้ใช้เขียนลงในช่องข้อความ ทําตามแนวทางปฏิบัติแนะนําในการใช้ช่องข้อความ Android เพื่อให้แน่ใจว่า Android สามารถตรวจหาช่องข้อความในเกมและป้องกันไม่ให้คีย์ที่แมปใหม่ รบกวนช่องข้อความเหล่านั้น หากเกมต้องใช้ช่องข้อความที่ไม่เป็นไปตามรูปแบบปกติ คุณสามารถใช้ setInputContext() กับ InputContext ที่มี รายการว่างของ InputGroups เพื่อปิดใช้การแมปใหม่ด้วยตนเอง
  • หากเกมรองรับการแมปใหม่ ให้พิจารณาอัปเดตการเชื่อมโยงคีย์ ซึ่งเป็น การดำเนินการที่ละเอียดอ่อนซึ่งอาจขัดแย้งกับเวอร์ชันที่ผู้ใช้บันทึกไว้ หลีกเลี่ยง การเปลี่ยนรหัสของตัวควบคุมที่มีอยู่ หากเป็นไปได้

ฟีเจอร์การแมปใหม่

Google Play Games บน PC รองรับการเปลี่ยนการแมปการควบคุมด้วยแป้นพิมพ์ตามการเชื่อมโยงคีย์ ที่เกมของคุณระบุโดยใช้ Input SDK คุณจะทำขั้นตอนนี้หรือไม่ก็ได้และ ปิดใช้ได้ทั้งหมด เช่น คุณอาจต้องการจัดเตรียมอินเทอร์เฟซการเปลี่ยนเส้นทางแป้นพิมพ์ของคุณเอง หากต้องการปิดใช้การแมปใหม่สำหรับเกม คุณเพียงแค่ต้องระบุ ตัวเลือกการแมปใหม่ที่ปิดใช้สำหรับ InputMap (ดูข้อมูลเพิ่มเติมได้ที่ สร้าง InputMap)

หากต้องการเข้าถึงฟีเจอร์นี้ ผู้ใช้ต้องเปิดการซ้อนทับแล้วคลิกการดำเนินการ ที่ต้องการแมปใหม่ หลังจากเหตุการณ์การแมปใหม่ทุกครั้ง Google Play Games บน PC จะแมป การควบคุมที่ผู้ใช้แมปใหม่แต่ละรายการกับการควบคุมเริ่มต้นที่เกมคาดหวัง ที่จะได้รับ ดังนั้นเกมจึงไม่จำเป็นต้องทราบการแมปใหม่ของผู้เล่น คุณ สามารถเลือกอัปเดตชิ้นงานที่ใช้แสดงการควบคุมด้วยแป้นพิมพ์ใน เกมได้โดยเพิ่ม Callback สำหรับเหตุการณ์การแมปใหม่

ลองแมปแป้นใหม่

Google Play Games บน PC จะจัดเก็บการควบคุมที่แมปใหม่ไว้ในเครื่องสำหรับผู้ใช้แต่ละราย เพื่อให้การควบคุมยังคงอยู่ตลอดเซสชันการเล่นเกม ระบบจะจัดเก็บข้อมูลนี้ ไว้ในดิสก์สำหรับแพลตฟอร์ม PC เท่านั้น และไม่มีผลต่อประสบการณ์การใช้งานบนอุปกรณ์เคลื่อนที่ ระบบจะลบข้อมูลการควบคุมเมื่อผู้ใช้ถอนการติดตั้งหรือติดตั้ง Google Play Games บน PC อีกครั้ง ข้อมูลนี้จะไม่คงอยู่บนอุปกรณ์ PC หลายเครื่อง

หากต้องการรองรับฟีเจอร์การแมปใหม่ในเกม ให้หลีกเลี่ยงข้อจำกัดต่อไปนี้

ข้อจำกัดของการแมปใหม่

คุณปิดใช้ฟีเจอร์การแมปใหม่ในเกมได้หากการเชื่อมโยงคีย์มีกรณีใดกรณีหนึ่งต่อไปนี้

  • 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 บน PC

คุณเปิดใช้ฟีเจอร์การแมปใหม่ในโปรแกรมจำลอง Google Play Games บน PC ได้ทุกเมื่อ โดยใช้คำสั่ง adb ต่อไปนี้

adb shell dumpsys input_mapping_service --set RemappingFlagValue true

การเปลี่ยนแปลงการวางซ้อนดังรูปภาพต่อไปนี้

การวางซ้อนที่เปิดใช้การแมปคีย์ใหม่

เพิ่ม SDK

ติดตั้ง Input SDK ตามแพลตฟอร์มการพัฒนาของคุณ

Java และ Kotlin

รับ Input SDK สำหรับ Java หรือ Kotlin โดยเพิ่มทรัพยากร Dependency ลงใน ไฟล์ระดับโมดูล build.gradle

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

Unity

Input SDK เป็นแพ็กเกจ Unity มาตรฐานที่มีทรัพยากร Dependency หลายรายการ

คุณต้องติดตั้งแพ็กเกจที่มีการอ้างอิงทั้งหมด การติดตั้งแพ็กเกจทำได้หลายวิธี

ติดตั้ง .unitypackage

ดาวน์โหลดไฟล์ unitypackage ของ Input SDK พร้อมกับทรัพยากร Dependency ทั้งหมด คุณติดตั้ง .unitypackage ได้โดยเลือกชิ้นงาน > นำเข้าแพ็กเกจ > แพ็กเกจที่กำหนดเอง แล้วค้นหาไฟล์ที่คุณดาวน์โหลด

ติดตั้งโดยใช้ UPM

หรือคุณจะติดตั้งแพ็กเกจโดยใช้ Unity Package Manager ก็ได้โดย ดาวน์โหลด .tgz และติดตั้งทรัพยากร Dependency ดังนี้

ติดตั้งโดยใช้ OpenUPM

คุณติดตั้งแพ็กเกจได้โดยใช้ OpenUPM

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

เกมตัวอย่าง

ดูตัวอย่างวิธีผสานรวมกับ Input SDK ได้ที่ AGDK Tunnel สำหรับเกม Kotlin หรือ Java และ Trivial Kart สำหรับเกม Unity

สร้างการเชื่อมโยงคีย์

ลงทะเบียนการเชื่อมโยงคีย์โดยการสร้าง InputMap และส่งคืนพร้อมกับ InputMappingProvider ตัวอย่างต่อไปนี้แสดงเค้าโครงของInputMappingProvider

Kotlin

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

Java

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

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

C#

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

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

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

กำหนดการทำงานของอินพุต

คลาส InputAction ใช้เพื่อแมปคีย์หรือชุดแป้นที่กดร่วมกันกับ การดำเนินการในเกม InputActions ต้องมีรหัสที่ไม่ซ้ำกันใน InputActions ทั้งหมด

หากรองรับการแมปใหม่ คุณจะกำหนดได้ว่าInputActionsสามารถ แมปใหม่ได้ หากเกมไม่รองรับการแมปใหม่ คุณควรตั้งค่าตัวเลือกการแมปใหม่ เป็นปิดสำหรับInputActionsทั้งหมด แต่ Input SDK จะ ฉลาดพอที่จะปิดการแมปใหม่หากคุณไม่รองรับในInputMap

ตัวอย่างนี้แมปแป้นเว้นวรรคกับคำสั่งไดรฟ์

Kotlin

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

Java

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

C#

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 แบบแป้นเดียวที่แสดงในภาพซ้อนทับ

การดำเนินการอาจแสดงถึงอินพุตของเมาส์ได้ด้วย ตัวอย่างนี้ตั้งค่าคลิกซ้ายเป็นการดำเนินการย้าย ดังนี้

Kotlin

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

Java

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

C#

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

MouseInputAction ที่แสดงในการวางซ้อน

คุณระบุชุดแป้นพิมพ์ลัดได้โดยส่งรหัสแป้นพิมพ์หลายรหัสไปยัง InputAction ในตัวอย่างนี้ space + shift จะแมปกับ การดำเนินการ Turbo ซึ่งจะทำงานได้แม้ว่า Space จะแมปกับ Drive ก็ตาม

Kotlin

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

Java

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

C#

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 ช่วยให้คุณผสมปุ่มเมาส์และปุ่มแป้นพิมพ์เข้าด้วยกันเพื่อ การทำงานเดียวได้ ตัวอย่างนี้ระบุว่าการกด Shift และคลิกขวา พร้อมกันจะเพิ่มจุดพักในเกมตัวอย่างนี้

Kotlin

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

Java

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

C#

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: สตริงที่แสดงใน UI เพื่อแสดงการดำเนินการนี้ ระบบจะไม่ทำการแปลโดยอัตโนมัติ ดังนั้นคุณจึงต้องทำการแปลล่วงหน้า
  • InputControls: กำหนดตัวควบคุมอินพุตที่การดำเนินการนี้ใช้ โดย การควบคุมจะแมปกับอักขระที่สอดคล้องกันในภาพซ้อนทับ
  • InputActionId: ออบเจ็กต์ InputIdentifier ที่จัดเก็บรหัสหมายเลขและเวอร์ชันของ InputAction (ดูข้อมูลเพิ่มเติมได้ที่รหัสคีย์การติดตาม)
  • InputRemappingOption: หนึ่งใน InputEnums.REMAP_OPTION_ENABLED หรือ InputEnums.REMAP_OPTION_DISABLED กำหนดว่าเปิดใช้การดำเนินการเพื่อ เปลี่ยนเส้นทางหรือไม่ หากเกมไม่รองรับการแมปใหม่ คุณอาจข้ามช่องนี้หรือ ตั้งค่าเป็นปิดใช้
  • RemappedInputControls: ออบเจ็กต์ InputControls แบบอ่านอย่างเดียวที่ใช้ในการอ่านชุดคีย์ที่ผู้ใช้แมปใหม่ในเหตุการณ์การแมปใหม่ (ใช้สำหรับรับการแจ้งเตือนเกี่ยวกับเหตุการณ์การแมปใหม่)

InputControls แสดงอินพุตที่เชื่อมโยงกับการดำเนินการและมีฟิลด์ต่อไปนี้

  • AndroidKeycodes: คือรายการจำนวนเต็มที่แสดงถึงอินพุตของแป้นพิมพ์ ที่เชื่อมโยงกับการดำเนินการ ซึ่งกำหนดไว้ในคลาส KeyEvent หรือคลาส AndroidKeycode สำหรับ Unity
  • MouseActions: คือรายการค่า MouseAction ที่แสดงถึงอินพุตของเมาส์ ที่เชื่อมโยงกับการดำเนินการนี้

กำหนดกลุ่มอินพุต

InputActions จะจัดกลุ่มกับการดำเนินการที่เกี่ยวข้องอย่างมีตรรกะโดยใช้ InputGroups เพื่อ ปรับปรุงการนำทางและความสามารถในการค้นพบการควบคุมในภาพซ้อนทับ แต่ละInputGroupรหัสต้องไม่ซ้ำกันในInputGroupsทั้งหมดในเกม

การจัดระเบียบการกระทำที่ป้อนเป็นกลุ่มจะช่วยให้ผู้เล่นค้นหาการเชื่อมโยงคีย์ที่ถูกต้องสำหรับบริบทปัจจุบันได้ง่ายขึ้น

หากรองรับการแมปใหม่ คุณจะกำหนดได้ว่าInputGroupsสามารถ แมปใหม่ได้ หากเกมไม่รองรับการแมปใหม่ คุณควรตั้งค่าตัวเลือกการแมปใหม่ เป็นปิดสำหรับInputGroupsทั้งหมด แต่ Input SDK จะ ฉลาดพอที่จะปิดการแมปใหม่หากคุณไม่รองรับในInputMap

Kotlin

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

Java

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

C#

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 ดูข้อมูลเพิ่มเติมได้ที่ติดตามรหัสคีย์
  • InputRemappingOption: หนึ่งใน InputEnums.REMAP_OPTION_ENABLED หรือ InputEnums.REMAP_OPTION_DISABLED หากปิดใช้ ระบบจะปิดใช้การแมปใหม่ของInputAction ออบเจ็กต์ทั้งหมดที่อยู่ในกลุ่มนี้ แม้ว่าออบเจ็กต์จะ ระบุว่าเปิดใช้ตัวเลือกการแมปใหม่ก็ตาม หากเปิดใช้ การดำเนินการทั้งหมดที่อยู่ในกลุ่มนี้จะสามารถแมปใหม่ได้ เว้นแต่จะมีการระบุว่าปิดใช้โดยการดำเนินการแต่ละรายการ

กำหนดบริบทอินพุต

InputContexts ช่วยให้เกมใช้ชุดการควบคุมด้วยแป้นพิมพ์ที่แตกต่างกันสำหรับ ฉากต่างๆ ของเกมได้ เช่น

  • คุณอาจระบุชุดอินพุตที่แตกต่างกันสำหรับการไปยังส่วนต่างๆ ของเมนูเทียบกับการเคลื่อนที่ ในเกม
  • คุณอาจระบุชุดอินพุตที่แตกต่างกันได้โดยขึ้นอยู่กับโหมดการเคลื่อนที่ ในเกม เช่น การขับรถเทียบกับการเดิน
  • คุณอาจระบุชุดอินพุตที่แตกต่างกันตามสถานะปัจจุบันของเกม เช่น การไปยังส่วนต่างๆ ของโลกเหนือเทียบกับการเล่นแต่ละเลเวล

เมื่อใช้ InputContexts การซ้อนทับจะแสดงกลุ่มของบริบท ที่ใช้อยู่ก่อน หากต้องการเปิดใช้ลักษณะการทำงานนี้ ให้เรียกใช้ setInputContext() เพื่อตั้งค่า บริบททุกครั้งที่เกมเข้าสู่ฉากอื่น รูปภาพต่อไปนี้ แสดงลักษณะการทำงานนี้ ในฉาก "ขับรถ" การดำเนินการการควบคุมบนท้องถนน จะแสดงที่ด้านบนของการซ้อนทับ เมื่อเปิดเมนู "ร้านค้า" การดำเนินการ "การควบคุมเมนู" จะแสดงที่ด้านบนของการซ้อนทับ

จัดกลุ่มการเรียงลำดับ InputContexts ในการวางซ้อน

การอัปเดตภาพซ้อนเหล่านี้ทำได้โดยการตั้งค่า InputContext ที่แตกต่างกัน ในจุดต่างๆ ของเกม โดยทำดังนี้

  1. จัดกลุ่ม InputActions ด้วยการดำเนินการที่เกี่ยวข้องอย่างมีตรรกะโดยใช้ InputGroups
  2. กำหนดInputGroupsเหล่านี้ให้กับ InputContext สำหรับส่วนต่างๆ ของ เกม

InputGroups ที่อยู่ในInputContextเดียวกันต้องไม่มีInputActions ที่ขัดแย้งกัน เมื่อใช้คีย์เดียวกัน แนวทางปฏิบัติที่ดีคือการกำหนดแต่ละ InputGroupให้กับ InputContext เดียว

โค้ดตัวอย่างต่อไปนี้แสดงตรรกะ InputContext

Kotlin

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

Java

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

C#

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 ทั้งหมดที่ใช้ในเกม

หากเกมไม่รองรับการแมปใหม่ ให้ตั้งค่าตัวเลือกการแมปใหม่เป็นปิดและ ปล่อยให้ปุ่มที่สงวนไว้ว่างเปล่า

ตัวอย่างต่อไปนี้สร้าง InputMap ที่ใช้รายงานคอลเล็กชันของ InputGroups

Kotlin

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

Java

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

C#

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 กำหนดว่าฟีเจอร์การแมปใหม่ เปิดใช้อยู่หรือไม่
  • ReservedControls: รายการ InputControls ที่ผู้ใช้จะเปลี่ยนเส้นทางไม่ได้

ติดตามรหัสคีย์

ออบเจ็กต์ InputAction, InputGroup, InputContext และ InputMap มีออบเจ็กต์ InputIdentifier ที่จัดเก็บรหัสตัวเลขที่ไม่ซ้ำกันและรหัสเวอร์ชันสตริง การติดตามออบเจ็กต์เวอร์ชันสตริงเป็นตัวเลือกที่ไม่บังคับ แต่ขอแนะนำให้ติดตามเวอร์ชันของ InputMap หากไม่ได้ระบุเวอร์ชันสตริง สตริงจะเป็นค่าว่าง ต้องระบุเวอร์ชันสตริงสำหรับออบเจ็กต์ InputMap

ตัวอย่างต่อไปนี้กำหนดสตริงเวอร์ชันให้กับ InputActions หรือ InputGroups

Kotlin

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

Java

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

C#

#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รหัสออบเจ็กต์ต้องไม่ซ้ำกันในInputMapทั้งหมด InputGroups ตัวอย่างต่อไปนี้แสดงวิธีใช้ enumเพื่อติดตามรหัสที่ไม่ซ้ำกันของออบเจ็กต์

Kotlin

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

Java

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;

C#

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 เวอร์ชัน

รับการแจ้งเตือนเมื่อมีการแมปเหตุการณ์ใหม่ (ไม่บังคับ)

รับการแจ้งเตือนเกี่ยวกับเหตุการณ์การแมปใหม่เพื่อรับทราบคีย์ที่ใช้ในเกม ของคุณ ซึ่งจะช่วยให้เกมอัปเดตชิ้นงานที่แสดงบนหน้าจอเกม ที่ใช้แสดงตัวควบคุมการดำเนินการได้

รูปภาพต่อไปนี้แสดงตัวอย่างลักษณะการทำงานนี้ ซึ่งหลังจากรีแมป ปุ่ม G, P และ S เป็น J, X และ T ตามลำดับแล้ว องค์ประกอบ UI ของเกมจะได้รับการอัปเดตเพื่อ แสดงปุ่มที่ผู้ใช้ตั้งค่าไว้

UI ตอบสนองต่อเหตุการณ์การแมปใหม่โดยใช้การเรียกกลับ InputRemappingListener

ฟังก์ชันนี้จะทำงานโดยการลงทะเบียนInputRemappingListener การเรียกกลับ หากต้องการใช้ฟีเจอร์นี้ ให้เริ่มด้วยการลงทะเบียนอินสแตนซ์ InputRemappingListener

Kotlin

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

Java

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

C#

#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

Kotlin

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

Java

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

C#

#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 จะฉลาด พอที่จะหลีกเลี่ยงการรั่วไหลของทรัพยากรหากคุณไม่ยกเลิกการลงทะเบียนก็ตาม

Kotlin

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

    super.onDestroy()
}

Java

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

    super.onDestroy();
}

C#

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

ทดสอบ

คุณสามารถทดสอบการติดตั้งใช้งาน Input SDK ได้โดยเปิดการวางซ้อนด้วยตนเองเพื่อดูประสบการณ์การใช้งานของผู้เล่น หรือผ่าน adb shell สำหรับการทดสอบและการยืนยันอัตโนมัติ

โปรแกรมจำลอง Google Play Games บน PC จะตรวจสอบความถูกต้องของแผนที่อินพุต เทียบกับข้อผิดพลาดที่พบบ่อย สำหรับสถานการณ์ต่างๆ เช่น รหัสที่ไม่ซ้ำกันที่ซ้ำกัน การใช้ อินพุตแมปที่แตกต่างกัน หรือการไม่เป็นไปตามกฎการแมปใหม่ (หากเปิดใช้การแมปใหม่) การวางซ้อนจะแสดงข้อความแสดงข้อผิดพลาดดังนี้ ภาพซ้อนทับของ Input 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 ไม่ได้ใช้ระบบการแปลของ Android ด้วยเหตุนี้ คุณจึงต้องระบุสตริงที่แปลแล้วเมื่อส่ง InputMap คุณยังใช้ระบบการแปลของเกมเอนจินได้ด้วย

Proguard

เมื่อใช้ 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 บน PC ดูข้อมูลเพิ่มเติมได้ที่เริ่มต้นใช้งาน Google Play Games บน PC