Skip to content

2026-04-15開發日誌

  • 日期:2026-04-15
  • 專案:Cá xấu Duckduck

今天做的事情比較分散,但每一塊都是紮實的基礎建設:背包的視覺位置調對了、遊戲第一次有了真正的存讀檔,還有道具的狀態現在會被記錄下來。


背包 UI 調整

靠近包包、動態寬度

昨天的背包面板是從畫面左邊延伸過來的,感覺和包包的位置有點脫節。今天把它改成從包包左緣往左展開——兩個 anchor 都釘在 0.88(包包左緣),grow_horizontal = 0 讓它往左長,offset_left 動態計算寬度。

寬度公式:

panel_width = 道具數 × 160 + (道具數 - 1) × 12 + 28
  • 最少顯示一格(空背包不會縮成一條線)
  • 超過螢幕範圍時 ScrollContainer 接手

每次 _refresh_items() 都會重算一次,InventoryPanelPanelBGoffset_left 同步更新。

PanelBG 延伸到包包後面

原本 PanelBGInventoryPanel 的子節點,背景只蓋到道具列的範圍。今天把它搬出來變成 Bag 的直接子節點anchor_right = 1.0 讓背景延伸到螢幕右緣,包包圖示也被包進去了。

打開背包時,PanelBGInventoryPanel 一起淡入;收起時一起淡出。


存檔系統(SaveManager)

架構

建了一個新的 autoload SaveManager,統一管理遊戲進度的讀寫。存檔位置是 user://save.json——Godot 的慣例,對應到 OS 的使用者資料夾,不放在 project 裡面。

存檔格式:

json
{
    "version": 1,
    "current_scene": "res://Scene/chapter1_background.tscn",
    "inventory": [
        {"id": "sugar", "texture_path": "res://Images/chapter1/puzzle/suger.png"}
    ],
    "props": {
        "sugar_collected": 1
    },
    "timestamp": 1744934400
}

props 是一個通用的旗標字典,之後每個有狀態的道具或事件都可以往裡面加。

API

  • save_checkpoint(scene_path) — 存目前場景 + 背包 + props,寫入磁碟
  • set_prop(key, value) — 設單一旗標,立即寫入(不需要完整 checkpoint)
  • get_prop(key, default) — 讀旗標
  • restore_inventory() — 把存檔裡的道具清單還原進 InventoryManager
  • reset() — 清除所有存檔

自動存檔

chapter1_background._ready() 加了 SaveManager.save_checkpoint(...)。玩家每次進到這個場景,進度就自動寫入。之後新增場景時,一行就能接上。


Continue 按鈕啟用邏輯

開始畫面的 Continue 按鈕現在有真正的邏輯了:

  • 無存檔:半透明、mouse_filter = IGNORE,完全不可互動
  • 有存檔:亮起、mouse_filter = STOP,可以點

按 Continue 會先呼叫 SaveManager.restore_inventory(),把上次的道具還回背包,再直接跳到存檔記錄的場景。

按 Play 時的確認視窗

如果玩家已有存檔又按 Play,會跳出一個確認視窗:

已有存檔,確定要開始新遊戲? 原本的進度將會消失。

確定 → SaveManager.reset() + InventoryManager.clear() + 進 cutscene 取消 → 關閉視窗,回到開始畫面


道具狀態:sugar_collected

props 字典的第一個用例。糖塊收集後,會呼叫:

gdscript
SaveManager.set_prop("sugar_collected", 1)

這會立刻寫入 JSON,不等下次 checkpoint。

「已經拿過了」提示

如果玩家再回到糖罐場景、再點罐子,不會什麼都沒發生——會出現「已經拿過了」的文字提示,停留約 1.2 秒後淡出。

判斷邏輯在 _ready() 就處理掉了:

gdscript
_collected = InventoryManager.has_item("sugar")

這樣不管是同一個 session 還是 Continue 回來的,狀態都能正確恢復。


小結

今天最重要的事是存檔系統打通了——從「玩家做了什麼」到「下次開啟遊戲還在」,這條路現在是完整的。props 的設計讓之後每個有狀態的事件都可以輕鬆接上,不需要動架構。