เลือกป๊อปอัปให้ถูกแบบ
ป๊อปอัปคือการขัดจังหวะ ก่อนเลือกสักแบบ ให้ตอบคำถามข้อเดียว: ผู้เล่นต้องตอบก่อนที่เกมจะ ไปต่อหรือไม่? ถ้าใช่ ให้บล็อก ถ้าไม่ใช่ อย่าบล็อกเด็ดขาด — การ์ดที่หยุดเกมเพื่อแจ้งข่าวที่ผู้เล่นไม่ได้ขอ คือวิธีที่เร็วที่สุดที่จะทำให้เขาปิดเกมของคุณ
- คำถามที่มีคำตอบ — ซื้อ ลบ ออกจากเกม เขียนทับ →
GoDialogs.confirm()สิ่งที่ต้องรับทราบ →alert()เลือกหนึ่งจากไม่กี่ตัวเลือก →choose() - หน้าจอของมันเอง — การตั้งค่า แผ่นข้อมูลตัวละคร ไอเทมในร้านค้า →
GoSurfaceกลางหน้าจอ - รายการยาวหรือหน้าจัดการ — กระเป๋า จดหมาย เพื่อน →
GoSheetจากด้านล่างบนมือถือ หรือGoDrawerจากด้านข้างบนจอกว้าง - เรื่องของสิ่งที่เพิ่งถูกกด — ค่าสถานะของไอเทม สิ่งที่ทำกับมันได้ →
GoPopoverหรือGoContextMenuข้าง ๆ มัน - ข่าวที่เกมเล่นต่อไปได้ — บันทึกแล้ว ทิ้งของแล้ว ได้รับคำเชิญ →
GoSnackbar,GoNoticeหรือGoPromptCardสิ่งเหล่านี้ไม่ยึดหน้าจอเลย - สอนวิธีควบคุม →
GoCoachMarkสูตรโกงระหว่างพัฒนา →GoConsole
ป๊อปอัปทั้งหมดในแวบเดียว
| ป๊อปอัป | หยุดเกม | ย้อนกลับ / Escape | แตะข้างนอก | ใช้กับ |
|---|---|---|---|---|
GoDialogs confirm · alert | ใช่ | ปิด — confirm ตอบ false ส่วน alert คืนกลับมา | ปิดเฉพาะเมื่อมี dismiss_on_scrim | คำถามที่ต้องการคำตอบ |
GoDialogs.choose | ใช่ | ปิด — ตอบ -1 | ปิดเฉพาะเมื่อมี dismiss_on_scrim | เลือกหนึ่งจากไม่กี่ตัวเลือก |
GoSurface | ใช่ | ขอให้ปิดอันบนสุด | ขอให้ปิดเมื่อมี dismiss_on_scrim | หน้าต่างของคุณเอง |
GoSheet | ใช่ | ปิดทั้งชีต | ปิด | กระเป๋า รายการในร้านค้า จดหมาย |
GoDrawer | ใช่ | เฉพาะปุ่มย้อนกลับของ Android | ปิด | รายการด้านข้างบนจอกว้าง |
GoPopover | จนกว่าจะปิด | ปิด | ปิด — และไม่ส่งต่อไปไหนอีก | รายละเอียดไอเทมและสกิล |
รายการของ GoCombobox | จนกว่าจะปิด | ปิด | ปิด | รายการให้เลือกที่ค้นหาได้ |
GoContextMenu · ดรอปดาวน์ · ซีเล็กต์ | ตามเมนูของเอนจิน | ตามเมนูของเอนจิน | ปิด | การกระทำ รายการสั้น ๆ |
GoSnackbar | ไม่ | — | ส่งถึงเกม | บันทึกแล้ว · เลิกทำ · ลองใหม่ |
GoPromptCard | ไม่ | — | ส่งถึงเกม | คำเชิญ คำขอแลกเปลี่ยน |
GoNotice | ไม่เลย | — | ส่งถึงเกม | บรรทัดประจำที่บน HUD |
GoBanner | ไม่ — มันอยู่ในหน้า | — | — | "คุณออฟไลน์อยู่ ลองใหม่" |
GoCoachMark | เฉพาะการ์ดของมัน | จบทัวร์ | ส่งถึงเกม | ทัวร์ตอนเปิดเกมครั้งแรก |
GoConsole | เฉพาะพาเนลของมัน | ปุ่ม Escape ปิดมัน | ส่งถึงเกม | คำสั่งดีบัก |
แต่ละอันเปิดบนเลเยอร์ไหน และทำไมจึงเรียงลำดับแบบนั้น ดูได้ที่ เลเยอร์ ปุ่มย้อนกลับ และอินพุต

GoDialogs
GoSurface
GoSheet
GoDrawer
GoPopover
GoContextMenu
GoSnackbar
GoPromptCard
GoCoachMark
GoConsoleทุกภาพในหน้านี้คือเกมเล็ก ๆ เกมเดียวกัน — พลังชีวิตและมานาที่มุมซ้ายบน เหรียญที่มุมขวาบน จอยสติ๊กและ
ควิกสล็อตสามช่องที่ด้านล่าง — ในพรีเซ็ต default_dark โดยมีป๊อปอัปหนึ่งอันเปิดทับอยู่ ป๊อปอัป
เดียวกันในทั้งสิบสี่ลุค →
กายวิภาคของป๊อปอัป
ป๊อปอัปแบบบล็อกเกือบทุกตัวใน gohud — ไดอะล็อก ชีต ป๊อปโอเวอร์ รายการของคอมโบบ็อกซ์ และหน้าต่าง
ของคุณเอง — คือโหนดเดียวกัน คือ GoSurface เรียนรู้ส่วนประกอบของมันครั้งเดียว แล้วคุณจะรู้จักทุกตัว

| ส่วนประกอบ | คืออะไร | เข้าถึงอย่างไร |
|---|---|---|
| เลเยอร์ | CanvasLayer ที่ป๊อปอัปอาศัยอยู่ หมายเลขของมันเป็นตัวตัดสินว่ามันบังอะไรบ้าง | คุณกำหนดเองสำหรับ GoSurface ส่วนตัวอื่นกำหนดไว้ในตัว (ตาราง) |
| ม่าน | ม่านที่คลุมทุกอย่างข้างหลัง คอยรับการแตะที่พลาดตัวการ์ด | scrim_transparent, dismiss_on_scrim, สี scrim ของธีม |
| การ์ด | ตัวหน้าต่างเอง — หน้าพาเนลของธีม จะมน ตัดมุม ตีขึ้นรูป หรือเป็นเยลลี่ ก็แล้วแต่ลุค | card, alpha |
| ส่วนหัว | ปุ่มย้อนกลับ (ถ้าต้องการ) ชื่อเรื่อง (ไม่เกินสองบรรทัด เกินนั้นเป็น "…") และปุ่มปิด | set_title() · set_title_key() · set_back() · close_enabled · show_header |
| แถบเครื่องมือ | แถวที่ตรึงไว้ใต้ส่วนหัว — ช่องค้นหา ชิปตัวกรอง ซ่อนอยู่จนกว่าคุณจะใช้ | toolbar |
| เนื้อหา | ส่วนที่เลื่อนได้ ใส่เนื้อหาไว้ที่นี่ | body · scroll_body · clear() |
| บรรทัดสถานะ | บรรทัดที่ตรึงไว้ระหว่างเนื้อหากับส่วนท้าย — "ตรวจชื่อไม่ผ่าน" "กำลังบันทึก…" | set_status_text() · set_status_key() · clear_status() |
| ส่วนท้าย | ปุ่มที่ตรึงไว้ด้านล่าง ซ่อนอยู่จนกว่าคุณจะใช้ | footer |
toolbar บรรทัดสถานะ และ footer เพราะมันอยู่กับที่ขณะที่เนื้อหาเลื่อน
ไดอะล็อกที่ await ได้
GoDialogs ถามแล้วรอ โหนดเดียว สามคำถาม — และคำตอบกลับมาที่บรรทัดเดิม
โค้ดจึงอ่านเรียงตามลำดับเดียวกับที่ผู้เล่นได้สัมผัส
var dialogs := GoDialogs.new()
add_child(dialogs)
if await dialogs.confirm("Buy the starter pack?", "300 gems for a sword, a shield and 10 potions.",
"Buy", "Not now"):
buy_starter_pack()

confirm()
confirm(…, true) — ย้อนกลับไม่ได้
alert()
choose()
ActionLayout.HORIZONTALสามคำถาม
confirm(title, body, ok_text := "", cancel_text := "", extra := "",
args := {}, destructive := false) -> bool
alert(title, body, ok_text := "", extra := "", args := {}) # ใช้ await เพื่อรอ
choose(title, options: Array, cancel_text := "", translate := false) -> int
confirm_key(…) · alert_key(…) # รับคีย์คำแปล
| การเรียก | คืนค่า | ใช้กับ |
|---|---|---|
confirm() | true หรือ false | ใช่หรือไม่ — ซื้อ ลบ ออกจากเกม เขียนทับ |
alert() | ไม่คืนค่า — await มันเพื่อรอ | สิ่งที่ผู้เล่นต้องเห็น: รางวัล การเชื่อมต่อที่หลุด |
choose() | ดัชนีของตัวเลือก หรือ -1 | หนึ่งจากไม่กี่ตัวเลือก ตัวเลือกหนึ่งตัวคือ String หรือ {text, icon, danger, subtitle} |
confirm_key() · alert_key() | เหมือนข้างบน | แบบเดียวกันแต่ใช้คีย์คำแปล — ข้อความเปลี่ยนตามเมื่อเปลี่ยนภาษาขณะที่ไดอะล็อกเปิดอยู่ |
ป้ายปุ่มที่ปล่อยว่างจะย้อนกลับไปใช้คำของชุดเอง (confirm, cancel ใน GoConfig.text_keys)
extra จะถูกต่อท้ายเนื้อความในบรรทัดใหม่ args เติม {placeholders} ทั้งในชื่อเรื่อง
และเนื้อความ: confirm("Buy {item}?", "It costs {price} gold.", "Buy", "", "", {"item": name, "price": 300})
ทุกทางออกมีคำตอบ
| ผู้เล่น… | confirm | alert | choose |
|---|---|---|---|
| กด OK / กดตัวเลือก | true | คืนกลับมา | ดัชนีของตัวเลือก |
| กดยกเลิก | false | — | -1 |
| กด × Escape หรือปุ่มย้อนกลับของ Android | false | คืนกลับมา | -1 |
แตะม่าน (เฉพาะเมื่อมี dismiss_on_scrim) | false | คืนกลับมา | -1 |
โหนด GoDialogs ออกจากทรี | false | คืนกลับมา | -1 |
โค้ดหลัง await จึงได้ทำงานเสมอ — ไม่มีคำถามไหนถูกปล่อยค้างไว้ แม้ฉากจะเปลี่ยนไปใต้ไดอะล็อกก็ตาม
การกระทำที่ย้อนกลับไม่ได้
ส่ง destructive = true สำหรับสิ่งที่ย้อนกลับไม่ได้ ปุ่ม OK จะกลายเป็นปุ่มทึบ
Tone.DANGER_SOLID — ตัวอักษรขาวบนพื้นแดงในทุกลุค ในขณะที่ป้ายข้อความสีอันตรายแบบย้อมสี
ต้องเข้มลงจนเกือบดำบนธีมสว่างจึงจะยังอ่านออก โทนสีถูกรีเซ็ตทุกครั้งที่เรียก เพราะหน้าต่างถูกใช้ซ้ำ
if await dialogs.confirm("Delete this save?", "Slot 2 — Aria, level 12. This cannot be undone.",
"Delete", "Keep it", "", {}, true):
delete_save(2)
การจัดวางปุ่ม
action_layout | ทำอะไร |
|---|---|
VERTICAL (ค่าเริ่มต้น) | ปุ่มซ้อนกันลงมา เต็มความกว้าง ปลอดภัยกับทุกภาษา — ป้ายภาษาเยอรมันหรือรัสเซียยาว ๆ ไม่มีวันถูกบีบ |
HORIZONTAL | แถวเดียว ปุ่มละครึ่งความกว้าง |
AUTO | แถวเดียวก็ต่อเมื่อป้ายทั้งสองพอดีกับครึ่งการ์ดในบรรทัดเดียว ไม่อย่างนั้นก็ซ้อนกัน ตัดสินใหม่ทุกครั้งที่เปลี่ยนภาษาหรือปรับขนาด |
action_gap คือช่องไฟระหว่างปุ่ม ส่วน body_gap คือช่องไฟเหนือปุ่ม — ตั้งให้กว้างกว่า
ช่องไฟระหว่างปุ่ม เพื่อให้คำถามกับตัวเลือกอ่านออกมาเป็นสองส่วน ถ้าจะเปลี่ยนเฉพาะไดอะล็อกเดียว ให้เรียก
set_next_action_layout() ก่อนเปิดมัน ปุ่มเดียวของ alert ใช้ความกว้างเต็มเสมอ
ทีละอัน
การเรียก confirm() ครั้งที่สองขณะที่ยังมีอันหนึ่งเปิดอยู่ จะคืนค่า false ทันที — ไดอะล็อก
ไม่มีทางซ้อนบนไดอะล็อกโดยไม่ตั้งใจ เปิด queue_when_busy แล้วมันจะรอคิวแทน ส่วน alert() รอคิวเสมอ
queue_limit (8) จำกัดความยาวคิว และเมื่อคิวล้น คำขอที่เก่าที่สุดจะได้คำตอบ false
pending() นับคำขอที่รออยู่ และ clear_pending() ตอบ false ให้ทั้งหมด
choose() ไม่เข้าคิวเลย: มันคืนค่า -1 เมื่อมีไดอะล็อกเปิดอยู่แล้ว
มือถือและเดสก์ท็อป
choose() เปิดเป็นชีตจากด้านล่างบน Android และ iOS — ตรงที่นิ้วโป้งอยู่ — และเป็นการ์ดกลางจอ
บนเดสก์ท็อป กล่องยืนยันกว้างไม่เกิน max_width (420dp) และจัดกึ่งกลาง ตั้ง layer_index,
max_width และค่า export อื่น ๆ ก่อน add_child เพราะค่าเหล่านี้ถูกนำไปใช้ตอนที่โหนดเข้าสู่ทรี
var picked := await dialogs.choose("Sort the bag by", [
{"text": "Newest first", "icon": GoIconSet.CLOCK},
{"text": "Rarity", "icon": GoIconSet.STAR},
{"text": "Drop everything", "icon": GoIconSet.TRASH, "danger": true}])
if picked >= 0:
sort_bag(picked)
GoSurface — หน้าต่างของคุณเอง
เมื่อป๊อปอัปเป็นมากกว่าคำถาม — การตั้งค่า แผ่นข้อมูลตัวละคร ไอเทมที่จะซื้อ — ให้สร้างบน
GoSurface เซฟแอเรีย คีย์บอร์ด แถวที่ตรึงไว้ การเลื่อน ปุ่มย้อนกลับ โฟกัส และการปรับขนาด จัดการไว้ให้หมดแล้ว
var layer := CanvasLayer.new()
layer.layer = 50
add_child(layer)
var window := GoSurface.new()
window.set_title("Settings")
window.close_requested.connect(layer.queue_free)
window.body.add_child(GoStyle.toggle("Music", false))
window.body.add_child(GoStyle.slider(0.0, 1.0, 0.05))
var row := GoStyle.row()
row.add_child(GoStyle.button("Cancel", window.request_close))
row.add_child(GoStyle.button("Save", save_and_close, GoStyle.Tone.PRIMARY))
window.footer.add_child(row)
window.footer.visible = true
layer.add_child(window)
close_requested ออกมา การซ่อนหรือ
คืนหน่วยความจำของหน้าต่างเป็นหน้าที่ของหน้าจอที่เป็นเจ้าของ — มีแต่มันที่รู้ว่า "ปิด" หมายถึงบันทึก ทิ้ง หรือถามก่อน
สามตำแหน่งการวาง

CENTER — กล่องยืนยัน การตั้งค่า
BOTTOM — รายการ หน้าจัดการ
ANCHOR — ข้างคอนโทรล
GoDatePicker ในหน้าต่าง CENTERANCHOR ต้องมี anchor_control การ์ดจะไปอยู่ทางขวาของมันถ้า anchor_min_width พอดี
ไม่อย่างนั้นไปด้านที่กว้างกว่า ไม่อย่างนั้นก็อยู่ภายในหน้าจอ และจะเปิดขึ้นด้านบนเมื่อด้านบนมีที่มากกว่า มันตาม
จุดตรึงที่เคลื่อนที่ทุกเฟรม anchor_width (320) และ anchor_max_height (520) กำหนดขนาดของมัน
ใหญ่ได้แค่ไหน
| ค่าที่วัด | กฎ | เปลี่ยนด้วย |
|---|---|---|
| พื้นที่ | เซฟแอเรีย ลบคีย์บอร์ด ลบระยะขอบจอ (16dp และไม่เกิน 10% ของด้านสั้น) | — |
| ความกว้าง | 94% ของพื้นที่ในแนวตั้ง 72% ในแนวนอน สูงสุด 480dp | max_width · GoConfig.surface_width_ratio_* |
| ความสูง | สูงเท่าเนื้อหา (fit_content) ได้ถึง 68% ของพื้นที่ โดยมีเพดานที่ 72% การ์ดกลางจอที่ปรับขนาดไม่ได้อาจโตได้ถึง 94% เพื่อเลี่ยงการเลื่อน | height_ratio · max_height_ratio · max_height |
| ความแน่น | แน่นขึ้นเมื่อตั้ง compact เมื่อหน้าจอสูงไม่ถึง 420dp (มือถือแนวนอน) หรือเมื่อเนื้อหาไม่พอดี: ระยะขอบในน้อยลง ช่องไฟเล็กลง ชื่อเรื่องเล็กลง มันวัดทั้งสองแบบแล้วเลือกแบบที่โปร่งกว่าซึ่งยังพอดี — ไม่กระตุก | compact |
| การปรับขนาด | เมื่อมี resizable ชื่อเรื่องคือที่จับ: ลากได้ระหว่าง 30% ถึงเพดาน height_changed(ratio) รายงานผลลัพธ์ | resizable |
สัดส่วนที่เกินเพดานจะถูกตัดกลับ พร้อมคำเตือนหนึ่งครั้งในบิลด์ดีบัก คีย์บอร์ดดันการ์ดขึ้นและเลื่อนช่องที่มีโฟกัส ให้มองเห็น เซฟแอเรียกันมันออกจากรอยบากและแถบท่าทางบน Android และ iOS
พร็อพเพอร์ตี้
| พร็อพเพอร์ตี้ | ค่าเริ่มต้น | ทำอะไร |
|---|---|---|
placement | CENTER | CENTER, BOTTOM หรือ ANCHOR |
dismiss_on_scrim | GoConfig.dismiss_on_scrim (false) | การแตะม่านคือการขอปิด การลากที่เริ่มในการ์ดไม่นับ |
scrim_transparent | false | ให้เกมยังมองเห็นได้ — ม่านยังคงรับการแตะข้างนอก |
close_enabled | true | แสดง × และให้ Escape กับปุ่มย้อนกลับขอปิดได้ |
show_header · scroll_body · resizable | true · true · false | แถวส่วนหัว เนื้อหาที่เลื่อนได้ และที่จับสำหรับลาก |
fit_content · compact | true · false | ปรับขนาดตามเนื้อหา และเริ่มแบบแน่น |
fade_in | GoConfig.surface_fade_in (false) | ค่อย ๆ แสดงการ์ดตอนเปิด แสดงทันทีเมื่อมี reduce_motion |
alpha | -1 (ของธีม) | ความทึบของหน้าการ์ด เป็นสัดส่วน มีแค่หน้าการ์ดที่จาง — ตัวอักษรไม่จางด้วย |
initial_focus | ไม่มี | คอนโทรลที่ได้โฟกัสตอนเปิด |
add_child: scrim_transparent, show_header, resizable และ
scroll_body ถูกนำไปใช้ตอนที่หน้าต่างเข้าสู่ทรี ส่วน dismiss_on_scrim, close_enabled,
fade_in และ initial_focus ถูกอ่านสด ๆ ทุกครั้ง
สัญญาณและการเรียก
| ชื่อ | คืออะไร |
|---|---|
close_requested | × Escape ปุ่มย้อนกลับของ Android หรือการแตะม่าน — รวมถึง request_close() ส่งออกมาจากหน้าต่างบนสุดเท่านั้น |
back_requested · set_back(action) | ปุ่มย้อนกลับในส่วนหัว สำหรับหน้าต่างที่มีหลายหน้า Callable ว่างจะซ่อนมัน |
height_changed(ratio) | หลังลากที่จับ |
is_top() · GoSurface.is_any_open() | หน้าต่างนี้คือหน้าต่างที่รับปุ่มย้อนกลับหรือไม่ — และมีหน้าต่างใดเปิดอยู่บ้างหรือไม่ |
relayout() | จัดเลย์เอาต์ใหม่เดี๋ยวนี้ การเปลี่ยนพร็อพเพอร์ตี้ด้านขนาดทำให้อยู่แล้ว |
ชีตและดรอว์เออร์
รายการที่ต้องใช้พื้นที่ จะขึ้นมาจากด้านล่างบนมือถือที่ถือแนวตั้ง — ตรงที่นิ้วโป้งอยู่ — และเข้ามา จากด้านข้างบนแท็บเล็ตหรือเดสก์ท็อป ซึ่งชีตจะไปบังเกม

GoSheet
GoDrawer · LEFT
GoDrawer · RIGHTGoSheet — หน้าที่มาจากด้านล่าง
var sheet := GoSheet.new()
add_child(sheet)
func show_bag(items: Array) -> void:
sheet.open("Bag")
var search := GoStyle.line_edit("Search the bag…")
search.text_changed.connect(filter_bag)
sheet.toolbar().add_child(search)
sheet.toolbar().visible = true
for item in items:
sheet.body.add_child(GoStyle.list_button(GoIconSet.BOX, item.name, show_item.bind(item),
Color.TRANSPARENT, item.description, false, GoIconSet.CHEVRON_RIGHT))
sheet.add_footer(GoStyle.button("Close", sheet.close, GoStyle.Tone.PRIMARY))
- มันคือ
CanvasLayer(เลเยอร์ 10) ที่ถือGoSurfaceแบบBOTTOMที่ปรับขนาดได้ — มันจึง อยู่เหนือ HUD และผู้เล่นลากให้สูงขึ้นได้ open(title)เริ่มหน้าใหม่: ล้างเนื้อหา แถบเครื่องมือ และปุ่มย้อนกลับ และคืนหน่วยความจำของสิ่งที่add_footer()เพิ่มไว้ เรียกอีกครั้งเพื่อแสดงหน้าอื่นในชีตเดิมset_back()แสดงปุ่มย้อนกลับ ไว้เป็นทางกลับ ส่วนopen_key()รับคีย์คำแปลclose()ซ่อนมัน — ไม่ได้คืนหน่วยความจำopen()ครั้งถัดไปจึงเปิดได้ทันทีclosedและpage_changedบอกคุณว่าเกิดขึ้นเมื่อไรheight_ratio(0.6) คือความสูงตอนเปิดmax_height_ratioยกเพดานขึ้น ส่วนdismissable(true) ปิดมันเมื่อแตะม่าน
set_back() — มีแต่ปุ่มย้อนกลับบนหน้าจอ
ที่ถอยกลับทีละหน้า ถ้าอยากให้ปุ่มย้อนกลับถอยทีละหน้า ให้จัดการ close_requested บน sheet.surface เอง
GoDrawer — จากด้านข้าง
var drawer := GoDrawer.new()
drawer.side = GoDrawer.Side.LEFT
drawer.follow_text_direction = true # ด้านที่เป็นจุดเริ่ม: ซ้าย หรือขวาในภาษาอาหรับ
add_child(drawer)
drawer.body.add_child(GoNavBar.drawer_list([{"icon": GoIconSet.HOME, "text": "Town"},
{"icon": GoIconSet.MAP, "text": "World map"}], 0, go_to))
drawer.open("Menu")
- เลเยอร์ 80 กว้าง 42% ของหน้าจอ (
width_ratio) อยู่ระหว่างmin_width(320dp) ถึงmax_width(420dp) — บนมือถือมันจึงบังหน้าจอเกือบหมด นั่นคือเหตุผลที่ชีตเป็นตัวเลือกที่ดีกว่าบนมือถือ - พาเนลยื่นไปถึงขอบจอ มีแต่เนื้อหาที่ถอยเข้ามาตามเซฟแอเรีย มุมมนจึงไม่ตัดแถวแรกหายไป
LEFTหมายถึงด้านซ้ายของจอแม้ในภาษาอาหรับ เปิดfollow_text_directionเมื่อประเด็นคือ "ด้านที่เป็น จุดเริ่ม" แล้วeffective_side()จะบอกว่ามันไปอยู่ด้านไหนopen(title)เก็บเนื้อหาไว้ — เติมครั้งเดียวพอset_title("")ซ่อนส่วนหัวmotion_seconds(0.2) คือเวลาที่มันเลื่อนเข้ามา ส่วนreduce_motionทำให้มันเปิดทันที
drawer.close() จากปุ่มที่คุณผูกเอง — และพึงรู้ว่า GoSurface.is_any_open() ไม่นับดรอว์เออร์
ให้ถาม drawer.is_open() แทน
การ์ดและเมนูข้างคอนโทรล
สิ่งที่ผู้เล่นเพิ่งกดสมควรได้คำตอบตรงข้างนิ้ว ไม่ใช่กลางหน้าจอ

GoPopover
GoContextMenu
GoStyle.dropdown()
GoStyle.select()
GoComboboxGoPopover — การ์ดบนสิ่งที่ถูกกด
slot.pressed.connect(func() -> void:
var card := GoStyle.item_card({"icon": GoIconSet.POTION, "title": "Health potion",
"subtitle": "Heals 50 HP · 12 left"}, false)
GoPopover.open(slot, card, {"width": 280}))
surface แบบ ANCHOR ที่ต่อสายเลเยอร์ จุดตรึง และการเก็บกวาดไว้ให้แล้ว — สิบบรรทัดที่ทุกเกมต้องเขียน
ซ้ำแล้วซ้ำอีก ทีละอัน: การเปิดอันที่สองจะปิดอันแรก เพราะการ์ดที่ซ้อนกันจะกลบหน้าจอจนไม่มีใครบอกได้ว่า
การ์ดไหนเป็นของสล็อตไหน ม่านของมันโปร่งใส — การ์ดที่เปิดขึ้นมาเพื่อเทียบอุปกรณ์ต้องไม่ทำให้อุปกรณ์นั้นมืดลง —
แต่ยังคงรับการแตะข้างนอกครั้งแรก ซึ่งจะปิดการ์ดและไม่ส่งต่อไปไหนอีก
| ตัวเลือก | ค่าเริ่มต้น | ทำอะไร |
|---|---|---|
title | "" | ส่วนหัวพร้อม × ว่างหมายถึงไม่มีส่วนหัว |
width · max_height | 320 · 520 | ขนาดของการ์ด |
dismissable | true | การแตะข้างนอกปิดมัน |
compact · alpha · translate | false · -1 · false | ระยะห่างแบบแน่น ความทึบของหน้าการ์ด ชื่อเรื่องเป็นคีย์คำแปล |
layer | 95 | CanvasLayer ที่มันเปิดอยู่ |
GoPopover.close() และ GoPopover.is_open() เป็นแบบ static ส่วน open() คืนค่า surface — หรือ
null เมื่อจุดตรึงไม่อยู่ในทรี จึงควรตรวจก่อนจะ await shown.close_requested
dismissable = false และไม่มีชื่อเรื่อง ก็ไม่มีอะไรบนหน้าจอปิดมันได้ —
มีแค่ Escape ปุ่มย้อนกลับ หรือการเรียกของคุณเอง
GoContextMenu — กดค้างและคลิกขวา
var menu := [
{"text": "Use", "icon": GoIconSet.POTION, "action": use},
{"separator": true},
{"text": "Drop", "icon": GoIconSet.TRASH, "action": drop, "danger": true}]
GoContextMenu.attach(slot, menu)
var more := GoStyle.icon_button(GoIconSet.MORE, Callable(), -1, &"more")
more.pressed.connect(func() -> void: GoContextMenu.open_at(more, menu))
- คลิกขวาเปิดมันทันที นิ้วที่กดค้างครึ่งวินาที (
HOLD_SECONDS) ก็เปิดมันได้ แต่ถ้าขยับเกิน 12dp จะยกเลิก — การเลื่อนรายการจึงไม่มีทางทำให้เมนูเด้งขึ้นมา - รายการหนึ่งคือ
{text, action, icon, disabled, checked, separator, danger, translate}ส่งCallableแทนอาร์เรย์เมื่อรายการในเมนูขึ้นอยู่กับสิ่งที่ถูกกด มันจะถูกเรียกทุกครั้งที่เมนูเปิด dangerใส่ "⚠ " ไว้หน้าข้อความ — เมนูของเอนจินลงสีได้แค่ทุกรายการพร้อมกันเท่านั้น- มันคือ
PopupMenuของเอนจิน ที่แต่งตัวโดยGoStyle.style_popup()และไม่นับเป็น surface ที่เปิดอยู่
⋯ ที่มองเห็นได้
(open_at() ดังข้างบน) หรือสอนครั้งเดียวด้วยโค้ชมาร์ก
ดรอปดาวน์ ซีเล็กต์ และปุ่มแยก
var sort := GoStyle.dropdown("Sort", ["Highest score", "Most wins", {"text": "Friends only", "disabled": true}],
func(index: int) -> void: sort_board(index))
var quality := GoStyle.select(["Low", "Medium", "High", "Ultra"], "Quality")
quality.item_selected.connect(func(index: int) -> void: settings.quality = index)
var send := GoSplitButton.make("Send", send_now, ["Send later", "Save as draft"])
ทั้งสามเปิดเมนูป๊อปอัปของเอนจิน: dropdown() คือ MenuButton ที่สั่งการกระทำ
select() คือ OptionButton ที่เก็บค่าไว้ ส่วน GoSplitButton คือการกระทำหลักพร้อมเมนูบน
ลูกศรของมัน (chosen(index)) เมนูทึบเต็มที่โดยตั้งใจ — เมนูที่แสดงในหน้าต่าง OS ของตัวเองจะเปลี่ยน
พิกเซลโปร่งแสงเป็นสีดำ
GoCombobox — รายการที่ค้นหาได้
var friend := GoCombobox.make(friend_names, -1, "Find a friend")
friend.picked.connect(func(index: int) -> void: invite(friend_names[index]))
เมื่อถูกกด มันจะเปิดรายการที่ตรึงไว้บนเลเยอร์ 96 พร้อมม่านโปร่งใส ตั้งแต่ search_threshold (8) รายการขึ้นไป
ช่องค้นหาจะปรากฏและรับโฟกัส มันจับคู่ได้ทุกตำแหน่งในข้อความหรือคำใบ้ โดยไม่สนตัวพิมพ์เล็กใหญ่ และเมื่อไม่พบผลลัพธ์
ก็จะบอกเช่นนั้น list_width (0 = ความกว้างของปุ่ม อย่างน้อย 180dp) กำหนดขนาดรายการ
ป๊อปอัปที่ไม่หยุดเกมเลย
ข่าวที่ผู้เล่นอ่านได้ขณะเล่น ไม่มีอันไหนมีม่าน การแตะที่ใดก็ตามที่ไม่ใช่ตัวการ์ดเองจะส่ง ถึงเกม

GoSnackbar
GoNotice
GoPromptCard
GoStyle.tooltip_node()GoSnackbar — ข้อความพร้อมปุ่มเลิกทำ
var snack := GoSnackbar.new()
add_child(snack)
if await snack.post({"text": "Dropped {name}", "args": {"name": item.name}, "icon": GoIconSet.TRASH,
"actions": ["Undo"], "seconds": 5.0}) == 0:
restore(item)
คีย์ของ post() | ทำอะไร |
|---|---|
text · title · args · translate | ข้อความ บรรทัดตัวหนา (ถ้าต้องการ) {placeholders} และคีย์คำแปล |
tone · icon | สีเน้น (GoTheme.SUCCESS, WARNING…) และไอคอน |
actions · closable | ปุ่ม — String หรือ {text, action} — และ × |
seconds | อยู่นานแค่ไหน ค่าติดลบ: ใช้ notice_duration_ms ของธีม 0: อยู่จนกว่าจะถูกปิด — ถ้าอย่างนั้นต้องใส่ปุ่มให้มัน |
post() คืนดัชนีของปุ่มที่ถูกกด หรือ -1 เมื่อหมดเวลาหรือถูกปิด โพสต์จะเข้าคิว
(queue_limit 4 อันเก่าสุดถูกทิ้งพร้อม -1) และข้อความเดียวกันที่โพสต์ซ้ำขณะที่ยังแสดงอยู่จะแค่เริ่มนับเวลาใหม่
มันอยู่ด้านล่าง (หรือด้านบน ด้วย placement) เหนือเซฟแอเรียและคีย์บอร์ด บนเลเยอร์ 90 — อ่านได้แม้อยู่
เหนือดรอว์เออร์ เมื่อมี tap_to_dismiss (เปิดอยู่) การแตะการ์ดที่ไม่มีปุ่มจะปิดมัน ส่วนการแตะนอกการ์ดส่ง
ถึงเกมเสมอ show_text() และ show_key() คือรูปแบบบรรทัดเดียว
GoNotice — บรรทัดหนึ่งบน HUD
var notice := GoNotice.new()
hud_corner.add_child(notice)
notice.show_text("Game saved", GoTheme.SUCCESS)
notice.custom_minimum_size.x = notice.preferred_width(340)
พาเนลธรรมดาที่คุณวางเอง — ใน GoHudAnchor ที่มุม หรือที่ไหนก็ได้ มันไม่รับทั้งอินพุตและโฟกัส
การกดทะลุมันจึงถึงปุ่มที่อยู่ข้างใต้ และข้อความใหม่จะแทนที่ข้อความเก่า กำหนดความกว้างให้มัน:
preferred_width(limit) คืนความกว้างตามธรรมชาติของข้อความสั้น และตัดบรรทัดข้อความยาวภายใน limit
ส่วน set_content() แสดงคอนโทรลใดก็ได้แทนข้อความ
GoPromptCard — คำถามที่รอได้
var spot := GoHudAnchor.new()
spot.spot = GoHudAnchor.Spot.TOP_CENTER
spot.reserve_space = false
spot.avoid_peers = true # ถอยลงไปใต้แถบพลังชีวิตแทนที่จะบังมัน
hud.add_child(spot)
var invite := GoPromptCard.new()
spot.add_child(invite)
invite.set_icon(GoIconSet.USERS, Color.TRANSPARENT, true)
invite.set_title("Party invite from Ann")
invite.set_subtitle("Dragon's Lair · 3 / 4 members")
invite.set_actions([{"text": "Accept", "action": accept, "primary": true},
{"text": "Decline", "action": invite.hide}])
invite.fit_width(340)
invite.show()
get_tree().create_timer(15.0).timeout.connect(invite.hide)
- มันเริ่มต้นแบบซ่อนอยู่ — ต้องเรียก
show()มันไม่มีตัวจับเวลาของตัวเอง ให้เพิ่มเองถ้าคำถามมีวันหมดอายุ - ปุ่มของมันไม่ได้ปิดมัน — การกระทำเป็นผู้ตัดสิน × (
set_closable(true)) ส่งclosed fit_width(width)ตั้งความกว้างและคาดความสูงจากฟอนต์ สิ่งที่จัดวางอิงกับมันจึงไม่กระโดดในเฟรมถัดไป- หน้าการ์ดของมันตาม
hud_alpha— มันเป็นส่วนหนึ่งของ HUD ไม่ใช่หน้าต่าง
GoBanner และทูลทิป
GoBanner.make("You're offline.", [{"text": "Retry", "action": reconnect}], GoIconSet.WARNING) อยู่ในตัวหน้า
เหนือเนื้อหา และไม่หมดเวลา ปุ่มจะปิดมัน เว้นแต่ปุ่มนั้นมี "keep": true
GoStyle.tooltip_node(text) คือสิ่งที่ควรคืนจาก _make_custom_tooltip() — เอนจินวาดแผ่นพื้น
(TooltipPanel) gohud วาดข้อความ และตัดบรรทัดเมื่อเกิน 260dp ทูลทิปมีไว้สำหรับเมาส์ อย่าซ่อนสิ่งที่ผู้เล่นต้องใช้ไว้หลังทูลทิป
ทัวร์และคอนโซล

GoCoachMark
GoConsoleGoCoachMark — ทัวร์ของคอนโทรลจริง
var tour := GoCoachMark.new()
tour.keep_clear = [top_bar]
tour_layer.add_child(tour)
tour.finished.connect(func(completed: bool) -> void: save.tutorial_done = true)
tour.start([{"target": bag_button, "title": "Bag", "body": "Everything you pick up lands here."},
{"target": map_button, "title": "Map", "body": "Tap to open the full map."}])
- วงแหวนรอบคอนโทรลจริงและการ์ดข้าง ๆ การกดเป้าหมายจะพาทัวร์เดินหน้าต่อ (สัญญาณ
pressedของมัน หรือ"signal"ในขั้นตอนนั้น) ปุ่มถัดไปและข้ามอยู่บนการ์ด เป้าหมายที่ซ่อนอยู่จะถูกข้ามไป - การ์ดหลีกพ้นกลางหน้าจอ (
avoid_center_band) พ้นจุดยึด HUD และพ้นkeep_clear - ขณะที่มี
GoSurfaceใดเปิดอยู่ ทัวร์จะซ่อนตัวและรอ ปุ่มย้อนกลับหรือ Escape จบทัวร์ด้วยfinished(false) - มันไม่มีเลเยอร์ของตัวเอง — เพิ่มมันเป็นลำดับสุดท้าย หรือเพิ่มบนเลเยอร์ที่อยู่เหนือ HUD
GoConsole — สูตรโกงระหว่างพัฒนา
var console := GoConsole.new()
add_child(console)
console.register("give", "give <item> <count>", func(args: PackedStringArray) -> String:
return "gave %s x%s" % [args[0], args[1]] if args.size() >= 2 else "give <item> <count>")
func _unhandled_input(event: InputEvent) -> void:
if event.is_action_pressed(&"dev_console"): console.toggle()
เลเยอร์ 200 — อยู่เหนือทุกอย่าง คุณจึงดีบักทับไดอะล็อกได้ — และมันยังทำงานต่อขณะที่ทรีถูกหยุดชั่วคราว พาเลตต์ แนะนำคำสั่งระหว่างที่คุณพิมพ์ ลูกศรขึ้นและลงไล่ดูประวัติ Escape ปิดมัน ไม่มีปุ่มลัดในตัว: ผูกปุ่มเอง
debug_only (เปิดอยู่) บล็อกมันในบิลด์รีลีส — ทั้ง open() และ run() คำสั่งโกงที่ตกไป
อยู่ในมือผู้เล่นจบระบบเศรษฐกิจของเกมได้ตั้งแต่วันแรก
เลเยอร์ ปุ่มย้อนกลับ และอินพุต
กฎสามข้อตัดสินว่าผู้เล่นแตะอะไรได้บ้างขณะที่มีบางอย่างเปิดอยู่: อะไรอยู่บนสุด ใครเป็นเจ้าของปุ่มย้อนกลับ และใครรับการแตะ
อะไรอยู่บนสุด
| เลเยอร์ | อะไรอยู่ตรงนั้น | ตั้งด้วย |
|---|---|---|
| 5 | HUD ของคุณ (เลือกเอง) | — |
| 10 | GoSheet | CanvasLayer.layer |
| ~50 | หน้าต่าง GoSurface ของคุณเอง เมนูหยุดชั่วคราว | CanvasLayer ของคุณ |
| 80 | GoDrawer | CanvasLayer.layer |
| 90 | GoSnackbar | layer_index |
| 95 | GoPopover | ตัวเลือก "layer" |
| 96 | รายการของ GoCombobox | — |
| 100 | GoDialogs | layer_index |
| 200 | GoConsole | layer_index |
ลำดับนี้ตั้งใจ: สแน็กบาร์รายงานสิ่งที่เพิ่งเกิดขึ้น จึงต้องอ่านได้เหนือดรอว์เออร์ ป๊อปโอเวอร์เป็นของสิ่งที่เพิ่ง ถูกกด จึงอยู่เหนือสิ่งนั้น ไดอะล็อกถามคำถามและบังทั้งสองอย่าง ส่วนคอนโซลบังทุกอย่าง เพราะมันคือสิ่งที่คุณ เปิดเมื่อส่วนที่เหลือพังไปแล้ว
ใครเป็นเจ้าของปุ่มย้อนกลับ
- มีแค่หน้าต่างบนสุดที่ตอบ ในบรรดาหน้าต่างที่เปิดอยู่ อันที่อยู่บนเลเยอร์สูงสุด (ถ้าเท่ากันก็อันล่าสุดในทรี)
จะรับ Escape ปุ่มยกเลิกของเกมแพด (
ui_cancel) ปุ่มย้อนกลับของ Android และการแตะม่าน หน้าต่างที่ซ้อนกัน ปิดทีละอัน ไม่ปิดพร้อมกัน - ปุ่มย้อนกลับของ Android ถูกจัดการช้าไปหนึ่งเฟรม การกดครั้งเดียวจึงไม่มีทางปิดสองหน้าต่าง
- ขณะที่มีสิ่งที่เป็นเจ้าของปุ่มย้อนกลับเปิดอยู่ ปุ่มย้อนกลับของ Android จะไม่ปิดแอป (
GoBackPolicyนับ เจ้าของ และคืนค่าquit_on_go_backหลังจากอันสุดท้ายปิด) ป๊อปอัปของคุณเองที่รับปุ่มย้อนกลับควรเรียกGoBackPolicy.acquire()และrelease()— ใน_exit_treeด้วย - ดรอว์เออร์ ทัวร์โค้ชมาร์ก และฟอร์ม จะหลีกทางขณะที่มีหน้าต่างเปิดอยู่ — ปุ่มย้อนกลับไปถึงหน้าต่างก่อน
GoConfig.close_on_back(เปิดอยู่) คือสวิตช์สำหรับหน้าต่าง
ใครรับการแตะ
- หน้าต่างคลุมทั้งหน้าจอและหยุดอินพุต — แม้ม่านจะโปร่งใสก็ตาม ไม่มีอะไรบนเลเยอร์ที่ต่ำกว่าถูกกดได้ ขณะที่มันเปิดอยู่ ส่วนเลเยอร์ที่อยู่เหนือมัน (สแน็กบาร์) ยังกดได้
- ล้อเมาส์และการบีบนิ้วก็หยุดที่หน้าต่างเช่นกัน: การเลื่อนเลยท้ายรายการไม่มีทางไปเลื่อนเกม
- อินพุตของเกมเพลย์ที่ไม่ผ่านคอนโทรล —
_unhandled_inputสำหรับการเคลื่อนที่ การลากกล้อง — ควรหยุดไว้ก่อน:if GoSurface.is_any_open(): returnซึ่งครอบคลุมไดอะล็อก ชีต ป๊อปโอเวอร์ และคอมโบบ็อกซ์ ส่วนดรอว์เออร์ ต้องถามis_open()แยกต่างหาก - สแน็กบาร์ ข้อความแจ้งเตือน พรอมต์การ์ด และโค้ชมาร์ก รับอินพุตเฉพาะบนการ์ดของตัวเอง ที่อื่นทั้งหมดเกมเป็นผู้รับ
โฟกัส
หน้าต่างที่เปิดด้วยพอยน์เตอร์จะไม่โฟกัสอะไรเลย — ไม่มีวงแหวนกะพริบเมื่อแตะ ถ้าเปิดจากคีย์บอร์ดหรือเกมแพด
ปุ่มปิด (หรือ initial_focus) จะรับโฟกัส โฟกัสอยู่ภายในหน้าต่างตลอดที่เปิดอยู่ และกลับไปยังคอนโทรลที่ถือ
โฟกัสไว้เมื่อหน้าต่างปิด ไดอะล็อกโฟกัสที่ OK ทุกครั้ง
การหยุดเกมชั่วคราว
การหยุดทรีชั่วคราวก็หยุดป๊อปอัปด้วย — มีแต่คอนโซลที่ยังทำงาน สำหรับเมนูหยุดชั่วคราว ให้ตั้ง
process_mode = PROCESS_MODE_ALWAYS ให้เลเยอร์ของมัน แล้วหน้าต่าง ปุ่มในนั้น และปุ่มย้อนกลับจะยังทำงานขณะที่เกมหยุดอยู่
ป๊อปอัปเดียวกันในทุกลุค
กล่องยืนยันจากต้นหน้านี้ ทับเกมเดิม โดยเปลี่ยนแค่พรีเซ็ต โค้ดเหมือนเดิมทุกตัวอักษร — ธีม สกิน และไอคอนจัดการส่วนที่เหลือ

default_dark
default_light
scifi_dark
scifi_light
medieval_dark
medieval_light
material_light ใหม่
material_dark ใหม่
kids_light ใหม่
kids_dark ใหม่
comic_light ใหม่
comic_dark ใหม่
arcade_light ใหม่
arcade_dark ใหม่แต่ละลุคเปลี่ยนอะไร
| ลุค | ตัวหน้าต่าง | ส่วนหัวและปุ่ม |
|---|---|---|
| ค่าเริ่มต้น | มุมมน (18dp) เงานุ่ม ทึบ 80% เหนือเกม | ชื่อเรื่องและ × แบบเรียบ ปุ่มหลักยกตัวขึ้นและยุบลง |
| ไซไฟ | มุมตัดพร้อมขอบนีออนและแสงเรือง ขอบย้อมตามสีเน้น | เครื่องหมายโฟกัสแบบวงเล็บ |
| ยุคกลาง | กรอบตีขึ้นรูป — เหล็กและหนัง หรือกระดาษหนัง — พร้อมหมุดย้ำและลวดลายมุม | ชื่อเรื่องด้วยฟอนต์ Cinzel |
| Material 3 | มุม 28dp ระยะขอบใน 24dp ทึบเต็มที่ | หัวเรื่อง 24sp ด้วย Roboto ปุ่มแคปซูลที่มีแผ่นปุ่ม 40dp ในพื้นที่กด 48dp |
| สำหรับเด็ก | การ์ดลูกอมเยลลี่พร้อมเงานุ่ม ทึบเต็มที่ | ริบบิ้นชื่อเรื่องสีแดดสดใส (สีมินต์ตอนกลางคืน) ปุ่มปิดลูกอมทรงกลม และปุ่มที่ยุบนุ่มเมื่อกด |
| การ์ตูน | เส้นขอบหมึกหนาและเงาคม ทึบเต็มที่ | ป้ายชื่อเรื่องสีเหลือง ปุ่มปิดทรงกลมตัดเส้นหมึก และปุ่มที่เงาหายไปเมื่อกด |
| อาร์เคด | บอร์ดหนา — กรอบสีล้อมรอบแอ่งสีอ่อน — ทึบเต็มที่ | แถบชื่อเรื่องสีทองพร้อมชื่อเรื่องสีขาวตัดเส้นหมึก ปุ่มปิดทรงกลมสีแดง และปุ่มลงสีที่เด้งเมื่อกด |
แต่งป๊อปอัปจากสกิน
สกินตัดสินส่วนที่ Theme เอื้อมไม่ถึง ฮุกต่อไปนี้คือฮุกที่ป๊อปอัปเรียกใช้ — เขียนทับมันในซับคลาส
GoSkin ของคุณเอง โดยค่าเริ่มต้นมันไม่ทำอะไรเลย
| ฮุก | แต่งอะไร |
|---|---|
title_plate_box() · title_plate_ink() · title_plate_min_size() | แผ่นพื้นหลังชื่อเรื่องของหน้าต่าง ชื่อเรื่องจะจัดกึ่งกลางบนแผ่นนั้นและย่อลงได้จนถึงขนาดต่ำสุด (15) เพื่อให้อยู่ในบรรทัดเดียว |
dress_close_button(button) | × ของหน้าต่าง — ลุคสำหรับเด็กทำให้มันเป็นลูกอมกลม ลุคการ์ตูนทำให้เป็นแผ่นกลมตัดเส้นหมึก ส่วนลุคอาร์เคดทำให้เป็นปุ่มกลมสีแดง |
dress_title(label) | ป้ายชื่อเรื่องของหน้าต่าง — ลุคอาร์เคดตัดเส้นหมึกให้ชื่อเรื่องสีขาว |
press_feedback(control) | ทำงานทันทีที่นิ้วแตะลงบนปุ่มที่ gohud สร้าง — ลุคสำหรับเด็กทำให้ปุ่มยุบนุ่ม ลุคอาร์เคดทำให้ปุ่มเด้ง |
floating_box() · notice_box() · drawer_box() · banner_box() · overlay_box() | หน้าของพรอมต์การ์ดและการ์ดโค้ช สแน็กบาร์และข้อความแจ้งเตือน ดรอว์เออร์ แบนเนอร์ และคอนโซล |
coach_ring_box() · draw_coach_pointer() | วงแหวนและตัวชี้ของทัวร์ |
โปร่งแค่ไหน
ป๊อปอัปแต่ละชนิดใช้ค่าความทึบของตัวเอง การ์ด HUD จึงโปร่งเบาได้ขณะที่ไดอะล็อกยังทึบอยู่: หน้าต่าง ชีต ไดอะล็อก
ป๊อปโอเวอร์ และคอมโบบ็อกซ์ใช้ panel_alpha ดรอว์เออร์และการ์ดโค้ชใช้ card_alpha พรอมต์การ์ดและ
คอนโซลใช้ hud_alpha สแน็กบาร์และข้อความแจ้งเตือนใช้ notice_alpha เมนูใช้ popup_alpha ลุค Material
และสำหรับเด็กทำให้ทั้งหมดทึบ ลุคการ์ตูนก็เช่นกัน ยกเว้นพรอมต์การ์ดและคอนโซลที่ทึบ 92% และลุคอาร์เคดก็เช่นกัน ยกเว้นสองอย่างนั้นที่ทึบ 94% เขียนทับชนิดเดียวด้วย GoConfig.container_alpha_overrides ทั้งหมดด้วย
container_alpha (เป็นสัดส่วน — 0.95) หรือหน้าต่างเดียวด้วย alpha ของมัน มีแค่หน้าการ์ดที่จาง
ตัวอักษรไม่เคยโปร่ง ความทึบโดยละเอียด →
การตั้งค่าที่กำหนดรูปแบบของทุกป๊อปอัป
ฟิลด์ของ GoConfig (GoUi.config) บางค่าถูกคัดลอกตอนที่หน้าต่างถูกสร้าง จึงควรตั้ง
ก่อนสร้าง UI — หรือเรียก GoUi.refresh() หลังเปลี่ยน
| ฟิลด์ | ค่าเริ่มต้น | ทำอะไร |
|---|---|---|
surface_max_width · surface_max_height | 480 · 700 | ขนาดใหญ่สุดของหน้าต่าง |
surface_height_ratio · surface_max_height_ratio | 0.68 · 0.72 | ความสูงปกติและเพดาน (ซึ่งเป็นขีดจำกัดของการลากด้วย) |
surface_fit_max_height_ratio | 0.94 | การ์ดกลางจอที่ปรับตามเนื้อหาโตได้แค่ไหนก่อนจะต้องเลื่อน |
surface_width_ratio_portrait · …_landscape | 0.94 · 0.72 | ความกว้างเป็นสัดส่วนของพื้นที่ |
dismiss_on_scrim | false | การแตะม่านปิดหน้าต่างและไดอะล็อก ถูกคัดลอกตอนที่หน้าต่างถูกสร้าง |
surface_fade_in · fade_seconds | false · 0.14 | ค่อย ๆ แสดงหน้าต่าง และเร็วแค่ไหน |
close_on_back | true | Escape, ui_cancel และปุ่มย้อนกลับของ Android ปิดหน้าต่างบนสุด |
close_button_visual | 36 | × ดูใหญ่แค่ไหน — พื้นที่กดสัมผัสยังคงเป็น 48dp |
suppress_pointer_focus_ring | true | ไม่มีวงแหวนโฟกัสเมื่อหน้าต่างถูกเปิดด้วยการแตะหรือเมาส์ |
container_alpha · container_alpha_overrides | -1 · {} | ความทึบของทุกหน้า หรือแยกตามชนิด (GoTheme.BOX_*) |
reduce_motion | false | ไม่มีการจาง การเลื่อน การเต้นเป็นจังหวะ หรือการยุบนุ่ม |
min_touch_size | 48 | ความสูงของปุ่มในพรอมต์การ์ดและโค้ชมาร์ก |
respect_safe_area | true | กันหน้าต่างออกจากรอยบากและแถบท่าทาง |
text_keys · text_overrides | — | คำของชุดเอง: close, back, confirm, cancel, next, done, skip, search, empty… |
sound_cues · การสั่น | — | สิ่งที่ GoFeedback เล่นเมื่อเปิด ปิด ยืนยัน ยกเลิก และแตะ |
สูตรสำเร็จ
คัดลอก วาง แล้วเปลี่ยนข้อความ ทุกการเรียกในนี้ตรวจกับซอร์สโค้ดแล้ว
การซื้อที่บอกราคาไว้ในข้อความ
func buy(item_name: String, price: int) -> void:
if await dialogs.confirm("Buy {item}?", "It costs {price} gold.", "Buy", "", "",
{"item": item_name, "price": price}):
purchase(item_name)
หน้าต่างตั้งค่าที่ตรึงปุ่มบันทึกไว้
func open_settings() -> void:
var layer := CanvasLayer.new()
layer.layer = 50
add_child(layer)
var window := GoSurface.new()
window.set_title("Settings")
window.dismiss_on_scrim = true
window.close_requested.connect(layer.queue_free)
window.body.add_child(GoStyle.section("Sound", false))
window.body.add_child(GoStyle.toggle("Music", false))
window.body.add_child(GoStyle.slider(0.0, 1.0, 0.05))
var row := GoStyle.row()
row.add_child(GoStyle.button("Cancel", window.request_close))
row.add_child(GoStyle.button("Save", func() -> void:
save_settings()
layer.queue_free(), GoStyle.Tone.PRIMARY))
window.footer.add_child(row)
window.footer.visible = true
layer.add_child(window)
ฟอร์มที่บอกว่าผิดตรงไหน
var field := GoField.make("Name", GoStyle.line_edit(), "3–12 letters")
window.body.add_child(field)
create_button.pressed.connect(func() -> void:
if name_is_taken():
field.set_error("That name is taken")
window.set_status_text("Name check failed", GoTheme.DANGER)
else:
window.clear_status()
create_character())
วันที่ในชีต
sheet.open("Delivery date")
var picker := GoDatePicker.make({"year": 2026, "month": 10, "day": 14}, func(date: Dictionary) -> void:
delivery = date
sheet.close())
picker.min_date = GoDatePicker.today()
sheet.body.add_child(picker)
เมนูหยุดชั่วคราว
func pause() -> void:
get_tree().paused = true
var layer := CanvasLayer.new()
layer.layer = 50
layer.process_mode = Node.PROCESS_MODE_ALWAYS # ยังทำงานต่อขณะที่เกมหยุดชั่วคราว
add_child(layer)
var menu := GoSurface.new()
menu.set_title("Paused")
menu.close_requested.connect(func() -> void:
get_tree().paused = false
layer.queue_free())
menu.body.add_child(GoStyle.button("Resume", menu.request_close, GoStyle.Tone.PRIMARY))
menu.body.add_child(GoStyle.button("Settings", open_settings))
menu.body.add_child(GoStyle.button("Quit to title", quit_to_title, GoStyle.Tone.DANGER))
layer.add_child(menu)
เกมเพลย์ที่รอป๊อปอัป
func _unhandled_input(event: InputEvent) -> void:
if GoSurface.is_any_open() or drawer.is_open():
return
handle_gameplay(event)
หลุมพราง
GoSurfaceไม่คืนหน่วยความจำตัวเองเลย ต่อclose_requestedไว้ ไม่อย่างนั้น × จะไม่ทำอะไร- ตัวเลือกที่ตั้งหลัง
add_child—scrim_transparent,show_header,resizable,scroll_bodyและlayer_indexกับmax_widthของGoDialogs— จะไม่ถูกนำไปใช้ confirm()ครั้งที่สองคืนค่าfalseทันที เปิดqueue_when_busyถ้าสองคำถามอาจมาชนกัน- Enter คือการยืนยัน OK มีโฟกัส บนไดอะล็อกแบบย้อนกลับไม่ได้ด้วย
- ปุ่มย้อนกลับบนชีตปิดทั้งชีต ไม่ใช่แค่หน้าย่อย
- Escape ไม่ปิดดรอว์เออร์ และ
is_any_open()ก็มองไม่เห็นมัน - ป๊อปโอเวอร์หรือรายการของคอมโบบ็อกซ์บล็อกเกม จนกว่าจะปิด แม้ม่านจะโปร่งใส — และป๊อปโอเวอร์ที่มี
dismissable = falseและไม่มีชื่อเรื่อง จะไม่มีอะไรบนหน้าจอที่ปิดมันได้ - พรอมต์การ์ดเริ่มต้นแบบซ่อนอยู่ และไม่มีการหมดเวลา
- สแน็กบาร์ที่มี
seconds: 0และไม่มีปุ่มจะไม่มีวันหายไป และโดยค่าเริ่มต้นมันรับการแตะบนการ์ดของตัวเอง - การกดค้างที่ไม่มีใครรู้ คือฟีเจอร์ที่ไม่มีใครใช้ แสดงปุ่ม
⋯ด้วย - ทรีที่หยุดชั่วคราวจะหยุดป๊อปอัปของคุณด้วย ใช้
PROCESS_MODE_ALWAYSบนเลเยอร์ของมัน - ข้อความแจ้งเตือนที่ไม่กำหนดความกว้าง จะหดจนแคบที่สุด — ใช้
preferred_width() - อย่าถามในสแน็กบาร์ อย่าแจ้งข่าวในไดอะล็อก คำถามจะหายไปเมื่อถูกเพิกเฉย ส่วนข่าวไม่ควรหยุดเกม