เปิดใช้งาน callback อินพุตบนหน้าจอล็อก¶
เมื่อ PC โฮสต์เปลี่ยนเป็นหน้าจอล็อกของ Windows อินพุตคีย์บอร์ดและเมาส์ในระดับระบบปฏิบัติการ (OS) จะถูกบล็อกโดยนโยบายความปลอดภัยของ OS ในสภาพแวดล้อมระยะไกล ยังสามารถสตรีมหน้าจอและเสียงได้ แต่จะไม่สามารถควบคุมเกมจริงได้
ก่อนหน้านี้ ผู้ใช้ระยะไกลต้องป้อนรหัสผ่านบัญชี Windows เพื่อปลดล็อก PC อย่างไรก็ตาม วิธีนี้มีความเสี่ยงจากการเปิดเผยสิทธิ์ควบคุม PC ทั้งหมด และทำให้ขั้นตอนการเชื่อมต่อยุ่งยาก
Remote Play Plugin มีฟีเจอร์ เล่นโดยไม่ปลดล็อกหน้าจอล็อก ในการตั้งค่า Remote Play ของ Hive Console เมื่อเปิดใช้งานฟีเจอร์นี้ อินพุตทั้งหมด เช่น คีย์บอร์ด เมาส์ ทัช จอยสติ๊ก วีล และซูม จะถูกส่งตรงไปยัง callback ของเกม
พฤติกรรมหลัก¶
- การส่งผ่าน callback: อินพุตทั้งหมดจะถูกส่งไปยังฟังก์ชัน callback ที่ลงทะเบียนด้วย
RegisterCallbackในรูปแบบeventType: "Control"โดยทำงานเหมือนกันไม่ว่าสถานะจะล็อกหรือไม่ และยังส่งผ่าน callback ต่อไปแม้ปลดล็อกหน้าจอแล้ว - ข้ามเส้นทางอินพุตของ OS: จะไม่ใช้เส้นทาง
SendInputของ OS อีกต่อไป จึงรับอินพุตได้อย่างสม่ำเสมอไม่ว่า Windows จะล็อกอยู่หรือไม่ - ฉีดเข้าเกมโดยตรง: เกมจะนำข้อมูลอินพุตที่ได้รับผ่าน callback ส่งเข้า internal input system โดยตรง และประมวลผลการควบคุมเหมือนกันทั้งในสถานะล็อกและสถานะปกติ
สิ่งที่ต้องเตรียม¶
เตรียมรายการต่อไปนี้ก่อนใช้ฟีเจอร์นี้
- Import Remote Play Plugin
- ลงทะเบียนฟังก์ชัน callback เพื่อรับเหตุการณ์
- เวอร์ชัน SDK / Plugin ที่รองรับ:
- Remote Play Plugin 1.2.0 หรือสูงกว่า
- Hive SDK v4 26.4.0 หรือสูงกว่า
Warning
หากเปิดใช้งานฟีเจอร์ เล่นโดยไม่ปลดล็อกหน้าจอล็อก ใน Hive Console โดยไม่ได้ลงทะเบียน callback ผ่าน RegisterCallback อินพุตทั้งหมดจะสูญหายและทำให้ควบคุมระยะไกลได้ยาก ต้องลงทะเบียน callback ก่อนเสมอ
โปรโตคอลเหตุการณ์ Control¶
หลังจากเปิดใช้งานฟีเจอร์ เล่นโดยไม่ปลดล็อกหน้าจอล็อก อินพุตที่ส่งผ่าน callback จะใช้รูปแบบ JSON เดียวกับ เหตุการณ์แชท และค่า eventType จะถูกกำหนดเป็น "Control" เสมอ ประเภทอินพุตจริงจะแยกด้วย controlType ภายใน
โครงสร้าง envelope ทั่วไป¶
{
"version" : "1.02.00",
"eventType" : "Control",
"eventValue": {
"value": {
"controlType" : "<Key | Click | Wheel | Zoom>",
"controlValue": {
"value" : "<controlType-specific-value>",
"action" : "<controlType-specific-action>"
}
}
},
"etc" : { }
}
| ฟิลด์ | คำอธิบาย |
|---|---|
version | กำหนดเป็น "1.02.00" |
eventType | เป็น "Control" เสมอ |
eventValue.value.controlType | ประเภทอินพุต มี 4 ประเภท: "Key" / "Click" / "Wheel" / "Zoom" |
eventValue.value.controlValue.value | ข้อมูลตาม controlType เช่น พิกัดหรือคีย์โค้ด |
eventValue.value.controlValue.action | สถานะหรือ action ตาม controlType |
etc | ออบเจ็กต์ว่าง |
controlType = "Key" - อินพุตคีย์บอร์ด¶
ส่งสถานะกดหรือปล่อยของคีย์เดี่ยวบนคีย์บอร์ด ทั้งอินพุตคีย์บอร์ดและอินพุตจอยสติ๊กที่ผู้ใช้สร้างใน web client จะถูกแปลงเป็น controlType
| ฟิลด์ | ค่า |
|---|---|
controlValue.value | สตริงเลขฐานสิบหกของ Windows Virtual-Key Code เช่น "0x41" = 'A', "0x57" = 'W', "0x20" = Space |
controlValue.action | "Down" หรือ "Up" |
โค้ดทิศทางของจอยสติ๊กจะถูกแยกเป็นเหตุการณ์คีย์เดี่ยวตามการตั้งค่า mapping (WASD / DIRECTION) และส่งเป็น controlType ดังนั้นฝั่งเกมจะมองเห็นอินพุตคีย์บอร์ดและจอยสติ๊กในรูปแบบเดียวกัน การเคลื่อนที่แนวทแยงจะถูกโฮสต์แยกเป็นเหตุการณ์คีย์เดี่ยว 2 รายการก่อนส่ง
ตัวอย่าง - กดคีย์ 'A'
{
"version" : "1.02.00",
"eventType" : "Control",
"eventValue": {
"value": {
"controlType" : "Key",
"controlValue": { "value": "0x41", "action": "Down" }
}
},
"etc" : { }
}
ตัวอย่าง - จอยสติ๊กขึ้น (คีย์ 'W' เมื่อใช้ WASD mapping)
{
"version" : "1.02.00",
"eventType" : "Control",
"eventValue": {
"value": {
"controlType" : "Key",
"controlValue": { "value": "0x57", "action": "Down" }
}
},
"etc" : { }
}
controlType = "Click" - อินพุตพิกัดเมาส์และทัช¶
ส่งพิกัดคลิกเมาส์ การเคลื่อนที่ของเมาส์ และทัช ทั้งอินพุตเมาส์และทัชที่ผู้ใช้สร้างใน web client จะถูกแปลงอัตโนมัติเป็นพิกัดพิกเซลของ client area ในหน้าต่างเกมโดยอิงจาก GetClientRect() ก่อนส่ง
| ฟิลด์ | ค่า |
|---|---|
controlValue.value | รูปแบบ "x#y" หน่วยคือ พิกัดพิกเซลของ client area ในหน้าต่างเกม โดยมีทศนิยม 1 ตำแหน่ง พิกัด normalized 0-1000 จาก web client จะถูกโฮสต์แปลงเป็นพิกเซลก่อนส่ง |
controlValue.action | "Down", "Up" หรือ "Move" |
Note
สำหรับอินพุตทัช ข้อมูล touchID จะไม่ถูกเก็บไว้ระหว่างการแปลงเป็นประเภท Click
ข้อมูลอ้างอิงการแปลงพิกัด¶
curWidth / curHeight คือขนาด client area ของหน้าต่างเกมตาม GetClientRect() ดังนั้นในเกมให้ใช้เป็นพิกัดหน้าต่างได้โดยตรง
| ตำแหน่งใน web client | พิกัด normalized (inbound) | ตัวอย่างพิกัดพิกเซล (curWidth=1053, curHeight=1585) |
|---|---|---|
| ซ้ายบน | "0.0#0.0" | "0.0#0.0" |
| ขวาบน | "1000.0#0.0" | "1053.0#0.0" |
| ซ้ายล่าง | "0.0#1000.0" | "0.0#1585.0" |
| ขวาล่าง | "1000.0#1000.0" | "1053.0#1585.0" |
| กึ่งกลาง | "500.0#500.0" | "526.5#792.5" |
ตัวอย่าง - คลิกเมาส์ (Down -> Up)
{
"version" : "1.02.00",
"eventType" : "Control",
"eventValue": {
"value": {
"controlType" : "Click",
"controlValue": { "value": "526.5#792.5", "action": "Down" }
}
},
"etc" : { }
}
{
"version" : "1.02.00",
"eventType" : "Control",
"eventValue": {
"value": {
"controlType" : "Click",
"controlValue": { "value": "526.5#792.5", "action": "Up" }
}
},
"etc" : { }
}
ตัวอย่าง - ลาก (Move)
{
"version" : "1.02.00",
"eventType" : "Control",
"eventValue": {
"value": {
"controlType" : "Click",
"controlValue": { "value": "645.1#681.7", "action": "Move" }
}
},
"etc" : { }
}
controlType = "Wheel" - วีลเมาส์¶
ส่ง action การเลื่อนด้วยวีลเมาส์
| ฟิลด์ | ค่า |
|---|---|
controlValue.value | รูปแบบ "x.0#y.0" เป็น พิกัดพิกเซลปัจจุบันของเคอร์เซอร์เมาส์ โดยอิงจาก client area ของหน้าต่างเกม ข้อมูล step การเลื่อนที่ web client ส่งมาจะสูญหาย |
controlValue.action | "Up" (เลื่อนขึ้น) หรือ "Down" (เลื่อนลง) |
ตัวอย่าง - wheel down (ตำแหน่งเมาส์ 1234.0 px, 567.0 px)
{
"version" : "1.02.00",
"eventType" : "Control",
"eventValue": {
"value": {
"controlType" : "Wheel",
"controlValue": { "value": "1234.0#567.0", "action": "Down" }
}
},
"etc" : { }
}
controlType = "Zoom" - ซูมเข้าและซูมออก¶
ส่ง action ซูมเข้าและซูมออกสำหรับกล้องหรือหน้าจอแผนที่
| ฟิลด์ | ค่า |
|---|---|
controlValue.value | กำหนดเป็น "1" เสมอ ข้อมูล step การเลื่อนที่ web client ส่งมาจะสูญหาย |
controlValue.action | "In" (ซูมเข้า) หรือ "Out" (ซูมออก) |
ตัวอย่าง - ซูมเข้า
{
"version" : "1.02.00",
"eventType" : "Control",
"eventValue": {
"value": {
"controlType" : "Zoom",
"controlValue": { "value": "1", "action": "In" }
}
},
"etc" : { }
}
ตัวอย่าง - ซูมออก
{
"version" : "1.02.00",
"eventType" : "Control",
"eventValue": {
"value": {
"controlType" : "Zoom",
"controlValue": { "value": "1", "action": "Out" }
}
},
"etc" : { }
}
สรุป mapping การแปลงประเภทอินพุต¶
ตารางต่อไปนี้แสดงโครงสร้าง mapping ที่แปลงประเภทอินพุตต้นทางจาก web client เป็นเหตุการณ์ callback ของโฮสต์
| อินพุตจาก web client | Callback controlType | การแปลงหลัก / ข้อมูลที่สูญหาย |
|---|---|---|
| Key | "Key" | ค่า VK เลขฐานสิบหกคงเดิม |
| Joystick | "Key" | โค้ดทิศทาง -> WASD / DIRECTION mapping -> แยกเป็นคีย์เดี่ยว |
| Click | "Click" | พิกัด normalized -> แปลงเป็นพิกเซล |
| Touch | "Click" | พิกัด normalized -> แปลงเป็นพิกเซล, ไม่ส่ง touchID (ตัดสิน multi-touch ด้วย Down ซ้ำ) |
| Wheel | "Wheel" | ข้อมูล step -> แทนที่ด้วยพิกัดพิกเซลของเมาส์ |
| Zoom | "Zoom" | ข้อมูล step -> กำหนดเป็น "1" เสมอ |
เหตุการณ์สถานะ (Event) และแชท (Message) จะไม่ถูกส่งผ่านเหตุการณ์ Control นี้ แต่จะถูกส่งแยกตามข้อกำหนดโปรโตคอลเดิม
คู่มือการประมวลผลอินพุตฝั่งเกม¶
เมื่อเปิดใช้งานฟีเจอร์ เล่นโดยไม่ปลดล็อกหน้าจอล็อก ในการตั้งค่า Remote Play ของ Hive Console แล้ว Remote Play จะไม่ส่งอินพุตไปยังคิวอินพุตของระบบปฏิบัติการ (OS) แต่จะส่งตรงไปยังเกมผ่าน callback ที่ลงทะเบียนไว้
ดังนั้น อินพุตที่ได้รับจาก callback ต้องถูกส่งตรงไปยังระบบอินพุตภายในเกม เช่น Unity Input System หรือ Unreal Input Subsystem เพื่อประมวลผล
วิธีนี้เป็นโครงสร้างสำหรับหลีกเลี่ยงข้อจำกัดของ Windows lock screen ที่จำกัดการฉีดอินพุตระดับ OS เช่น SendInput และ SendKey เนื่องจากอินพุตถูก dispatch โดยตรงภายในโปรเซสเกม จึงแนะนำให้ใช้เส้นทางประมวลผลเดียวกันไม่ว่าสถานะจะล็อกหรือปกติ
กล่าวคือ หลังจากเปิดใช้งานฟีเจอร์ เล่นโดยไม่ปลดล็อกหน้าจอล็อก ควร implement ให้เกมรับอินพุตทั้งหมดที่ส่งผ่าน callback นี้และส่งต่อไปยังระบบอินพุตภายในเกม เพื่อให้การประมวลผลอินพุตและ Remote Play ทำงานสม่ำเสมอไม่ว่าสถานะหน้าจอล็อกจะเป็นอย่างไร
- Unity: Dispatch ด้วย
InputSystem.QueueEventหรือ input manager ที่กำหนดเอง - Unreal Engine: Dispatch ด้วย Slate event เช่น
FSlateApplication::OnKeyDown / OnMouseButtonDownหรือใช้ input manager ของเกมเอง
คู่มือการเรนเดอร์และเสียงเบื้องหลังในสภาพแวดล้อมหน้าจอล็อก¶
หากต้องการใช้ Remote Play ในสภาพแวดล้อมหน้าจอล็อกของ Windows เกมต้องสามารถทำงานได้ตามปกติแม้อยู่ใน background เกมเอนจินส่วนใหญ่จะหยุด rendering จำกัด frame หรือหยุดเสียงชั่วคราวเพื่อลดการใช้ CPU เมื่อเสีย focus หรือเข้าสู่ background ดังนั้นเพื่อรักษาการสตรีมระยะไกลให้ปกติ ต้องตั้งค่าให้โปรเซสเกมทำงานต่อใน background
1. นโยบายที่แนะนำตามสถานะการเชื่อมต่อ Remote Play¶
เกมต้องตรวจสอบสถานะการเชื่อมต่อ Remote Play เพื่อ判断ว่ามีผู้ใช้ระยะไกลเชื่อมต่ออยู่หรือไม่ พฤติกรรมที่แนะนำมีดังนี้
| สถานะการเชื่อมต่อ | พฤติกรรมที่แนะนำ |
|---|---|
| ไม่ได้เชื่อมต่อ | คงนโยบายเดิมของเกมไว้ สามารถหยุด rendering และเสียงเมื่อเข้าสู่ background ได้ |
| กำลังเชื่อมต่อ | คง rendering ไว้แม้อยู่ใน background และคงการส่งออกเสียงไว้แม้อยู่ใน background |
สามารถ判断สถานะการเชื่อมต่อได้ผ่าน callback เหตุการณ์สถานะ (eventType: "Event", value REMOTE_PLAY_CONNECTED / REMOTE_PLAY_DISCONNECTED)
2. คงการเรนเดอร์เบื้องหลัง¶
ต้องให้ rendering loop ทำงานต่อ เพื่อให้ Remote Play จับภาพหน้าจอได้แม้ Windows จะล็อกอยู่ ตัวอย่างรายการตรวจสอบมีดังนี้
- อย่าหยุด frame update
- คง render loop ไว้
- คง camera rendering ไว้
- คงการอัปเดต UI ไว้
- ตรวจสอบนโยบายจำกัด frame ใน background
Warning
หาก rendering หยุดใน background Remote Play อาจส่งหน้าจอล่าสุดต่อไปหรือแสดงหน้าจอสีดำ
3. คงเอาต์พุตเสียงเบื้องหลัง¶
Remote Play จับเสียงที่ออกจากโปรเซสเกมและส่งให้ผู้เล่นระยะไกล ดังนั้นต้องปิดนโยบายที่บล็อกเสียงใน background เช่นรายการต่อไปนี้
- หยุดเสียงชั่วคราวเมื่อ Focus Lost
- หยุด Audio Engine เมื่อ Window Deactivate
- เปิดใช้งาน Background Mute
Warning
หากเอาต์พุตเสียงหยุดใน background ผู้เล่นระยะไกลจะไม่ได้ยินเสียงเกม
รายการตรวจสอบและข้อมูลอ้างอิงตามเอนจิน¶
Unity¶
โดยค่าเริ่มต้น Unity อาจจำกัดการอัปเดตเมื่อเกมเข้าสู่ background เมื่อใช้ฟีเจอร์ Remote Play บนหน้าจอล็อก แนะนำให้เปิดใช้งานการทำงานใน background ดังนี้
หากมี logic แบบ custom ที่หยุดเสียงเมื่อเสีย focus ให้จัดการเป็นข้อยกเว้นโดยพิจารณาสถานะการเชื่อมต่อ Remote Play
Unreal Engine¶
ในโปรเจกต์ Unreal Engine ต้องตรวจสอบว่ามีการตั้งค่าที่จำกัด Tick, rendering หรือการประมวลผลเสียงเมื่อเสีย focus หรือไม่ แนะนำให้ตรวจสอบรายการต่อไปนี้เป็นพิเศษ
- Background Tick ทำงานหรือไม่
- มีการ Pause เมื่อ Focus Lost หรือไม่
- Audio Mixer ทำงานหรือไม่
- นโยบายจำกัด FPS ใน background
ขณะเชื่อมต่อ Remote Play ต้อง implement ให้ game loop ปกติคงอยู่แม้อยู่ใน background
สถานการณ์การทำงานที่แนะนำ¶
- ผู้ใช้เปลี่ยนเป็นหน้าจอล็อกของ Windows
- Remote Play client เชื่อมต่อ
- เกมตรวจพบสถานะการเชื่อมต่อและคง background rendering กับ audio output ไว้
- การเรนเดอร์เกมดำเนินต่อ
- เอาต์พุตเสียงเกมดำเนินต่อ
- Remote Play Plugin จับภาพหน้าจอและเสียงแล้วส่งได้ตามปกติ
- ผู้ใช้ระยะไกลรับภาพและเสียงเกมแบบ real time
- อินพุตถูกส่งไปยังระบบอินพุตภายในเกมผ่าน callback อินพุต
Controlของ Remote Play
สรุปรายการตรวจสอบที่จำเป็น
เพื่อให้ Remote Play ทำงานได้ตามปกติในสภาพแวดล้อมหน้าจอล็อก ต้องเป็นไปตามเงื่อนไขทั้งสองข้อต่อไปนี้
- เปิดใช้งานฟีเจอร์ เล่นโดยไม่ปลดล็อกหน้าจอล็อก ในการตั้งค่า Remote Play ของ Hive Console เพื่อส่งอินพุตไปยังระบบอินพุตภายในเกม
- คง rendering และ audio processing ไว้ใน background ระหว่างเชื่อมต่อ Remote Play
หากไม่เป็นไปตามเงื่อนไขข้อใดข้อหนึ่ง อาจทำงานผิดปกติ เช่น หน้าจอค้าง ข้อผิดพลาดของเอาต์พุตเสียง หรือควบคุมได้ยาก