การป้อนข้อมูลด้วยเมาส์

หัวข้อนี้ครอบคลุมวิธีใช้การป้อนข้อมูลจากเมาส์สำหรับ Google Play Games บน PC ในเกมที่โหมดการแปลอินพุตไม่ได้มอบประสบการณ์การเล่นที่เหมาะสม

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

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

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

คุณต้องปิดใช้โหมดการแปลอินพุตเพื่อรองรับรูปแบบ UI ที่ใช้กันทั่วไปใน PC

การจัดการอินพุตสำหรับ Google Play Games บน PC จะเหมือนกับของ ChromeOS การเปลี่ยนแปลงที่รองรับ PC ยังช่วยปรับปรุงเกมสำหรับผู้เล่น Android ทุกคนด้วย

ปิดใช้โหมดการแปลอินพุต

ประกาศฟีเจอร์ ในไฟล์ AndroidManifest.xml android.hardware.type.pc ซึ่งจะระบุว่าเกมของคุณใช้ฮาร์ดแวร์ PC และปิดใช้โหมดการแปลอินพุต นอกจากนี้ การเพิ่ม required="false" จะช่วยให้มั่นใจว่ายังสามารถติดตั้งเกมบนโทรศัพท์และแท็บเล็ตที่ไม่มีเมาส์ได้ เช่น

<manifest ...>
  <uses-feature
      android:name="android.hardware.type.pc"
      android:required="false" />
  ...
</manifest>

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

ภาพหน้าจอของ &quot;โหมด PC(KiwiMouse)&quot; ที่เลือกในเมนูตามบริบท

หลังจากทำเช่นนี้แล้ว ระบบจะรายงานการเคลื่อนไหวของเมาส์โดย View.onGenericMotionEvent ที่มีแหล่งที่มา SOURCE_MOUSE ซึ่งระบุว่าเป็นเหตุการณ์ของเมาส์

Kotlin

gameView.setOnGenericMotionListener { _, motionEvent ->
    var handled = false
    if (motionEvent.isFromSource(InputDevice.SOURCE_CLASS_POINTER)) {
        // handle the mouse event here
        handled = true
    }
    handled
}

Java

gameView.setOnGenericMotionListener((view, motionEvent) -> {
    if (motionEvent.isFromSource(InputDevice.SOURCE_CLASS_POINTER)) {
        // handle the mouse event here
        return true;
    }
    return false;
});

โปรดดูรายละเอียดเกี่ยวกับการจัดการอินพุตจากเมาส์ใน เอกสารประกอบของ ChromeOS

การจัดการการเคลื่อนไหวของเมาส์

หากต้องการตรวจหาการเคลื่อนไหวของเมาส์ ให้ฟังเหตุการณ์ ACTION_HOVER_ENTER, ACTION_HOVER_EXIT และ ACTION_HOVER_MOVE

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

Kotlin

gameView.setOnGenericMotionListener { _, motionEvent ->
   var handled = false
   if (motionEvent.isFromSource(InputDevice.SOURCE_CLASS_POINTER)) {
       when(motionEvent.action) {
           MotionEvent.ACTION_HOVER_ENTER -> Log.d("MA", "Mouse entered at ${motionEvent.x}, ${motionEvent.y}")
           MotionEvent.ACTION_HOVER_EXIT -> Log.d("MA", "Mouse exited at ${motionEvent.x}, ${motionEvent.y}")
           MotionEvent.ACTION_HOVER_MOVE -> Log.d("MA", "Mouse hovered at ${motionEvent.x}, ${motionEvent.y}")
       }
       handled = true
   }

   handled
}

Java

gameView.setOnGenericMotionListener((view, motionEvent) -> {
    if (motionEvent.isFromSource(InputDevice.SOURCE_CLASS_POINTER)) {
        switch (motionEvent.getAction()) {
            case MotionEvent.ACTION_HOVER_ENTER:
                Log.d("MA", "Mouse entered at " + motionEvent.getX() + ", " + motionEvent.getY());
                break;
            case MotionEvent.ACTION_HOVER_EXIT:
                Log.d("MA", "Mouse exited at " + motionEvent.getX() + ", " + motionEvent.getY());
                break;
            case MotionEvent.ACTION_HOVER_MOVE:
                Log.d("MA", "Mouse hovered at " + motionEvent.getX() + ", " + motionEvent.getY());
                break;
        }
        return true;
    }
    return false;
});

การจัดการปุ่มเมาส์

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

หากต้องการจัดการการกดปุ่ม ให้ใช้ ACTION_DOWN และ ACTION_UP จากนั้นใช้ getActionButton เพื่อกำหนดว่าปุ่มใดเป็นตัวกระตุ้นการดำเนินการ หรือใช้ getButtonState เพื่อดูสถานะของปุ่มทั้งหมด

ในตัวอย่างนี้ เราใช้ Enum เพื่อช่วยแสดงผลลัพธ์ของ getActionButton

Kotlin

enum class MouseButton {
   LEFT,
   RIGHT,
   UNKNOWN;
   companion object {
       fun fromMotionEvent(motionEvent: MotionEvent): MouseButton {
           return when (motionEvent.actionButton) {
               MotionEvent.BUTTON_PRIMARY -> LEFT
               MotionEvent.BUTTON_SECONDARY -> RIGHT
               else -> UNKNOWN
           }
       }
   }
}

Java

enum MouseButton {
    LEFT,
    RIGHT,
    MIDDLE,
    UNKNOWN;
    static MouseButton fromMotionEvent(MotionEvent motionEvent) {
        switch (motionEvent.getActionButton()) {
            case MotionEvent.BUTTON_PRIMARY:
                return MouseButton.LEFT;
            case MotionEvent.BUTTON_SECONDARY:
                return MouseButton.RIGHT;
            default:
                return MouseButton.UNKNOWN;
        }
    }
}

ในตัวอย่างนี้ ระบบจะจัดการการดำเนินการในลักษณะเดียวกับเหตุการณ์วางเมาส์เหนือ

Kotlin

// Handle the generic motion event
gameView.setOnGenericMotionListener { _, motionEvent ->
   var handled = false
   if (motionEvent.isFromSource(InputDevice.SOURCE_CLASS_POINTER)) {
       when (motionEvent.action) {
           MotionEvent.ACTION_BUTTON_PRESS -> Log.d(
               "MA",
               "${MouseButton.fromMotionEvent(motionEvent)} pressed at ${motionEvent.x}, ${motionEvent.y}"
           )
           MotionEvent.ACTION_BUTTON_RELEASE -> Log.d(
               "MA",
               "${MouseButton.fromMotionEvent(motionEvent)} released at ${motionEvent.x}, ${motionEvent.y}"
           )
       }
       handled = true
   }

   handled
}

Java

gameView.setOnGenericMotionListener((view, motionEvent) -> {
    if (motionEvent.isFromSource(InputDevice.SOURCE_CLASS_POINTER)) {
        switch (motionEvent.getAction()) {
            case MotionEvent.ACTION_BUTTON_PRESS:
                Log.d("MA", MouseButton.fromMotionEvent(motionEvent) + " pressed at " + motionEvent.getX() + ", " + motionEvent.getY());
                break;
            case MotionEvent.ACTION_BUTTON_RELEASE:
                Log.d("MA", MouseButton.fromMotionEvent(motionEvent) + " released at " + motionEvent.getX() + ", " + motionEvent.getY());
                break;
        }
        return true;
    }
    return false;
});

จัดการการเลื่อนด้วยล้อเลื่อนของเมาส์

เราขอแนะนำให้คุณใช้ล้อเลื่อนของเมาส์แทนท่าทางบีบเพื่อซูมหรือพื้นที่เลื่อนแบบแตะแล้วลากในเกม

หากต้องการอ่านค่าล้อเลื่อน ให้ฟังเหตุการณ์ ACTION_SCROLL คุณสามารถดึงข้อมูล delta ตั้งแต่เฟรมล่าสุดได้โดยใช้ getAxisValue กับ AXIS_VSCROLL สำหรับออฟเซ็ตแนวตั้ง และ AXIS_HSCROLL สำหรับออฟเซ็ตแนวนอน เช่น

Kotlin

gameView.setOnGenericMotionListener { _, motionEvent ->
   var handled = false
   if (motionEvent.isFromSource(InputDevice.SOURCE_CLASS_POINTER)) {
       when (motionEvent.action) {
           MotionEvent.ACTION_SCROLL -> {
               val scrollX = motionEvent.getAxisValue(MotionEvent.AXIS_HSCROLL)
               val scrollY = motionEvent.getAxisValue(MotionEvent.AXIS_VSCROLL)
               Log.d("MA", "Mouse scrolled $scrollX, $scrollY")
           }
       }
       handled = true
   }
   handled
}

Java

gameView.setOnGenericMotionListener((view, motionEvent) -> {
    if (motionEvent.isFromSource(InputDevice.SOURCE_CLASS_POINTER)) {
        switch (motionEvent.getAction()) {
            case MotionEvent.ACTION_SCROLL:
                float scrollX = motionEvent.getAxisValue(MotionEvent.AXIS_HSCROLL);
                float scrollY = motionEvent.getAxisValue(MotionEvent.AXIS_VSCROLL);
                Log.d("MA", "Mouse scrolled " + scrollX + ", " + scrollY);
                break;
        }
        return true;
    }
    return false;
});

จับอินพุตจากเมาส์

บางเกมจำเป็นต้องควบคุมเคอร์เซอร์ของเมาส์อย่างเต็มที่ เช่น เกมแอ็กชันมุมมองบุคคลที่หนึ่งหรือบุคคลที่สามที่แมปการเคลื่อนไหวของเมาส์กับการเคลื่อนไหวของกล้อง หากต้องการควบคุมเมาส์แบบพิเศษ ให้เรียกใช้ View.requestPointerCapture()

requestPointerCapture() จะทำงานได้ก็ต่อเมื่อลำดับชั้นการแสดงผลที่มีมุมมองของคุณมีโฟกัส ด้วยเหตุนี้ คุณจึงไม่สามารถรับการจับตัวชี้ในโค้ดเรียกกลับ onCreate ได้ คุณควรรอให้ผู้เล่นโต้ตอบเพื่อจับ เคอร์เซอร์เมาส์ เช่น เมื่อโต้ตอบกับเมนูหลัก หรือใช้ onWindowFocusChanged โค้ดเรียกกลับ เช่น

Kotlin

override fun onWindowFocusChanged(hasFocus: Boolean) {
   super.onWindowFocusChanged(hasFocus)

   if (hasFocus) {
       gameView.requestPointerCapture()
   }
}

Java

@Override
public void onWindowFocusChanged(boolean hasFocus) {
    super.onWindowFocusChanged(hasFocus);

    if (hasFocus) {
        View gameView = findViewById(R.id.game_view);
        gameView.requestPointerCapture();
    }
}

ระบบจะส่งเหตุการณ์ที่จับโดย requestPointerCapture() ไปยังมุมมองที่โฟกัสได้ซึ่งลงทะเบียน OnCapturedPointerListener เช่น

Kotlin

gameView.focusable = View.FOCUSABLE
gameView.setOnCapturedPointerListener { _, motionEvent ->
    Log.d("MA", "${motionEvent.x}, ${motionEvent.y}, ${motionEvent.actionButton}")
    true
}

Java

gameView.setFocusable(true);
gameView.setOnCapturedPointerListener((view, motionEvent) -> {
    Log.d("MA", motionEvent.getX() + ", " + motionEvent.getY() + ", " + motionEvent.getActionButton());
    return true;
});

หากต้องการปล่อยการจับเมาส์แบบพิเศษ เช่น เพื่ออนุญาตให้ผู้เล่นโต้ตอบกับเมนูหยุดชั่วคราว ให้เรียกใช้ View.releasePointerCapture()