Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
93e2ca6
refactor RFC1
MakinoharaShoko Apr 28, 2026
cba2e22
refactor RFC1 it2
MakinoharaShoko Apr 28, 2026
99f4b6a
fix: intro 阻塞状态演算问题
MakinoharaShoko Apr 28, 2026
bc105a9
修改演出阻塞状态计算判断依据
MakinoharaShoko Apr 29, 2026
00dd6d4
不再使用“实验性快速预览”
MakinoharaShoko Apr 29, 2026
b522bef
Merge branch 'dev' into refactor-state-mgmt
MakinoharaShoko Apr 29, 2026
ecbde19
Merge branch 'dev' into refactor-state-mgmt
MakinoharaShoko May 1, 2026
c2206ef
Fix pixi conditional render refresh
MakinoharaShoko May 1, 2026
7851ddf
Fix preview state calculation blockers
MakinoharaShoko May 1, 2026
f30346a
Deduplicate none perform helpers
MakinoharaShoko May 1, 2026
ed9bfd0
优化快进功能,添加实时预览耗时日志
MakinoharaShoko May 1, 2026
a4d8d63
feat: notify editor on fast preview timeout
MakinoharaShoko May 1, 2026
b44c448
不再使用演算次数限制
MakinoharaShoko May 1, 2026
c4346a8
更新平行动画测试脚本,并处理状态计算中与在 changeFigure 和 changeBg 中预设 transform 时出现的问题。
MakinoharaShoko May 1, 2026
99c5e7d
update version number
MakinoharaShoko May 2, 2026
f3747cc
fix: bgm not play when fast preview
MakinoharaShoko May 2, 2026
fb0661c
flow testing scenes
MakinoharaShoko May 2, 2026
13439ee
fix: 遇到异步脚本应该停止流程继续,因为异步脚本将会在异步过程后手动恢复流程。
MakinoharaShoko May 2, 2026
55af05c
fix: choose auto next problem
MakinoharaShoko May 2, 2026
4cce67f
Merge branch 'dev' into refactor-state-mgmt
MakinoharaShoko May 3, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@webgal/base",
"version": "4.5",
"version": "4.6",
"description": "A brand new web Visual Novel engine.",
"repository": "https://github.com/OpenWebGAL/WebGAL.git",
"author": "Mahiru <Mahiru_@outlook.com>",
Expand Down
2 changes: 1 addition & 1 deletion packages/webgal/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "webgal-engine",
"version": "4.5.20",
"version": "4.6.0",
"scripts": {
"dev": "vite --host --port 3000",
"build": "node scripts/update-engine-version.js && cross-env NODE_ENV=production tsc && vite build --base=./",
Expand Down
16 changes: 9 additions & 7 deletions packages/webgal/public/game/scene/demo_parallel_animation.txt
Original file line number Diff line number Diff line change
@@ -1,31 +1,33 @@
changeBg:WebGalEnter.webp -next;
changeFigure:stand.webp -id=figure01 -transform={"position":{"x":1000,"y":720}};
;演示setAnimation平行执行
接下来演示setAnimation平行执行
setAnimation:shockwaveIn -target=figure01 -next
setAnimation:move-front-and-back -target=figure01 -parallel
;演示通过-continue接续执行两个常规setTransform正常运作、不被打断
接下来演示通过-continue接续执行两个常规setTransform正常运作、不被打断
setTransform:{"position":{"x":-1000}} -duration=5000 -target=figure01 -continue
setTransform:{"position":{"x":1000}} -duration=5000 -target=figure01
;演示setTransform平行执行
接下来演示setTransform平行执行
setTransform:{"position":{"x":-1000},"scale":{"x":0.5},"contrast":0.5} -duration=5000 -target=figure01 -ease=easeOut -next -keep
wait:2000
setTransform:{"position":{"y":0},"scale":{"y":0.5},"saturation":0} -duration=5000 -target=figure01 -ease=linear -parallel -continue
setTransform:{"position":{"y":-720},"scale":{"y":1},"saturation":1} -duration=5000 -target=figure01 -ease=linear -next
setTransform:{"position":{"x":1000},"scale":{"x":1},"contrast":1} -duration=5000 -target=figure01 -ease=easeIn -parallel;
;演示参数解耦改动后setTempAnimation普通运作是否正常
接下来演示参数解耦改动后setTempAnimation普通运作是否正常
setTempAnimation:[{"duration":0}, {"duration":500,"position":{"x":-1000}}, {"duration":500,"position":{"y":720},"scale":{"y":0.5},"saturation":0}, {"duration":500,"position":{"x":-1000, "y":720}}, {"duration":500}, {"duration":500,"position":{"x":-1000},"scale":{"x":0.5},"contrast":0.5}] -target=figure01
setTempAnimation:[{"duration":0}, {"duration":500,"position":{"x":1000}}, {"duration":500,"position":{"y":720}}, {"duration":500,"position":{"x":1000, "y":720}}, {"duration":500}, {"duration":500,"position":{"x":1000}}] -target=figure01
;演示参数解耦改动后setTransform的-writeDefault参数是否运作正常
接下来演示参数解耦改动后setTransform的-writeDefault参数是否运作正常
setTransform:{} -writeDefault -target=figure01 -duration=500
setTransform:{"position":{"x":1000,"y":720}} -target=figure01 -next;
setAnimation:shockwaveOut -target=figure01 -parallel
;演示setTempAnimation平行执行
接下来演示setTempAnimation平行执行
setTransform:{"alpha":1} -target=figure01 -next;
setTempAnimation:[{"duration":0},{"position":{"x":-1000},"scale":{"x":0.5},"contrast":0.5,"duration":5000,"ease":"easeOut"},{"position":{"x":-1000},"scale":{"x":0.5},"contrast":0.5,"duration":2000},{"position":{"x":600},"scale":{"x":1},"contrast":1,"duration":5000,"ease":"easeIn"},{"duration":2000}] -target=figure01 -next;
setTempAnimation:[{"duration":2000},{"position":{"y":0},"scale":{"y":0.5},"saturation":0,"duration":5000,"ease":"linear"},{"position":{"y":-720},"scale":{"y":1},"saturation":1,"duration":5000,"ease":"linear"},{"duration":2000}] -target=figure01 -parallel -continue;
;演示并行执行多条终止时间点不一致的setTransform
演示并行执行多条终止时间点不一致的setTransform
setTransform:{"position":{"x":-1000}} -duration=5000 -next -target=figure01;
setTransform:{"position":{"y":0}} -duration=3000 -parallel -target=figure01;
setTransform:{"position":{"x":1000}} -duration=3000 -next -target=figure01;
setTransform:{"position":{"y":-720}} -duration=5000 -parallel -target=figure01;
结束,接下来退场。
changeBg: -next;
changeFigure: -id=figure01
30 changes: 30 additions & 0 deletions packages/webgal/public/game/scene/demo_test_dom_control.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
DOM 管理测试 1 开始:验证 intro 创建 introContainer 后会自动清理。
intro:DOM 测试第一段|自动结束 -delayTime=80 -fontSize=small -animation=fadeIn;
DOM 管理测试 1 结束:预期状态:第一段 intro 结束后回到对话,introContainer 不应继续遮挡画面。

DOM 管理测试 2 开始:验证连续 intro 不残留前一个 DOM 内容。
intro:DOM 测试第二段|如果第一段文字仍可见就是异常 -delayTime=80 -fontSize=small -animation=slideIn -backgroundColor=rgba(16,16,16,1);
DOM 管理测试 2 结束:预期状态:只显示过第二段 intro,结束后没有残留黑屏内容。

DOM 管理测试 3 开始:验证 chooseContainer 在选择后清理;快速预览默认选择第 2 项。
choose:手动 DOM 路径:dom_choice_manual|快速预览 DOM 路径:dom_choice_fast -defaultChoose=2;
label:dom_choice_manual;
setVar:domChoice=manual;
DOM 管理测试 3 手动路径:手动选择第 1 项时会看到这里。
jumpLabel:dom_choice_after;
label:dom_choice_fast;
setVar:domChoice=fast;
DOM 管理测试 3 默认路径:快速预览默认选择第 2 项。
label:dom_choice_after;
DOM 管理测试 3 结束:预期状态:domChoice={domChoice};选择结束后 chooseContainer 不应残留按钮。

DOM 管理测试 4 开始:验证 getUserInput 的输入框 DOM 在确认后清理,快速预览写入默认值。
getUserInput:domInput -title=DOM 输入测试 -buttonText=确认 -defaultValue=DOMDefault;
DOM 管理测试 4 结束:预期状态:快速预览时 domInput={domInput},应为 DOMDefault;普通运行或普通快进确认后输入框 DOM 被清理。

DOM 管理测试 5 开始:验证 setTextbox hide/on 后文本框能恢复。
setTextbox:hide -next;
wait:300;
setTextbox:on -next;
DOM 管理测试 5 结束:预期状态:这句话可见,文本框已恢复显示,introContainer 和 chooseContainer 没有可见残留。
end;
32 changes: 32 additions & 0 deletions packages/webgal/public/game/scene/demo_test_flow_control.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
流程控制测试 1 开始:验证 jumpLabel 会跳过当前路径并继续执行。
jumpLabel:flow_jump_target;
流程控制测试 1 异常路径:如果看到这句话,说明 jumpLabel 没有跳过中间语句。
label:flow_jump_target;
流程控制测试 1 结束:预期状态:没有看到“异常路径”,并成功到达 flow_jump_target。

流程控制测试 2 开始:验证 choose 跳转到标签;快速预览默认选择第 2 项。
choose:路径 A:flow_choose_a|路径 B:flow_choose_b -defaultChoose=2;
label:flow_choose_a;
setVar:flowChoice=A;
流程控制测试 2 路径 A:手动选择 A 时会看到这里。
jumpLabel:flow_choose_end;
label:flow_choose_b;
setVar:flowChoice=B;
流程控制测试 2 路径 B:快速预览默认选择 B 时会看到这里。
label:flow_choose_end;
流程控制测试 2 结束:预期状态:快速预览时 flowChoice={flowChoice},应为 B;手动运行时应等于所选路径。

流程控制测试 3 开始:验证 callScene 子场景执行后会回到父场景继续。
setVar:flowChildDone=0;
callScene:demo_test_flow_control_child.txt;
流程控制测试 3 结束:预期状态:已从子场景返回,flowChildDone={flowChildDone},应为 1。

流程控制测试 4 开始:验证连续 jumpLabel 能落到统一出口,不进入中间路径。
jumpLabel:flow_chain_1;
流程控制测试 4 异常路径 A:如果看到这句话,说明第一段跳转失败。
label:flow_chain_1;
jumpLabel:flow_chain_2;
流程控制测试 4 异常路径 B:如果看到这句话,说明第二段跳转失败。
label:flow_chain_2;
流程控制测试 4 结束:预期状态:直接到达 flow_chain_2,未显示异常路径。
end;
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
流程控制子场景测试开始:验证 callScene 会压栈进入子场景。
setVar:flowChildDone=1;
流程控制子场景测试结束:预期状态:flowChildDone={flowChildDone},下一句应回到父场景。;
34 changes: 34 additions & 0 deletions packages/webgal/public/game/scene/demo_test_input_flow_control.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
用户输入流程控制测试 1 开始:验证快速预览下 getUserInput 会写入 defaultValue。
getUserInput:previewName -title=输入测试 -buttonText=确认 -defaultValue=FastPreviewName;
jumpLabel:input_default_ok -when=previewName=="FastPreviewName";
用户输入流程控制测试 1 手动输入路径:previewName={previewName},与默认值不同。
jumpLabel:input_default_end;
label:input_default_ok;
用户输入流程控制测试 1 默认值路径:previewName={previewName},快速预览默认值命中。
label:input_default_end;
用户输入流程控制测试 1 结束:预期状态:快速预览时 previewName={previewName},应为 FastPreviewName;普通运行或普通快进时应等待用户确认,并按实际输入分支。

用户输入流程控制测试 2 开始:验证快速预览下 choose 的 defaultChoose 会决定流程。
choose:手动路径:input_manual_path|快速预览默认路径:input_fast_path -defaultChoose=2;
label:input_manual_path;
setVar:inputChoice=manual;
用户输入流程控制测试 2 手动路径:手动选择第 1 项时会看到这里。
jumpLabel:input_choice_after;
label:input_fast_path;
setVar:inputChoice=fastPreviewDefault;
用户输入流程控制测试 2 默认路径:快速预览默认选择第 2 项。
label:input_choice_after;
用户输入流程控制测试 2 结束:预期状态:快速预览时 inputChoice={inputChoice},应为 fastPreviewDefault;普通运行或普通快进时应等待玩家选择。

用户输入流程控制测试 3 开始:验证输入变量可以参与后续 choose 的显示条件。
choose:(previewName=="FastPreviewName")->默认输入路径:input_name_default|(previewName!="FastPreviewName")->其它输入路径:input_name_other -defaultChoose=1;
label:input_name_default;
setVar:inputNameBranch=defaultName;
用户输入流程控制测试 3 默认输入路径:快速预览默认输入会显示并选择这里。
jumpLabel:input_name_after;
label:input_name_other;
setVar:inputNameBranch=otherName;
用户输入流程控制测试 3 其它输入路径:手动输入其它名字时可选择这里。
label:input_name_after;
用户输入流程控制测试 3 结束:预期状态:快速预览时 inputNameBranch={inputNameBranch},应为 defaultName。
end;
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
变量流程控制测试 1 开始:验证数值变量驱动 jumpLabel -when。
setVar:varScore=2;
jumpLabel:var_score_high -when=varScore>1;
变量流程控制测试 1 异常路径:varScore>1 时不应看到这里。
jumpLabel:var_score_end;
label:var_score_high;
变量流程控制测试 1 命中路径:varScore={varScore},条件跳转生效。
label:var_score_end;
变量流程控制测试 1 结束:预期状态:varScore={varScore},应为 2,并且只经过命中路径。

变量流程控制测试 2 开始:验证 choose 的显示条件、启用条件和默认选择。
setVar:varHasTicket=true;
setVar:varDoorPower=1;
choose:(varHasTicket==true)->进入可见路径:var_ticket_ok|[varDoorPower>1]->能量路径:var_power_path|(varScore<0)->隐藏路径:var_hidden -defaultChoose=1;
label:var_ticket_ok;
setVar:varChoice=ticket;
变量流程控制测试 2 可见路径:快速预览默认选择这里。
jumpLabel:var_choose_end;
label:var_power_path;
setVar:varChoice=power;
变量流程控制测试 2 能量路径:只有 varDoorPower>1 时才能手动选择。
jumpLabel:var_choose_end;
label:var_hidden;
setVar:varChoice=hidden;
变量流程控制测试 2 隐藏路径:varScore<0 时才应显示。
label:var_choose_end;
变量流程控制测试 2 结束:预期状态:快速预览时 varChoice={varChoice},应为 ticket;能量路径应显示但不可选,隐藏路径不应显示。

变量流程控制测试 3 开始:验证快速预览下后续测试继承前面变量状态。
setVar:varScore=varScore + 3;
jumpLabel:var_inherited_ok -when=varScore>4;
变量流程控制测试 3 异常路径:varScore 继承并加 3 后应大于 4。
jumpLabel:var_inherited_end;
label:var_inherited_ok;
变量流程控制测试 3 命中路径:varScore={varScore},varChoice={varChoice}。
label:var_inherited_end;
变量流程控制测试 3 结束:预期状态:varScore={varScore},应为 5;varChoice={varChoice},应保持 ticket。
end;
2 changes: 1 addition & 1 deletion packages/webgal/public/game/scene/function_test.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
choose: Lip Sync Animation Test:demo_animation.txt | Variable interpolation test:demo_var.txt | Change Config:demo_changeConfig.txt | Performs:demo_performs.txt;
choose: Lip Sync Animation Test:demo_animation.txt | Variable interpolation test:demo_var.txt | Change Config:demo_changeConfig.txt | Performs:demo_performs.txt | Flow Control Test:demo_test_flow_control.txt | Variable Flow Control Test:demo_test_variable_flow_control.txt | Input Flow Control Test:demo_test_input_flow_control.txt | DOM Lifecycle Test:demo_test_dom_control.txt;
2 changes: 1 addition & 1 deletion packages/webgal/public/game/template/template.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
"name":"WebGAL Refine 2026",
"webgal-version":"4.5.20"
"webgal-version":"4.6.0"
}
4 changes: 2 additions & 2 deletions packages/webgal/public/webgal-engine.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
"schemaVersion": "1.0.0",
"id": "open-webgal.webgal",
"name": "WebGAL",
"version": "4.5.20",
"version": "4.6.0",
"type": "official",
"webgalVersion": "4.5.20",
"webgalVersion": "4.6.0",
"description": "界面美观、功能强大、易于开发的全新网页端视觉小说引擎",
"descriptions": {
"en": "A brand new web Visual Novel engine with a beautiful interface, powerful features, and easy development",
Expand Down
56 changes: 42 additions & 14 deletions packages/webgal/src/Core/Modules/animationFunctions.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import { generateUniversalSoftInAnimationObj } from '@/Core/controller/stage/pixi/animations/universalSoftIn';
import { logger } from '@/Core/util/logger';
import { generateUniversalSoftOffAnimationObj } from '@/Core/controller/stage/pixi/animations/universalSoftOff';
import { webgalStore } from '@/store/store';
import cloneDeep from 'lodash/cloneDeep';
import { baseTransform } from '@/store/stageInterface';
import { baseTransform } from '@/Core/Modules/stage/stageInterface';
import { generateTimelineObj } from '@/Core/controller/stage/pixi/animations/timeline';
import { WebGAL } from '@/Core/WebGAL';
import PixiStage, { IAnimationObject } from '@/Core/controller/stage/pixi/PixiController';
Expand All @@ -15,7 +14,8 @@ import {
DEFAULT_FIG_IN_DURATION,
DEFAULT_FIG_OUT_DURATION,
} from '../constants';
import { stageActions } from '@/store/stageReducer';
import { stageStateManager } from '@/Core/Modules/stage/stageStateManager';
import { AnimationFrame } from '@/Core/Modules/animations';

// eslint-disable-next-line max-params
export function getAnimationObject(
Expand All @@ -24,7 +24,34 @@ export function getAnimationObject(
duration: number,
writeDefault: boolean,
writeFullEffect = true,
syncEndStateToStageState = true,
) {
const mappedEffects = getAnimationTimeline(animationName, target, writeDefault, writeFullEffect);
if (mappedEffects) {
return generateTimelineObj(mappedEffects, target, duration, syncEndStateToStageState);
}
return null;
}

export function applyAnimationEndState(
animationName: string,
target: string,
writeDefault: boolean,
writeFullEffect = true,
) {
const mappedEffects = getAnimationTimeline(animationName, target, writeDefault, writeFullEffect);
if (!mappedEffects || mappedEffects.length === 0) return null;
const { duration, ease, ...endState } = mappedEffects[mappedEffects.length - 1];
stageStateManager.updateEffect({ target, transform: endState });
return mappedEffects;
}

export function getAnimationTimeline(
animationName: string,
target: string,
writeDefault: boolean,
writeFullEffect = true,
): AnimationFrame[] | null {
const effect = WebGAL.animationManager.getAnimations().find((ani) => ani.name === animationName);
if (effect) {
const unionKeys = new Set<string>();
Expand All @@ -38,7 +65,7 @@ export function getAnimationObject(
});
}
const mappedEffects = effect.effects.map((effect) => {
const targetSetEffect = webgalStore.getState().stage.effects.find((e) => e.target === target);
const targetSetEffect = stageStateManager.getCalculationStageState().effects.find((e) => e.target === target);
let newEffect;

if (!writeDefault && targetSetEffect && targetSetEffect.transform) {
Expand All @@ -62,7 +89,7 @@ export function getAnimationObject(
return newEffect;
});
logger.debug('装载自定义动画', mappedEffects);
return generateTimelineObj(mappedEffects, target, duration);
return mappedEffects;
}
return null;
}
Expand Down Expand Up @@ -95,17 +122,18 @@ export function getEnterExitAnimation(
duration = DEFAULT_BG_IN_DURATION;
}
duration =
webgalStore.getState().stage.animationSettings.find((setting) => setting.target === target)?.enterDuration ??
stageStateManager.getCalculationStageState().animationSettings.find((setting) => setting.target === target)
?.enterDuration ??
duration;
// 走默认动画
let animation: IAnimationObject | null = generateUniversalSoftInAnimationObj(realTarget ?? target, duration);

const transformState = webgalStore.getState().stage.effects;
const transformState = stageStateManager.getCalculationStageState().effects;
const targetEffect = transformState.find((effect) => effect.target === target);

const animationName = webgalStore
.getState()
.stage.animationSettings.find((setting) => setting.target === target)?.enterAnimationName;
const animationName = stageStateManager
.getCalculationStageState()
.animationSettings.find((setting) => setting.target === target)?.enterAnimationName;
if (animationName && !targetEffect) {
logger.debug('取代默认进入动画', target);
animation = getAnimationObject(animationName, realTarget ?? target, getAnimateDuration(animationName), false);
Expand All @@ -118,9 +146,9 @@ export function getEnterExitAnimation(
if (isBg) {
duration = DEFAULT_BG_OUT_DURATION;
}
const animationSettings = webgalStore
.getState()
.stage.animationSettings.find((setting) => setting.target === target);
const animationSettings = stageStateManager
.getCalculationStageState()
.animationSettings.find((setting) => setting.target === target);
duration = animationSettings?.exitDuration ?? duration;
// 走默认动画
let animation: IAnimationObject | null = generateUniversalSoftOffAnimationObj(realTarget ?? target, duration);
Expand All @@ -132,7 +160,7 @@ export function getEnterExitAnimation(
}
if (animationSettings) {
// 退出动画拿完后,删了这个设定
webgalStore.dispatch(stageActions.removeAnimationSettingsByTargetOff(target));
stageStateManager.removeAnimationSettingsByTargetOff(target);
logger.debug('删除退出动画设定', target);
}
return { duration, animation };
Expand Down
2 changes: 1 addition & 1 deletion packages/webgal/src/Core/Modules/animations.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ITransform } from '@/store/stageInterface';
import { ITransform } from '@/Core/Modules/stage/stageInterface';

export interface IUserAnimation {
name: string;
Expand Down
6 changes: 3 additions & 3 deletions packages/webgal/src/Core/Modules/backlog.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
/**
* 当前的backlog
*/
import { IEffect, IStageState } from '@/store/stageInterface';
import { webgalStore } from '@/store/store';
import { IEffect, IStageState } from '@/Core/Modules/stage/stageInterface';
import { ISaveScene } from '@/store/userDataInterface';
import cloneDeep from 'lodash/cloneDeep';

import { SYSTEM_CONFIG } from '@/config';
import { SceneManager } from '@/Core/Modules/scene';
import { stageStateManager } from '@/Core/Modules/stage/stageStateManager';

export interface IBacklogItem {
currentStageState: IStageState;
Expand Down Expand Up @@ -40,7 +40,7 @@ export class BacklogManager {
}
public saveCurrentStateToBacklog() {
// 存一下 Backlog
const currentStageState = webgalStore.getState().stage;
const currentStageState = stageStateManager.getCalculationStageState();
const stageStateToBacklog = cloneDeep(currentStageState);
stageStateToBacklog.PerformList.forEach((ele) => {
ele.script.args.forEach((argelement) => {
Expand Down
Loading
Loading