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

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

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

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

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

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

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

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

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

เมื่อเกมของคุณใช้ Input 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 แต่รหัสของ InputActions จะต้องแตกต่างกัน
  • หากใช้แป้นกดร่วมกับชุดแป้น ให้พิจารณาใช้ InputAction 1 ตัวที่มีแป้นกดร่วมแทน InputAction หลายตัวที่รวมแป้นกดร่วม (เช่น ใช้ Shift และ W, A, S, D แทน Shift + W, Shift + A, Shift + S, Shift + D)InputActions
  • การแมปอินพุตใหม่จะปิดอยู่โดยอัตโนมัติเมื่อผู้ใช้เขียนในช่องข้อความ ทำตามแนวทางปฏิบัติแนะนำในการใช้ช่องข้อความของ 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 โดยเพิ่มการพึ่งพาในไฟล์ 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

ตัวอย่างนี้จะแมปแป้นเว้นวรรคกับการดำเนินการ Drive

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

Mouse InputAction ที่แสดงในวางซ้อน

คุณสามารถระบุชุดแป้นพิมพ์ลัดได้โดยส่งรหัสแป้นหลายรายการไปยัง 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() เพื่อตั้งค่าบริบททุกครั้งที่เกมเข้าสู่ฉากอื่น รูปภาพต่อไปนี้แสดงลักษณะการทํางานนี้ ในฉาก "ขับรถ" การดำเนินการการควบคุมถนนจะแสดงที่ด้านบนของการวางซ้อน เมื่อเปิดเมนู "Store" การดำเนินการ "การควบคุมเมนู" จะแสดงที่ด้านบนของการวางซ้อน

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

โปรแกรมจำลอง 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