存檔與版本更新
使用存讀檔 API,保留故事 ID,並讓新版遊戲讀取舊存檔。
Preview 與 Player
Preview 的 Save/Load 是暫存進度,重建或關閉後會清除。測試關閉遊戲後再讀檔,或測試版本升級時,使用打包後的 Player。
初期可以沿用 save.open、load.open、backlog.open 等預設 Action。製作自訂介面後,再接上相同的存檔 API。
使用存檔欄位
以下片段放在具有 persistence 能力的 Extension callback 中:
const saved = ctx.saves.save(3);
if (!saved) {
ctx.raw.log('save', 'Slot 3 could not be saved');
}
const information = ctx.saves.query(3);
ctx.raw.log('save information', JSON.stringify(information));
const slots = ctx.saves.list();
ctx.raw.log('available slots', JSON.stringify(slots));load(3) 讀取第 3 個欄位,autosave() 自動存檔,delete(3) 刪除該欄位。刪除或覆寫前,介面應清楚顯示操作對象,並處理 API 回報的失敗結果。
存檔包含劇情位置、變數、UI、Stage,以及引擎管理的非同步執行紀錄。自訂系統應透過 API 參與保存,避免直接修改未知的存檔格式。
版本與 ID
修改既有內容時,保留故事 [id ...]、場景 ID、資源 ID 和 state provider ID。三個版本欄位用途不同:version 是作品發行版本,contentVersion 是內容版本,saveVersion 是存檔格式版本。
重新命名變數、改型別、移除資源或調整劇情結構後,需要檢查舊存檔。保留台詞 ID 能穩定定位,但不能代替其他資料的相容性處理。
範例:把 score 改成 points
先建立測試基準:以 contentVersion: "tutorial-v1"、saveVersion: 1 打包遊戲並存檔。新版將 score 改成 points,同時更新 catalog 和程式中的引用。
建立 Content/Migrations/v1-v2.pxsavemigration:
{
"format": "PrismatiXSaveMigration",
"schemaRevision": 2,
"id": "tutorial.v1-v2",
"from": {"contentVersion": "tutorial-v1", "saveVersion": 1},
"to": {"contentVersion": "tutorial-v2", "saveVersion": 2},
"anchor": {"policy": "preserve"},
"operations": [
{"op": "renameVariable", "from": "score", "to": "points"}
]
}在新版專案合併:
{
"contentVersion": "tutorial-v2",
"saveVersion": 2,
"saveMigrations": [
{
"id": "tutorial.v1-v2",
"from": {"contentVersion": "tutorial-v1", "saveVersion": 1},
"to": {"contentVersion": "tutorial-v2", "saveVersion": 2},
"asset": "Content/Migrations/v1-v2.pxsavemigration"
}
]
}from、to 要與實際版本一致,新 catalog 也要接受 points。anchor.policy: preserve 適用於仍能沿用故事定位的更新;位置改變時,使用 map 並填入實際的 runtimeDocumentId、sourceId、operationId 對應。
其他遷移操作有 removeVariable、setVariable、renameRoute、setScript。先用舊存檔副本測試;CLI 的 migrate 是另一個處理作者文件的工具,不是這份存檔遷移設定。
更新 Extension 自有資料
假設同一個 provider 在 v1 保存 {score: number},v2 改為 {points: number}:
type PluginState = { points: number };
let pluginState: PluginState = { points: 0 };
ctx.state.registerProvider<PluginState>('tutorial.points', 2, {
capture: () => ({ ...pluginState }),
restore: (saved) => {
pluginState = { ...saved };
},
migrate: (saved, fromVersion) => {
if (fromVersion === 1 && saved !== null && typeof saved === 'object' && !Array.isArray(saved)) {
const value = (saved as { score?: unknown }).score;
if (typeof value === 'number' && Number.isFinite(value)) {
return { points: value };
}
}
throw new Error('Unsupported tutorial.points state migration');
},
});舊版也必須使用 tutorial.points 這個 provider ID。這個例子不是把其他 provider 改名,而是更新同一份資料的格式。遇到未支援的舊格式時回報錯誤,避免讀檔後意外清空進度。
capture、restore、migrate 必須同步處理 JSON 資料,期間不可播放媒體或呼叫引擎操作。
更新前的測試
保留每個已發布版本的 Player 和測試存檔,檢查對話、選項等待、動畫、Extension 的 await、語言切換、永久解鎖和 backlog 回溯。
還原進度可能重新執行部分 callback,要確認狀態沒有遺漏,也沒有重複產生不該重複的副作用。全螢幕轉場會取消後回復 UI/Stage 狀態;媒體不保證精確到每個音訊取樣或 GPU 影格。
需要檢查單一存檔時,先看 prismatix inspect-save --help,再提供相符的 package 或所需 secret。問題報告使用測試存檔,避免附上玩家資料和密鑰。
實作參考:遷移格式、專案設定、Runtime SDK、存檔與回放。