From f99e5ede1b21088532772f4de77573bfafb7b7a8 Mon Sep 17 00:00:00 2001 From: TremblingMoeNew <39522864+TremblingMoeNew@users.noreply.github.com> Date: Sun, 7 Dec 2025 00:10:10 +0800 Subject: [PATCH 1/4] Fix: setTransform cannot execute continuously when targeting the same object --- .../Core/Modules/perform/performController.ts | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/packages/webgal/src/Core/Modules/perform/performController.ts b/packages/webgal/src/Core/Modules/perform/performController.ts index f303a64b8..7de5585c5 100644 --- a/packages/webgal/src/Core/Modules/perform/performController.ts +++ b/packages/webgal/src/Core/Modules/perform/performController.ts @@ -50,7 +50,7 @@ export class PerformController { // perform.isOver = true; if (!perform.isHoldOn) { // 如果不是保持演出,清除 - this.unmountPerform(perform.performName); + this.softUnmountPerformObject(perform); } }, perform.duration); @@ -103,6 +103,26 @@ export class PerformController { } } + public softUnmountPerformObject(perform: IPerform) { + const idx = this.performList.indexOf(perform) + if (idx < 0) return; + perform.stopFunction(); + clearTimeout(perform.stopTimeout as unknown as number); + /** + * 在演出列表里删除演出对象的操作必须在调用 goNextWhenOver 之前 + * 因为 goNextWhenOver 会调用 nextSentence,而 nextSentence 会清除目前未结束的演出 + * 那么 nextSentence 函数就会删除这个演出,但是此时,在这个上下文,i 已经被确定了 + * 所以 goNextWhenOver 后的代码会多删东西,解决方法就是在调用 goNextWhenOver 前先删掉这个演出对象 + * 此问题对所有 goNextWhenOver 属性为真的演出都有影响,但只有 2 个演出有此问题 + */ + this.performList.splice(idx, 1); + if (perform.goNextWhenOver) { + // nextSentence(); + this.goNextWhenOver(); + } + + } + public erasePerformFromState(name: string) { webgalStore.dispatch(stageActions.removePerformByName(name)); } From 623570492d6de4a733d469841428e5abb693e498 Mon Sep 17 00:00:00 2001 From: TremblingMoeNew <39522864+TremblingMoeNew@users.noreply.github.com> Date: Sun, 7 Dec 2025 00:18:45 +0800 Subject: [PATCH 2/4] Feat: Decouple properties in timeline animations & support parallel animation for setTransform command --- .../src/Core/Modules/animationFunctions.ts | 25 +++++++- .../Core/Modules/perform/performController.ts | 24 ++++---- .../Core/Modules/perform/performInterface.ts | 2 + .../controller/stage/pixi/PixiController.ts | 8 ++- .../generateTransformAnimationObj.ts | 16 ++++- .../stage/pixi/animations/timeline.ts | 14 ++--- .../src/Core/gameScripts/setTransform.ts | 16 ++--- .../src/Stage/MainStage/useSetEffects.ts | 3 +- packages/webgal/src/store/stageInterface.ts | 58 +++++++++---------- packages/webgal/src/store/stageReducer.ts | 9 ++- 10 files changed, 113 insertions(+), 62 deletions(-) diff --git a/packages/webgal/src/Core/Modules/animationFunctions.ts b/packages/webgal/src/Core/Modules/animationFunctions.ts index 1c5fa4471..b8e85a23c 100644 --- a/packages/webgal/src/Core/Modules/animationFunctions.ts +++ b/packages/webgal/src/Core/Modules/animationFunctions.ts @@ -7,6 +7,8 @@ import { baseTransform } from '@/store/stageInterface'; import { generateTimelineObj } from '@/Core/controller/stage/pixi/animations/timeline'; import { WebGAL } from '@/Core/WebGAL'; import PixiStage, { IAnimationObject } from '@/Core/controller/stage/pixi/PixiController'; +import { IUserAnimation } from './animations'; +import { pickBy } from 'lodash'; import { DEFAULT_BG_IN_DURATION, DEFAULT_BG_OUT_DURATION, @@ -18,12 +20,25 @@ import { export function getAnimationObject(animationName: string, target: string, duration: number, writeDefault: boolean) { const effect = WebGAL.animationManager.getAnimations().find((ani) => ani.name === animationName); if (effect) { + const unionKeys = new Set(); + const unionScaleKeys = new Set(); + const unionPositionKeys = new Set(); + effect.effects.forEach((effect) => { + Object.keys(effect).forEach((k) => unionKeys.add(k)); + if (effect.scale) Object.keys(effect.scale).forEach((k) => unionScaleKeys.add(k)); + if (effect.position) Object.keys(effect.position).forEach((k) => unionPositionKeys.add(k)); + }); const mappedEffects = effect.effects.map((effect) => { const targetSetEffect = webgalStore.getState().stage.effects.find((e) => e.target === target); let newEffect; if (!writeDefault && targetSetEffect && targetSetEffect.transform) { - newEffect = cloneDeep({ ...targetSetEffect.transform, duration: 0, ease: '' }); + const targetScale = pickBy(targetSetEffect.transform.scale, (source, key)=> unionScaleKeys.has(key)) + const targetPosition = pickBy(targetSetEffect.transform.position, (source, key)=> unionPositionKeys.has(key)) + const originalTransform = { ...pickBy(targetSetEffect.transform, (source, key)=> unionKeys.has(key))}; + originalTransform.scale = targetScale + originalTransform.position = targetPosition + newEffect = cloneDeep({ ...originalTransform, duration: 0, ease: '' }); } else { newEffect = cloneDeep({ ...baseTransform, duration: 0, ease: '' }); } @@ -51,6 +66,14 @@ export function getAnimateDuration(animationName: string) { return 0; } +export function getAnimateDurationFromObj(animation: IUserAnimation) { + let duration = 0; + animation.effects.forEach((e) => { + duration += e.duration; + }); + return duration; +} + // eslint-disable-next-line max-params export function getEnterExitAnimation( target: string, diff --git a/packages/webgal/src/Core/Modules/perform/performController.ts b/packages/webgal/src/Core/Modules/perform/performController.ts index 7de5585c5..7d40a687b 100644 --- a/packages/webgal/src/Core/Modules/perform/performController.ts +++ b/packages/webgal/src/Core/Modules/perform/performController.ts @@ -19,17 +19,19 @@ export class PerformController { public performList: Array = []; public arrangeNewPerform(perform: IPerform, script: ISentence, syncPerformState = true) { - // 检查演出列表内是否有相同的演出,如果有,一定是出了什么问题 - const dupPerformIndex = this.performList.findIndex((p) => p.performName === perform.performName); - if (dupPerformIndex > -1) { - // 结束并删除全部重复演出 - for (let i = 0; i < this.performList.length; i++) { - const e = this.performList[i]; - if (e.performName === perform.performName) { - e.stopFunction(); - clearTimeout(e.stopTimeout as unknown as number); - this.performList.splice(i, 1); - i--; + if (!perform.isParallel){ + // 检查演出列表内是否有相同的演出,如果有,一定是出了什么问题 + const dupPerformIndex = this.performList.findIndex((p) => p.performName === perform.performName); + if (dupPerformIndex > -1) { + // 结束并删除全部重复演出 + for (let i = 0; i < this.performList.length; i++) { + const e = this.performList[i]; + if (e.performName === perform.performName) { + e.stopFunction(); + clearTimeout(e.stopTimeout as unknown as number); + this.performList.splice(i, 1); + i--; + } } } } diff --git a/packages/webgal/src/Core/Modules/perform/performInterface.ts b/packages/webgal/src/Core/Modules/perform/performInterface.ts index 8e00b2f90..3c2b5f63a 100644 --- a/packages/webgal/src/Core/Modules/perform/performInterface.ts +++ b/packages/webgal/src/Core/Modules/perform/performInterface.ts @@ -23,6 +23,8 @@ export interface IPerform { arrangePerformPromise?: Promise; // 跳过由 nextSentence 函数引发的演出回收 skipNextCollect?: boolean; + // + isParallel?: boolean; } // next之后,可以被打断的演出会被打断,不能被打断的演出会继续,阻塞next的演出会阻止next被响应。 diff --git a/packages/webgal/src/Core/controller/stage/pixi/PixiController.ts b/packages/webgal/src/Core/controller/stage/pixi/PixiController.ts index 76ec7f3d6..799f64323 100644 --- a/packages/webgal/src/Core/controller/stage/pixi/PixiController.ts +++ b/packages/webgal/src/Core/controller/stage/pixi/PixiController.ts @@ -10,6 +10,8 @@ import { SCREEN_CONSTANTS } from '@/Core/util/constants'; import { logger } from '@/Core/util/logger'; import { v4 as uuid } from 'uuid'; import { cloneDeep, isEqual } from 'lodash'; +import omitBy from 'lodash/omitBy'; +import isUndefined from 'lodash/isUndefined'; import * as PIXI from 'pixi.js'; import { INSTALLED } from 'pixi.js'; import { GifResource } from './GifResource'; @@ -71,9 +73,9 @@ export default class PixiStage { if (!source) return; const targetScale = target.scale; const targetPosition = target.position; - if (target.scale) Object.assign(targetScale, source.scale); - if (target.position) Object.assign(targetPosition, source.position); - Object.assign(target, source); + if (target.scale) Object.assign(targetScale!, omitBy(source.scale,isUndefined)); + if (target.position) Object.assign(targetPosition!, omitBy(source.position,isUndefined)); + Object.assign(target, omitBy(source,isUndefined)); target.scale = targetScale; target.position = targetPosition; } diff --git a/packages/webgal/src/Core/controller/stage/pixi/animations/generateTransformAnimationObj.ts b/packages/webgal/src/Core/controller/stage/pixi/animations/generateTransformAnimationObj.ts index 1cf2a38af..693cf4989 100644 --- a/packages/webgal/src/Core/controller/stage/pixi/animations/generateTransformAnimationObj.ts +++ b/packages/webgal/src/Core/controller/stage/pixi/animations/generateTransformAnimationObj.ts @@ -1,5 +1,6 @@ import { AnimationFrame } from '@/Core/Modules/animations'; import { webgalStore } from '@/store/store'; +import { has, pickBy } from 'lodash'; import isNull from 'lodash/isNull'; type AnimationObj = Array; @@ -10,6 +11,7 @@ export function generateTransformAnimationObj( applyFrame: AnimationFrame, duration: number | string | boolean | null, ease: string, + writeFullEffect: boolean = true, ): AnimationObj { let animationObj; // 获取那个 target 的当前变换 @@ -25,8 +27,18 @@ export function generateTransformAnimationObj( // 找到 effect if (targetEffect) { - const effectWithDuration = { ...targetEffect!.transform!, duration: 0, ease }; - animationObj.unshift(effectWithDuration); + if (writeFullEffect) { + const effectWithDuration = { ...targetEffect!.transform!, duration: 0, ease }; + animationObj.unshift(effectWithDuration); + } + else { + const targetScale = pickBy(targetEffect!.transform!.scale, (source, key)=> has(applyFrame.scale, key)) + const targetPosition = pickBy(targetEffect!.transform!.position, (source, key)=> has(applyFrame.position, key)) + const effectWithDuration = { ...pickBy(targetEffect!.transform!, (source, key)=> has(applyFrame, key) ), duration: 0, ease }; + effectWithDuration.scale = targetScale + effectWithDuration.position = targetPosition + animationObj.unshift(effectWithDuration); + } } else { // 应用默认effect,也就是最终的 effect 的 alpha = 0 版本 const effectWithDuration = { ...applyFrame, alpha: 0, duration: 0, ease }; diff --git a/packages/webgal/src/Core/controller/stage/pixi/animations/timeline.ts b/packages/webgal/src/Core/controller/stage/pixi/animations/timeline.ts index a49d55787..145c5c13b 100644 --- a/packages/webgal/src/Core/controller/stage/pixi/animations/timeline.ts +++ b/packages/webgal/src/Core/controller/stage/pixi/animations/timeline.ts @@ -33,7 +33,7 @@ export function generateTimelineObj( currentDelay += segmentDuration; const { position, scale, ...segmentValues } = segment; // 不能用 scale,因为 popmotion 不能用嵌套 - values.push({ x: position.x, y: position.y, scaleX: scale.x, scaleY: scale.y, ...segmentValues }); + values.push({ x: position?.x, y: position?.y, scaleX: scale?.x, scaleY: scale?.y, ...segmentValues }); // Easing 需要比 values 的长度少一个 if (i > 0) { easeArray.push(stringToEasing(segment.ease)); @@ -74,11 +74,11 @@ export function generateTimelineObj( if (target?.pixiContainer) { // 不能赋值到 position,因为 x 和 y 被 WebGALPixiContainer 代理,而 position 属性没有代理 const { position, scale, ...state } = getStartStateEffect(); - const assignValue = omitBy({ x: position.x, y: position.y, ...state }, isUndefined); + const assignValue = omitBy({ x: position?.x, y: position?.y, ...state }, isUndefined); // @ts-ignore PixiStage.assignTransform(target?.pixiContainer, assignValue); - if (target?.pixiContainer) { - if (!isUndefined(scale.x)) { + if (scale && target?.pixiContainer) { + if (!isUndefined(scale?.x)) { target.pixiContainer.scale.x = scale.x; } if (!isUndefined(scale?.y)) { @@ -101,11 +101,11 @@ export function generateTimelineObj( // 不能赋值到 position,因为 x 和 y 被 WebGALPixiContainer 代理,而 position 属性没有代理 // 不能赋值到 position,因为 x 和 y 被 WebGALPixiContainer 代理,而 position 属性没有代理 const { position, scale, ...state } = getEndStateEffect(); - const assignValue = omitBy({ x: position.x, y: position.y, ...state }, isUndefined); + const assignValue = omitBy({ x: position?.x, y: position?.y, ...state }, isUndefined); // @ts-ignore PixiStage.assignTransform(target?.pixiContainer, assignValue); - if (target?.pixiContainer) { - if (!isUndefined(scale.x)) { + if (scale && target?.pixiContainer) { + if (!isUndefined(scale?.x)) { target.pixiContainer.scale.x = scale.x; } if (!isUndefined(scale?.y)) { diff --git a/packages/webgal/src/Core/gameScripts/setTransform.ts b/packages/webgal/src/Core/gameScripts/setTransform.ts index 149287293..6e25dd291 100644 --- a/packages/webgal/src/Core/gameScripts/setTransform.ts +++ b/packages/webgal/src/Core/gameScripts/setTransform.ts @@ -10,15 +10,15 @@ import { baseTransform, ITransform } from '@/store/stageInterface'; import { AnimationFrame, IUserAnimation } from '../Modules/animations'; import { generateTransformAnimationObj } from '@/Core/controller/stage/pixi/animations/generateTransformAnimationObj'; import { WebGAL } from '@/Core/WebGAL'; -import { getAnimateDuration, getAnimationObject } from '../Modules/animationFunctions'; - +import { getAnimateDurationFromObj, getAnimationObject } from '../Modules/animationFunctions'; +import { v4 as uuid } from 'uuid'; /** * 设置变换 * @param sentence */ export const setTransform = (sentence: ISentence): IPerform => { const startDialogKey = webgalStore.getState().stage.currentDialogKey; - const animationName = (Math.random() * 10).toString(16); + const animationName = uuid(); const animationString = sentence.content; let animationObj: AnimationFrame[]; @@ -27,14 +27,16 @@ export const setTransform = (sentence: ISentence): IPerform => { const writeDefault = getBooleanArgByKey(sentence, 'writeDefault') ?? false; const target = getStringArgByKey(sentence, 'target') ?? '0'; const keep = getBooleanArgByKey(sentence, 'keep') ?? false; + const parallel = getBooleanArgByKey(sentence, 'parallel') ?? false; const performInitName = `animation-${target}`; - WebGAL.gameplay.performController.unmountPerform(performInitName, true); + if (!parallel) WebGAL.gameplay.performController.unmountPerform(performInitName, true); try { const frame = JSON.parse(animationString) as AnimationFrame; - animationObj = generateTransformAnimationObj(target, frame, duration, ease); + // writeDefault时需要完整的当前effect,其他时候不需要 + animationObj = generateTransformAnimationObj(target, frame, duration, ease, writeDefault); console.log('animationObj:', animationObj); } catch (e) { // 解析都错误了,歇逼吧 @@ -43,8 +45,7 @@ export const setTransform = (sentence: ISentence): IPerform => { const newAnimation: IUserAnimation = { name: animationName, effects: animationObj }; WebGAL.animationManager.addAnimation(newAnimation); - const animationDuration = getAnimateDuration(animationName); - + const animationDuration = getAnimateDurationFromObj(newAnimation); const key = `${target}-${animationName}-${animationDuration}`; let keepAnimationStopped = false; setTimeout(() => { @@ -82,5 +83,6 @@ export const setTransform = (sentence: ISentence): IPerform => { blockingNext: () => false, blockingAuto: () => !keep, stopTimeout: undefined, // 暂时不用,后面会交给自动清除 + isParallel: parallel, }; }; diff --git a/packages/webgal/src/Stage/MainStage/useSetEffects.ts b/packages/webgal/src/Stage/MainStage/useSetEffects.ts index c5a1790ad..3c1a2b517 100644 --- a/packages/webgal/src/Stage/MainStage/useSetEffects.ts +++ b/packages/webgal/src/Stage/MainStage/useSetEffects.ts @@ -2,6 +2,7 @@ import { baseTransform, IEffect, IStageState, ITransform } from '@/store/stageIn import { WebGAL } from '@/Core/WebGAL'; import PixiStage from '@/Core/controller/stage/pixi/PixiController'; +import { isUndefined, omitBy } from 'lodash'; export function setStageObjectEffects(stageState: IStageState) { const effects = stageState.effects; @@ -42,5 +43,5 @@ function convertTransform(transform: ITransform | undefined) { return {}; } const { position, ...rest } = transform; - return { ...rest, x: position.x, y: position.y }; + return omitBy({ ...rest, x: position?.x, y: position?.y },isUndefined); } diff --git a/packages/webgal/src/store/stageInterface.ts b/packages/webgal/src/store/stageInterface.ts index 5494635d2..9269e7edd 100644 --- a/packages/webgal/src/store/stageInterface.ts +++ b/packages/webgal/src/store/stageInterface.ts @@ -25,41 +25,41 @@ export interface IChooseItem { } export interface ITransform { - alpha: number; - scale: { - x: number; - y: number; + alpha?: number; + scale?: { + x?: number; + y?: number; }; // pivot: { // x: number; // y: number; // }; - position: { - x: number; - y: number; + position?: { + x?: number; + y?: number; }; - rotation: number; - blur: number; - brightness: number; - contrast: number; - saturation: number; - gamma: number; - colorRed: number; - colorGreen: number; - colorBlue: number; - bevel: number; - bevelThickness: number; - bevelRotation: number; - bevelSoftness: number; - bevelRed: number; - bevelGreen: number; - bevelBlue: number; - bloom: number; - bloomBrightness: number; - bloomBlur: number; - bloomThreshold: number; - shockwaveFilter: number; - radiusAlphaFilter: number; + rotation?: number; + blur?: number; + brightness?: number; + contrast?: number; + saturation?: number; + gamma?: number; + colorRed?: number; + colorGreen?: number; + colorBlue?: number; + bevel?: number; + bevelThickness?: number; + bevelRotation?: number; + bevelSoftness?: number; + bevelRed?: number; + bevelGreen?: number; + bevelBlue?: number; + bloom?: number; + bloomBrightness?: number; + bloomBlur?: number; + bloomThreshold?: number; + shockwaveFilter?: number; + radiusAlphaFilter?: number; } /** diff --git a/packages/webgal/src/store/stageReducer.ts b/packages/webgal/src/store/stageReducer.ts index 450efbe38..a67acd4e2 100644 --- a/packages/webgal/src/store/stageReducer.ts +++ b/packages/webgal/src/store/stageReducer.ts @@ -23,6 +23,7 @@ import { createSlice, PayloadAction } from '@reduxjs/toolkit'; import cloneDeep from 'lodash/cloneDeep'; import { commandType } from '@/Core/controller/scene/sceneInterface'; import { STAGE_KEYS } from '@/Core/constants'; +import { isUndefined, omitBy } from 'lodash'; // 初始化舞台数据 @@ -123,7 +124,13 @@ const stageSlice = createSlice({ const effectIndex = state.effects.findIndex((e) => e.target === target); if (effectIndex >= 0) { // Update the existing effect - state.effects[effectIndex].transform = transform; + const targetScale = state.effects[effectIndex]!.transform!.scale; + const targetPosition = state.effects[effectIndex]!.transform!.position; + if (transform!.scale) Object.assign(targetScale!, omitBy(transform!.scale,isUndefined)); + if (transform!.position) Object.assign(targetPosition!, omitBy(transform!.position,isUndefined)); + Object.assign(state.effects[effectIndex]!.transform!, omitBy(transform,isUndefined)) + state.effects[effectIndex].transform!.scale = targetScale; + state.effects[effectIndex].transform!.position = targetPosition; } else { // Add a new effect state.effects.push({ From 07a61d1f91d3ce43d81f128ca2b3ee7f36a12998 Mon Sep 17 00:00:00 2001 From: TremblingMoeNew <39522864+TremblingMoeNew@users.noreply.github.com> Date: Sun, 7 Dec 2025 00:19:48 +0800 Subject: [PATCH 3/4] Feat: Support parallel animation for setTempAnimation command --- .../webgal/src/Core/gameScripts/setTempAnimation.ts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/packages/webgal/src/Core/gameScripts/setTempAnimation.ts b/packages/webgal/src/Core/gameScripts/setTempAnimation.ts index 8da9635f7..778ef0df7 100644 --- a/packages/webgal/src/Core/gameScripts/setTempAnimation.ts +++ b/packages/webgal/src/Core/gameScripts/setTempAnimation.ts @@ -8,8 +8,9 @@ import { generateTimelineObj } from '@/Core/controller/stage/pixi/animations/tim import cloneDeep from 'lodash/cloneDeep'; import { baseTransform } from '@/store/stageInterface'; import { IUserAnimation } from '../Modules/animations'; -import { getAnimateDuration, getAnimationObject } from '@/Core/Modules/animationFunctions'; +import { getAnimateDurationFromObj, getAnimationObject } from '@/Core/Modules/animationFunctions'; import { WebGAL } from '@/Core/WebGAL'; +import { v4 as uuid } from 'uuid'; /** * 设置临时动画 @@ -17,7 +18,7 @@ import { WebGAL } from '@/Core/WebGAL'; */ export const setTempAnimation = (sentence: ISentence): IPerform => { const startDialogKey = webgalStore.getState().stage.currentDialogKey; - const animationName = (Math.random() * 10).toString(16); + const animationName = uuid(); const animationString = sentence.content; let animationObj; try { @@ -27,15 +28,16 @@ export const setTempAnimation = (sentence: ISentence): IPerform => { } const newAnimation: IUserAnimation = { name: animationName, effects: animationObj }; WebGAL.animationManager.addAnimation(newAnimation); - const animationDuration = getAnimateDuration(animationName); + const animationDuration = getAnimateDurationFromObj(newAnimation); const target = getStringArgByKey(sentence, 'target') ?? '0'; const writeDefault = getBooleanArgByKey(sentence, 'writeDefault') ?? false; const keep = getBooleanArgByKey(sentence, 'keep') ?? false; + const parallel = getBooleanArgByKey(sentence, 'parallel') ?? false; const key = `${target}-${animationName}-${animationDuration}`; const performInitName = `animation-${target}`; - WebGAL.gameplay.performController.unmountPerform(performInitName, true); + if (!parallel) WebGAL.gameplay.performController.unmountPerform(performInitName, true); let stopFunction = () => {}; setTimeout(() => { @@ -67,5 +69,6 @@ export const setTempAnimation = (sentence: ISentence): IPerform => { blockingNext: () => false, blockingAuto: () => !keep, stopTimeout: undefined, // 暂时不用,后面会交给自动清除 + isParallel: parallel, }; }; From 650fcfd3ffef80f96a14c027e15eef9c10fdc822 Mon Sep 17 00:00:00 2001 From: TremblingMoeNew <39522864+TremblingMoeNew@users.noreply.github.com> Date: Sun, 7 Dec 2025 13:48:48 +0800 Subject: [PATCH 4/4] Feat: Support parallel animation for setAnimation command; Fix: potential runtime error caused by undefined parameters reported by review bot --- .../game/scene/demo_parallel_animation.txt | 31 +++++++++++++++++++ .../src/Core/Modules/animationFunctions.ts | 4 +-- .../controller/stage/pixi/PixiController.ts | 4 +-- .../generateTransformAnimationObj.ts | 6 ++-- .../src/Core/gameScripts/setAnimation.ts | 4 ++- packages/webgal/src/store/stageReducer.ts | 19 +++++++----- 6 files changed, 53 insertions(+), 15 deletions(-) create mode 100644 packages/webgal/public/game/scene/demo_parallel_animation.txt diff --git a/packages/webgal/public/game/scene/demo_parallel_animation.txt b/packages/webgal/public/game/scene/demo_parallel_animation.txt new file mode 100644 index 000000000..796ac524b --- /dev/null +++ b/packages/webgal/public/game/scene/demo_parallel_animation.txt @@ -0,0 +1,31 @@ +changeBg:WebGalEnter.webp -next; +changeFigure:stand.webp -id=figure01 -transform={"position":{"x":1000,"y":720}}; +;演示setAnimation平行执行 +setAnimation:shockwaveIn -target=figure01 -next +setAnimation:move-front-and-back -target=figure01 -parallel +;演示通过-continue接续执行两个常规setTransform正常运作、不被打断 +setTransform:{"position":{"x":-1000}} -duration=5000 -target=figure01 -continue +setTransform:{"position":{"x":1000}} -duration=5000 -target=figure01 +;演示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:[{"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 -target=figure01 -duration=500 +setTransform:{"position":{"x":1000,"y":720}} -target=figure01 -next; +setAnimation:shockwaveOut -target=figure01 -parallel +;演示setTempAnimation平行执行 +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:{"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 diff --git a/packages/webgal/src/Core/Modules/animationFunctions.ts b/packages/webgal/src/Core/Modules/animationFunctions.ts index b8e85a23c..05289122c 100644 --- a/packages/webgal/src/Core/Modules/animationFunctions.ts +++ b/packages/webgal/src/Core/Modules/animationFunctions.ts @@ -33,8 +33,8 @@ export function getAnimationObject(animationName: string, target: string, durati let newEffect; if (!writeDefault && targetSetEffect && targetSetEffect.transform) { - const targetScale = pickBy(targetSetEffect.transform.scale, (source, key)=> unionScaleKeys.has(key)) - const targetPosition = pickBy(targetSetEffect.transform.position, (source, key)=> unionPositionKeys.has(key)) + const targetScale = pickBy(targetSetEffect.transform.scale || {}, (source, key)=> unionScaleKeys.has(key)) + const targetPosition = pickBy(targetSetEffect.transform.position || {}, (source, key)=> unionPositionKeys.has(key)) const originalTransform = { ...pickBy(targetSetEffect.transform, (source, key)=> unionKeys.has(key))}; originalTransform.scale = targetScale originalTransform.position = targetPosition diff --git a/packages/webgal/src/Core/controller/stage/pixi/PixiController.ts b/packages/webgal/src/Core/controller/stage/pixi/PixiController.ts index 799f64323..d63aa236d 100644 --- a/packages/webgal/src/Core/controller/stage/pixi/PixiController.ts +++ b/packages/webgal/src/Core/controller/stage/pixi/PixiController.ts @@ -73,8 +73,8 @@ export default class PixiStage { if (!source) return; const targetScale = target.scale; const targetPosition = target.position; - if (target.scale) Object.assign(targetScale!, omitBy(source.scale,isUndefined)); - if (target.position) Object.assign(targetPosition!, omitBy(source.position,isUndefined)); + if (target.scale) Object.assign(targetScale!, omitBy(source.scale || {},isUndefined)); + if (target.position) Object.assign(targetPosition!, omitBy(source.position || {},isUndefined)); Object.assign(target, omitBy(source,isUndefined)); target.scale = targetScale; target.position = targetPosition; diff --git a/packages/webgal/src/Core/controller/stage/pixi/animations/generateTransformAnimationObj.ts b/packages/webgal/src/Core/controller/stage/pixi/animations/generateTransformAnimationObj.ts index 693cf4989..598f81546 100644 --- a/packages/webgal/src/Core/controller/stage/pixi/animations/generateTransformAnimationObj.ts +++ b/packages/webgal/src/Core/controller/stage/pixi/animations/generateTransformAnimationObj.ts @@ -32,9 +32,9 @@ export function generateTransformAnimationObj( animationObj.unshift(effectWithDuration); } else { - const targetScale = pickBy(targetEffect!.transform!.scale, (source, key)=> has(applyFrame.scale, key)) - const targetPosition = pickBy(targetEffect!.transform!.position, (source, key)=> has(applyFrame.position, key)) - const effectWithDuration = { ...pickBy(targetEffect!.transform!, (source, key)=> has(applyFrame, key) ), duration: 0, ease }; + const targetScale = pickBy(targetEffect.transform?.scale || {}, (source, key)=> has(applyFrame.scale, key)) + const targetPosition = pickBy(targetEffect.transform?.position || {}, (source, key)=> has(applyFrame.position, key)) + const effectWithDuration = { ...pickBy(targetEffect.transform || {}, (source, key)=> has(applyFrame, key) ), duration: 0, ease }; effectWithDuration.scale = targetScale effectWithDuration.position = targetPosition animationObj.unshift(effectWithDuration); diff --git a/packages/webgal/src/Core/gameScripts/setAnimation.ts b/packages/webgal/src/Core/gameScripts/setAnimation.ts index 748b4498a..a9a384c0d 100644 --- a/packages/webgal/src/Core/gameScripts/setAnimation.ts +++ b/packages/webgal/src/Core/gameScripts/setAnimation.ts @@ -20,11 +20,12 @@ export const setAnimation = (sentence: ISentence): IPerform => { target = target !== '' ? target : 'default_id'; const writeDefault = getBooleanArgByKey(sentence, 'writeDefault') ?? false; const keep = getBooleanArgByKey(sentence, 'keep') ?? false; + const parallel = getBooleanArgByKey(sentence, 'parallel') ?? false; const key = `${target}-${animationName}-${animationDuration}`; const performInitName = `animation-${target}`; - WebGAL.gameplay.performController.unmountPerform(performInitName, true); + if (!parallel) WebGAL.gameplay.performController.unmountPerform(performInitName, true); let stopFunction; setTimeout(() => { @@ -56,5 +57,6 @@ export const setAnimation = (sentence: ISentence): IPerform => { blockingNext: () => false, blockingAuto: () => !keep, stopTimeout: undefined, // 暂时不用,后面会交给自动清除 + isParallel: parallel, }; }; diff --git a/packages/webgal/src/store/stageReducer.ts b/packages/webgal/src/store/stageReducer.ts index a67acd4e2..2c9320916 100644 --- a/packages/webgal/src/store/stageReducer.ts +++ b/packages/webgal/src/store/stageReducer.ts @@ -124,13 +124,18 @@ const stageSlice = createSlice({ const effectIndex = state.effects.findIndex((e) => e.target === target); if (effectIndex >= 0) { // Update the existing effect - const targetScale = state.effects[effectIndex]!.transform!.scale; - const targetPosition = state.effects[effectIndex]!.transform!.position; - if (transform!.scale) Object.assign(targetScale!, omitBy(transform!.scale,isUndefined)); - if (transform!.position) Object.assign(targetPosition!, omitBy(transform!.position,isUndefined)); - Object.assign(state.effects[effectIndex]!.transform!, omitBy(transform,isUndefined)) - state.effects[effectIndex].transform!.scale = targetScale; - state.effects[effectIndex].transform!.position = targetPosition; + if (!state.effects[effectIndex].transform) { + state.effects[effectIndex].transform = transform; + } + else if (transform){ + const targetScale = state.effects[effectIndex].transform.scale || {}; + const targetPosition = state.effects[effectIndex].transform.position || {}; + if (transform.scale) Object.assign(targetScale, omitBy(transform.scale,isUndefined)); + if (transform.position) Object.assign(targetPosition, omitBy(transform.position,isUndefined)); + Object.assign(state.effects[effectIndex].transform, omitBy(transform,isUndefined)) + state.effects[effectIndex].transform.scale = targetScale; + state.effects[effectIndex].transform.position = targetPosition; + } } else { // Add a new effect state.effects.push({