From c61826e87cb20362a7225deff7d55ae3a6a40ff1 Mon Sep 17 00:00:00 2001 From: cocos02 Date: Sun, 7 Jun 2026 18:42:34 +0800 Subject: [PATCH] feat: auto-save on scene change + resume from auto-save MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - useGameEngine: auto-save to slot 0 on every sceneChange event - useGameEngine: add resumeAutoSave() for continuing from auto-save - useGameEngine: extract registerEvents() to share between start and resume - SaveLoadMenu: show slot 0 as '自动存档' with distinct styling and read-only button - App.vue: check auto-save on load, show '继续上次进度' button if available --- src/App.vue | 16 +++++++++++++++- src/components/SaveLoadMenu.vue | 26 ++++++++++++++++++++++++++ src/composables/useGameEngine.ts | 20 +++++++++++++++----- 3 files changed, 56 insertions(+), 6 deletions(-) diff --git a/src/App.vue b/src/App.vue index 980c644..2605ef1 100644 --- a/src/App.vue +++ b/src/App.vue @@ -12,13 +12,15 @@ const videoElB = ref(null) const loading = ref(true) const started = ref(false) const showMenu = ref(false) +const hasAutoSave = ref(false) -const { loadGame, start, makeChoice, saveGame, loadGameFromSlot, refreshSaves } = +const { loadGame, start, resumeAutoSave, makeChoice, saveGame, loadGameFromSlot, refreshSaves, saveSystem } = useGameEngine(() => [videoElA.value, videoElB.value]) async function init() { await loadGame('/scenes/demo.json') loading.value = false + hasAutoSave.value = (await saveSystem.load(0)) !== null } function handleStart() { @@ -26,6 +28,11 @@ function handleStart() { start() } +async function handleResume() { + started.value = true + await resumeAutoSave() +} + function onVideoReady(elA: HTMLVideoElement, elB: HTMLVideoElement) { videoElA.value = elA videoElB.value = elB @@ -72,6 +79,7 @@ init()
+
游戏结束
@@ -190,4 +198,10 @@ html, body { .start-btn:hover { background: rgba(255, 255, 255, 0.25); } + +.resume-btn { + margin-top: 16px; + border-color: rgba(100, 200, 255, 0.3); + color: #8cf; +} diff --git a/src/components/SaveLoadMenu.vue b/src/components/SaveLoadMenu.vue index 12dac03..f866dc4 100644 --- a/src/components/SaveLoadMenu.vue +++ b/src/components/SaveLoadMenu.vue @@ -21,6 +21,23 @@ const maxSlots = 5

存档 / 读档

+
+
自动存档
+
+ {{ saves.find(s => s.slot === 0)!.sceneLabel }} +
+
暂无自动存档
+
+ +
+
+
[HTMLVideoElement | null, HTMLVide engine.stateManager.init(data.variables) } - function start() { - const [elA, elB] = videoEls() - engine.videoManager.attach(elA!, elB!) - + function registerEvents() { engine.on('sceneChange', (scene) => { store.setScene(scene) store.clearChoices() store.clearTimer() + saveGame(0) }) engine.on('choiceRequest', (choiceList) => { @@ -49,10 +47,22 @@ export function useGameEngine(videoEls: () => [HTMLVideoElement | null, HTMLVide store.setGameEnded(true) engine.choiceSystem.stop() }) + } + function start() { + const [elA, elB] = videoEls() + engine.videoManager.attach(elA!, elB!) + registerEvents() engine.start() } + async function resumeAutoSave(): Promise { + const [elA, elB] = videoEls() + engine.videoManager.attach(elA!, elB!) + registerEvents() + return await loadGameFromSlot(0) + } + function makeChoice(index: number) { const scene = store.currentScene if (!scene?.choices) return @@ -99,5 +109,5 @@ export function useGameEngine(videoEls: () => [HTMLVideoElement | null, HTMLVide destroy() }) - return { loadGame, start, makeChoice, saveGame, loadGameFromSlot, refreshSaves, engine, saveSystem } + return { loadGame, start, resumeAutoSave, makeChoice, saveGame, loadGameFromSlot, refreshSaves, engine, saveSystem } }