2026-04-16開發日誌
- 日期:2026-04-16
- 專案:Cá xấu Duckduck
今天的重點是「場景物件的開關互動」要怎麼做才對,過程中踩了幾個坑,最後找到一個乾淨的模式。
冰箱開關互動
第一次嘗試:TextureRect + anchor 定位
一開始的做法是把 refrigerator.png(裁切版,帶黑邊)放進場景,用 anchor 去手動對齊到背景圖的冰箱位置。結果發現這很難對準——anchor 是百分比,背景圖裡的冰箱是像素位置,兩者之間的換算誤差很大,在不同解析度下還會跑位。
正確做法:全螢幕 Overlay + mouse_filter = IGNORE
把 refrigerator.png 改成 2560×1080 透明背景,冰箱內容畫在正確的座標上,其他地方全透明。這樣 RefrigeratorOverlay 只要設成:
layout_mode = 0
anchor_right = 1.0
anchor_bottom = 1.0
stretch_mode = KEEP(不縮放)
mouse_filter = 2(IGNORE)填滿全螢幕、不需要任何手動對齊,透明區域自然對上背景。
mouse_filter = 2 是關鍵:overlay 完全不攔截滑鼠事件,所以冰箱開著的時候,其他所有按鈕照樣可以點。
RefrigeratorBtn 是一個 flat = true 的隱形按鈕,蓋在背景圖的冰箱門上(anchor 估值,在編輯器裡微調)。點一下開、再點一下關,0.2 秒 TRANS_QUAD 淡入淡出。
架構可擴展性
之後有 5-6 個可開關的物件,每一個就是:
- 一個全螢幕
TextureRect(overlay,mouse_filter = IGNORE) - 一個
flat Button(蓋在背景的物件上) - 腳本裡一個
bool+ 一個 handler
不同物件之間不互相干擾,腳本也不會複雜化。
糖罐背景動態換圖
糖罐場景(chapter1_sugar_prop)有兩張背景圖:
suger_prop.png:糖塊還在罐子前面suger_prop_dis.png:糖塊消失了(已被拿走)
進場時根據存檔切換
_ready() 檢查 InventoryManager.has_item("sugar"),已收集就直接用 suger_prop_dis.png:
_collected = InventoryManager.has_item("sugar")
if _collected:
_background.texture = _TEXTURE_DIS收集瞬間即時換圖
一開始把換圖放在動畫結束的 callback 裡,但視覺上感覺太慢。後來改成按下按鈕的瞬間就換圖,和動畫平行執行:
func _on_suger_pick_pressed() -> void:
...
_background.texture = _TEXTURE_DIS # 立即換圖
_play_collect_animation() # 動畫同步開始效果是:背景瞬間切換到「糖塊消失」版,同時糖塊動畫從罐子飛出彈起、再飛進包包。視覺上合理——糖被拿走的那一刻背景就反映了這個狀態。
preload 用於兩張貼圖,在編譯時載入,runtime 不會有找不到檔案的問題:
const _TEXTURE_NORMAL := preload("res://Images/chapter1/puzzle/suger_prop.png")
const _TEXTURE_DIS := preload("res://Images/chapter1/puzzle/suger_prop_dis.png")小結
今天最重要的收穫是確立了「可互動物件的 overlay 模式」:美術輸出全螢幕透明圖、程式直接疊上去、mouse_filter 控制是否擋點擊。這個模式之後所有的開關物件都可以直接套用,不需要每次在編輯器裡手動對齊座標。
