From da79845c8db6e75a13527cfb7d58e591896f412b Mon Sep 17 00:00:00 2001 From: Hardy--Lee <> Date: Thu, 4 Sep 2025 16:22:38 +0800 Subject: [PATCH] fix: enter exit duration fix: transform animation type refactor: resume stage object exiting fix: exit animation conflict --- .../src/Core/Modules/animationFunctions.ts | 240 +++++++++++++++--- packages/webgal/src/Core/constants.ts | 12 +- .../controller/stage/pixi/PixiController.ts | 124 +++++---- .../generateTransformAnimationObj.ts | 51 ++-- .../stage/pixi/animations/universalSoftIn.ts | 3 +- .../stage/pixi/animations/universalSoftOff.ts | 3 +- .../src/Core/gameScripts/changeBg/index.ts | 70 ++--- .../src/Core/gameScripts/changeFigure.ts | 103 +++----- .../src/Core/gameScripts/setAnimation.ts | 2 +- .../Core/gameScripts/setComplexAnimation.ts | 2 +- .../src/Core/gameScripts/setTempAnimation.ts | 2 +- .../src/Core/gameScripts/setTransform.ts | 6 +- .../src/Core/gameScripts/setTransition.ts | 2 +- .../webgal/src/Stage/MainStage/useSetBg.ts | 39 +-- .../src/Stage/MainStage/useSetFigure.ts | 147 ++--------- packages/webgal/src/store/stageReducer.ts | 8 +- 16 files changed, 393 insertions(+), 421 deletions(-) diff --git a/packages/webgal/src/Core/Modules/animationFunctions.ts b/packages/webgal/src/Core/Modules/animationFunctions.ts index 269997874..edf2167c1 100644 --- a/packages/webgal/src/Core/Modules/animationFunctions.ts +++ b/packages/webgal/src/Core/Modules/animationFunctions.ts @@ -3,11 +3,15 @@ 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, IEffect, ITransform } 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 { DEFALUT_FIG_IN_DURATION, DEFALUT_FIG_OUT_DURATION } from '../constants'; +import PixiStage, { IAnimationObject, IStageObject } from '@/Core/controller/stage/pixi/PixiController'; +import { DEFAULT_STAGE_OBJECT_ENTER_DURATION, DEFAULT_STAGE_OBJECT_EXIT_DURATION } from '../constants'; +import { AnimationFrame, IUserAnimation } from './animations'; +import { generateTransformAnimationObj } from '../controller/stage/pixi/animations/generateTransformAnimationObj'; +import { getNumberArgByKey, getStringArgByKey } from '../util/getSentenceArg'; +import { ISentence } from '../controller/scene/sceneInterface'; // eslint-disable-next-line max-params export function getAnimationObject(animationName: string, target: string, duration: number, writeDefault: boolean) { @@ -46,52 +50,216 @@ export function getAnimateDuration(animationName: string) { return 0; } +/*** + * 获取入场或退场动画的对象 + * @param target 目标对象 + * @param type 动画类型,'enter' 或 'exit' + * @param realTarget 真正的目标对象,用于立绘和背景移除时,打上特殊标记 + */ // eslint-disable-next-line max-params export function getEnterExitAnimation( target: string, type: 'enter' | 'exit', - isBg = false, - realTarget?: string, // 用于立绘和背景移除时,以当前时间打上特殊标记 + realTarget?: string, ): { duration: number; animation: IAnimationObject | null; } { + let duration = type === 'enter' ? DEFAULT_STAGE_OBJECT_ENTER_DURATION : DEFAULT_STAGE_OBJECT_EXIT_DURATION; + // 走默认动画 + let animation: IAnimationObject | null = null; + let animationName: string | undefined; if (type === 'enter') { - let duration = DEFALUT_FIG_IN_DURATION; - if (isBg) { - duration = 1500; - } - // 走默认动画 - let animation: IAnimationObject | null = generateUniversalSoftInAnimationObj(realTarget ?? target, duration); - - const transformState = webgalStore.getState().stage.effects; - const targetEffect = transformState.find((effect) => effect.target === target); - - const animarionName = WebGAL.animationManager.nextEnterAnimationName.get(target); - if (animarionName && !targetEffect) { - logger.debug('取代默认进入动画', target); - animation = getAnimationObject(animarionName, realTarget ?? target, getAnimateDuration(animarionName), false); - duration = getAnimateDuration(animarionName); - // 用后重置 + animation = generateUniversalSoftInAnimationObj(realTarget ?? target, duration); + animationName = WebGAL.animationManager.nextEnterAnimationName.get(target); + } else { + animation = generateUniversalSoftOffAnimationObj(realTarget ?? target, duration); + animationName = WebGAL.animationManager.nextExitAnimationName.get(target); + } + + const transformState = webgalStore.getState().stage.effects; + const targetEffect = transformState.find((effect) => effect.target === target); + + if (animationName && !targetEffect) { + logger.debug(`取代默认${type === 'enter' ? '入场' : '退场'}动画`, target); + animation = getAnimationObject(animationName, realTarget ?? target, getAnimateDuration(animationName), false); + duration = getAnimateDuration(animationName); + // 用后重置 + if (type === 'enter') { WebGAL.animationManager.nextEnterAnimationName.delete(target); + } else { + WebGAL.animationManager.nextExitAnimationName.delete(target); } - return { duration, animation }; + } + return { duration, animation }; +} + +/** + * 创建默认的入场或退场动画 + * @param type 动画类型,'enter' 或 'exit' + * @param target 目标对象 + * @param frame 应用的动画帧 + * @param duration 动画持续时间 + * @param ease 缓动类型 + */ +// eslint-disable-next-line max-params +export function createDefaultEnterExitAnimation( + type: 'enter' | 'exit', + target: string, + frame: AnimationFrame, + duration: number, + ease: string, +) { + const animationObj = generateTransformAnimationObj(target, frame, duration, ease, type); + const animationName = (Math.random() * 10).toString(16); + const newAnimation: IUserAnimation = { name: animationName, effects: animationObj }; + WebGAL.animationManager.addAnimation(newAnimation); + if (type === 'enter') { + WebGAL.animationManager.nextEnterAnimationName.set(target, animationName); } else { - // exit - let duration = DEFALUT_FIG_OUT_DURATION; - if (isBg) { - duration = 1500; + WebGAL.animationManager.nextExitAnimationName.set(target, animationName); + } +} + +// eslint-disable-next-line max-params +export function createEnterExitAnimation( + sentence: ISentence, + targetKey: string, + defaultDuration: number, + currentTransform: ITransform, +): number { + // 处理 transform 和 默认 transform + const transformString = getStringArgByKey(sentence, 'transform'); + const ease = getStringArgByKey(sentence, 'ease') ?? ''; + let duration = getNumberArgByKey(sentence, 'duration') ?? defaultDuration; + + if (transformString) { + console.log(transformString); + try { + const transform = JSON.parse(transformString.toString()) as ITransform; + const enterFrame = { ...transform, duration: 0, ease: '' }; + const exitFrame = { ...currentTransform, duration: 0, ease: '' }; + createDefaultEnterExitAnimation('enter', targetKey, enterFrame, duration, ease); + createDefaultEnterExitAnimation('exit', targetKey, exitFrame, duration, ease); + } catch (e) { + // 解析都错误了,歇逼吧 + applyDefaultTransform(); } - // 走默认动画 - let animation: IAnimationObject | null = generateUniversalSoftOffAnimationObj(realTarget ?? target, duration); - const animarionName = WebGAL.animationManager.nextExitAnimationName.get(target); - if (animarionName) { - logger.debug('取代默认退出动画', target); - animation = getAnimationObject(animarionName, realTarget ?? target, getAnimateDuration(animarionName), false); - duration = getAnimateDuration(animarionName); - // 用后重置 - WebGAL.animationManager.nextExitAnimationName.delete(target); + } else { + applyDefaultTransform(); + } + + function applyDefaultTransform() { + const enterFrame = { ...baseTransform, duration: 0, ease: '' }; + const exitFrame = { ...currentTransform, duration: 0, ease: '' }; + createDefaultEnterExitAnimation('enter', targetKey, enterFrame, duration, ease); + createDefaultEnterExitAnimation('exit', targetKey, exitFrame, duration, ease); + } + + const enterAnimation = getStringArgByKey(sentence, 'enter'); + const exitAnimation = getStringArgByKey(sentence, 'exit'); + if (enterAnimation) { + WebGAL.animationManager.nextEnterAnimationName.set(targetKey, enterAnimation); + duration = getAnimateDuration(enterAnimation); + } + if (exitAnimation) { + WebGAL.animationManager.nextExitAnimationName.set(targetKey, exitAnimation); + duration = getAnimateDuration(exitAnimation); + } + + return duration; +} + +export function getOldTargetSuffix(): string { + return '-old'; +} + +export function getOldTargetKey(targetKey: string): string { + const dateString = String(new Date().getTime()); + return targetKey + dateString + getOldTargetSuffix(); +} + +export function getEnterAnimationKey(targetKey: string): string { + return targetKey + '-enter'; +} + +export function getExitAnimationKey(targetKey: string): string { + return targetKey + '-exit'; +} + +/** + * 移除指定的场景对象及其动画 + * @param targetKey 目标对象的 key + * @param currentEffects 当前场景效果列表 + */ +export function removeStageObjectWithAnimationByKey(targetKey: string, currentEffects: IEffect[]) { + // 移除入场动画 + const enterAnimationKey = getEnterAnimationKey(targetKey); + WebGAL.gameplay.pixiStage?.removeAnimation(enterAnimationKey, false); + // 快进,跳过退出动画 + if (WebGAL.gameplay.isFast) { + logger.debug('快速模式,立刻关闭立绘'); + WebGAL.gameplay.pixiStage?.removeStageObjectByKey(targetKey); + return; + } + const oldTarget = WebGAL.gameplay.pixiStage?.getStageObjByKey(targetKey); + if (oldTarget) { + // 修改旧目标的 key, 以避免和新目标冲突 + const oldTargetKey = getOldTargetKey(targetKey); + oldTarget.key = oldTargetKey; + // 注册退场动画 + const exitAnimationKey = getExitAnimationKey(targetKey); + const { duration, animation } = getEnterExitAnimation(targetKey, 'exit', oldTargetKey); + WebGAL.gameplay.pixiStage?.registerPresetAnimation(animation, exitAnimationKey, oldTargetKey, currentEffects); + // 在动画结束后移除对象及其动画 + setTimeout(() => { + WebGAL.gameplay.pixiStage?.removeAnimation(exitAnimationKey, false, false); + WebGAL.gameplay.pixiStage?.removeStageObjectByKey(oldTargetKey); + }, duration); + } +} + +/*** + * 添加或移除场景对象 + * @param targetKey 目标对象的 key + * @param newUrl 新的资源地址 + * @param currentEffects 当前场景效果列表 + * @param addFunction 添加函数 + */ +// eslint-disable-next-line max-params +export function addOrRemoveStageObject( + targetKey: string, + newUrl: string, + currentEffects: IEffect[], + addFunction: Function, +) { + if (newUrl !== '') { + // 如果对象存在且地址不同,移除旧对象, 并添加新对象 + const currentStageObject = WebGAL.gameplay.pixiStage?.getStageObjByKey(targetKey); + if (currentStageObject) { + if (currentStageObject.sourceUrl !== newUrl) { + logger.debug(`移除目标 ${targetKey}: ${currentStageObject.sourceUrl}`); + removeStageObjectWithAnimationByKey(targetKey, currentEffects); + // 添加新对象 + logger.debug(`新增目标 ${targetKey}: ${newUrl}`); + addFunction(); + // 注册入场动画 + const enterAnimationKey = getEnterAnimationKey(targetKey); + const { duration, animation } = getEnterExitAnimation(targetKey, 'enter'); + WebGAL.gameplay.pixiStage!.registerPresetAnimation(animation, enterAnimationKey, targetKey, currentEffects); + } + } else { + // 如果对象不存在,添加新对象 + logger.debug(`新增目标 ${targetKey}: ${newUrl}`); + addFunction(); + // 注册入场动画 + const enterAnimationKey = getEnterAnimationKey(targetKey); + const { duration, animation } = getEnterExitAnimation(targetKey, 'enter'); + WebGAL.gameplay.pixiStage!.registerPresetAnimation(animation, enterAnimationKey, targetKey, currentEffects); } - return { duration, animation }; + } else { + // 如果新地址为空,移除对象 + logger.debug(`移除目标 ${targetKey}`); + removeStageObjectWithAnimationByKey(targetKey, currentEffects); } } diff --git a/packages/webgal/src/Core/constants.ts b/packages/webgal/src/Core/constants.ts index eea6cef48..2865b8cbc 100644 --- a/packages/webgal/src/Core/constants.ts +++ b/packages/webgal/src/Core/constants.ts @@ -1,11 +1,11 @@ export const STAGE_KEYS = { STAGE_MAIN: 'stage-main', - BGMAIN: 'bg-main', - FIG_C: 'fig-center', - FIG_L: 'fig-left', - FIG_R: 'fig-right', + BG_MAIN: 'bg-main', + FIG_CENTER: 'fig-center', + FIG_LEFT: 'fig-left', + FIG_RIGHT: 'fig-right', }; export const WEBGAL_NONE = 'none'; -export const DEFALUT_FIG_IN_DURATION = 300; -export const DEFALUT_FIG_OUT_DURATION = 450; +export const DEFAULT_STAGE_OBJECT_ENTER_DURATION = 300; +export const DEFAULT_STAGE_OBJECT_EXIT_DURATION = 450; diff --git a/packages/webgal/src/Core/controller/stage/pixi/PixiController.ts b/packages/webgal/src/Core/controller/stage/pixi/PixiController.ts index 2cedfdbb8..2cb9de11e 100644 --- a/packages/webgal/src/Core/controller/stage/pixi/PixiController.ts +++ b/packages/webgal/src/Core/controller/stage/pixi/PixiController.ts @@ -240,7 +240,13 @@ export default class PixiStage { } return; } - this.stageAnimations.push({ uuid: uuid(), animationObject, key: key, targetKey: target, type: 'preset' }); + this.stageAnimations.push({ + uuid: uuid(), + animationObject, + key: key, + targetKey: target, + type: 'preset', + }); // 上锁 this.lockStageObject(target); animationObject.setStartState(); @@ -258,51 +264,22 @@ export default class PixiStage { * 移除动画 * @param key */ - public removeAnimationByIndex(index: number) { - if (index >= 0) { + public removeAnimationByIndex(index: number, setEffect = false, setEndState = true) { + if (index >= 0 && index < this.stageAnimations.length) { const thisTickerFunc = this.stageAnimations[index]; this.currentApp?.ticker.remove(thisTickerFunc.animationObject.tickerFunc); - thisTickerFunc.animationObject.setEndState(); - this.unlockStageObject(thisTickerFunc.targetKey ?? 'default'); - this.stageAnimations.splice(index, 1); - } - } - - public removeAnimationWithoutSetEndState(key: string) { - const index = this.stageAnimations.findIndex((e) => e.key === key); - if (index >= 0) { - const thisTickerFunc = this.stageAnimations[index]; - this.currentApp?.ticker.remove(thisTickerFunc.animationObject.tickerFunc); - if (thisTickerFunc.animationObject.forceStopWithoutSetEndState) { - thisTickerFunc.animationObject.forceStopWithoutSetEndState(); + if (setEndState) { + thisTickerFunc.animationObject.setEndState(); + } else { + if (thisTickerFunc.animationObject.forceStopWithoutSetEndState) { + thisTickerFunc.animationObject.forceStopWithoutSetEndState(); + } } this.unlockStageObject(thisTickerFunc.targetKey ?? 'default'); - this.stageAnimations.splice(index, 1); - } - } - - public removeAllAnimations() { - while (this.stageAnimations.length > 0) { - this.removeAnimationByIndex(0); - } - } - - public removeAnimation(key: string) { - const index = this.stageAnimations.findIndex((e) => e.key === key); - this.removeAnimationByIndex(index); - } - - public removeAnimationWithSetEffects(key: string) { - const index = this.stageAnimations.findIndex((e) => e.key === key); - if (index >= 0) { - const thisTickerFunc = this.stageAnimations[index]; - this.currentApp?.ticker.remove(thisTickerFunc.animationObject.tickerFunc); - thisTickerFunc.animationObject.setEndState(); - const endStateEffect = thisTickerFunc.animationObject.getEndStateEffect?.() ?? {}; - this.unlockStageObject(thisTickerFunc.targetKey ?? 'default'); - if (thisTickerFunc.targetKey) { + if (setEffect && thisTickerFunc.targetKey) { const target = this.getStageObjByKey(thisTickerFunc.targetKey); if (target) { + const endStateEffect = thisTickerFunc.animationObject.getEndStateEffect?.() ?? {}; let effect: IEffect = { target: thisTickerFunc.targetKey, transform: endStateEffect, @@ -315,6 +292,17 @@ export default class PixiStage { } } + public removeAllAnimations() { + while (this.stageAnimations.length > 0) { + this.removeAnimationByIndex(0); + } + } + + public removeAnimation(key: string, setEffect = false, setEndState = true) { + const index = this.stageAnimations.findIndex((e) => e.key === key); + this.removeAnimationByIndex(index, setEffect, setEndState); + } + // eslint-disable-next-line max-params public performMouthSyncAnimation( key: string, @@ -936,39 +924,45 @@ export default class PixiStage { return [...this.figureObjects, ...this.backgroundObjects, this.mainStageObject]; } - /** - * 根据 key 删除舞台上的对象 - * @param key - */ - public removeStageObjectByKey(key: string) { - const indexFig = this.figureObjects.findIndex((e) => e.key === key); - const indexBg = this.backgroundObjects.findIndex((e) => e.key === key); - if (indexFig >= 0) { - const bgSprite = this.figureObjects[indexFig]; - if (bgSprite.pixiContainer) - for (const element of bgSprite.pixiContainer.children) { + public removeFigureByIndex(index: number) { + if (index >= 0 && index < this.figureObjects.length) { + const stageObject = this.figureObjects[index]; + if (stageObject.pixiContainer) { + for (const element of stageObject.pixiContainer.children) { element.destroy(); } - if (bgSprite.pixiContainer) { - bgSprite.pixiContainer.destroy(); - this.figureContainer.removeChild(bgSprite.pixiContainer); + stageObject.pixiContainer.destroy(); + this.figureContainer.removeChild(stageObject.pixiContainer); } - bgSprite.pixiContainer = null; - this.figureObjects.splice(indexFig, 1); + stageObject.pixiContainer = null; + this.figureObjects.splice(index, 1); } - if (indexBg >= 0) { - const bgSprite = this.backgroundObjects[indexBg]; - if (bgSprite.pixiContainer) - for (const element of bgSprite.pixiContainer.children) { + } + + public removeBackgroundByIndex(index: number) { + if (index >= 0 && index < this.backgroundObjects.length) { + const stageObject = this.backgroundObjects[index]; + if (stageObject.pixiContainer) { + for (const element of stageObject.pixiContainer.children) { element.destroy(); } - if (bgSprite.pixiContainer) { - bgSprite.pixiContainer.destroy(); - this.backgroundContainer.removeChild(bgSprite.pixiContainer); + stageObject.pixiContainer.destroy(); + this.backgroundContainer.removeChild(stageObject.pixiContainer); } - bgSprite.pixiContainer = null; - this.backgroundObjects.splice(indexBg, 1); + stageObject.pixiContainer = null; + this.backgroundObjects.splice(index, 1); } + } + + /** + * 根据 key 删除舞台上的对象 + * @param key + */ + public removeStageObjectByKey(key: string) { + const indexFig = this.figureObjects.findIndex((e) => e.key === key); + const indexBg = this.backgroundObjects.findIndex((e) => e.key === key); + this.removeFigureByIndex(indexFig); + this.removeBackgroundByIndex(indexBg); // /** // * 删掉相关 Effects,因为已经移除了 // */ 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..59ba1c7fb 100644 --- a/packages/webgal/src/Core/controller/stage/pixi/animations/generateTransformAnimationObj.ts +++ b/packages/webgal/src/Core/controller/stage/pixi/animations/generateTransformAnimationObj.ts @@ -8,29 +8,48 @@ type AnimationObj = Array; export function generateTransformAnimationObj( target: string, applyFrame: AnimationFrame, - duration: number | string | boolean | null, + duration: number, ease: string, + type: 'enter' | 'exit' | 'normal', ): AnimationObj { - let animationObj; + let animationObj: AnimationFrame[] = []; // 获取那个 target 的当前变换 const transformState = webgalStore.getState().stage.effects; const targetEffect = transformState.find((effect) => effect.target === target); - - applyFrame.duration = 500; - if (!isNull(duration) && typeof duration === 'number') { - applyFrame.duration = duration; + // 如果找不到 targetEffect, 并且是 normal 类型, 则回退为 enter 类型 + // eslint-disable-next-line no-param-reassign + if (isNull(targetEffect) && type === 'normal') { + type = 'enter'; } - applyFrame.ease = ease; - animationObj = [applyFrame]; - // 找到 effect - if (targetEffect) { - const effectWithDuration = { ...targetEffect!.transform!, duration: 0, ease }; - animationObj.unshift(effectWithDuration); - } else { - // 应用默认effect,也就是最终的 effect 的 alpha = 0 版本 - const effectWithDuration = { ...applyFrame, alpha: 0, duration: 0, ease }; - animationObj.unshift(effectWithDuration); + switch (type) { + case 'normal': { + // 找到存在的 effect 时, 将该 effect 作为起始状态 + const effectWithDuration = { ...targetEffect!.transform!, duration: 0, ease }; + let newFrame = { ...applyFrame, duration, ease }; + animationObj.push(effectWithDuration); + animationObj.push(newFrame); + break; + } + case 'enter': { + // 在最开头加上 applyFrame 的 alpha 0 版本, 实现透明度淡入动画 + const effectWithDuration = { ...applyFrame, alpha: 0, duration: 0, ease }; + let newFrame = { ...applyFrame, duration, ease }; + animationObj.push(effectWithDuration); + animationObj.push(newFrame); + break; + } + case 'exit': { + // 在最末尾加上 applyFrame 的 alpha 0 版本, 实现透明度淡出动画 + // 按理说应该拿 targetEffect 才对, 但是退场动画在调用这个函数前 + // 就已经把 effect 清掉了, 故需要手动传进来 + let newFrame = { ...applyFrame, duration: 0, ease }; + const effectWithDuration = { ...applyFrame, alpha: 0, duration, ease }; + animationObj.push(newFrame); + animationObj.push(effectWithDuration); + break + } } + return animationObj; } diff --git a/packages/webgal/src/Core/controller/stage/pixi/animations/universalSoftIn.ts b/packages/webgal/src/Core/controller/stage/pixi/animations/universalSoftIn.ts index 61f8a3d69..4e27fd013 100644 --- a/packages/webgal/src/Core/controller/stage/pixi/animations/universalSoftIn.ts +++ b/packages/webgal/src/Core/controller/stage/pixi/animations/universalSoftIn.ts @@ -1,6 +1,7 @@ import { WebGAL } from '@/Core/WebGAL'; +import { IAnimationObject } from '../PixiController'; -export function generateUniversalSoftInAnimationObj(targetKey: string, duration: number) { +export function generateUniversalSoftInAnimationObj(targetKey: string, duration: number): IAnimationObject { const target = WebGAL.gameplay.pixiStage!.getStageObjByKey(targetKey); let elapsedTime = 0; diff --git a/packages/webgal/src/Core/controller/stage/pixi/animations/universalSoftOff.ts b/packages/webgal/src/Core/controller/stage/pixi/animations/universalSoftOff.ts index 3f5477457..71d796d2d 100644 --- a/packages/webgal/src/Core/controller/stage/pixi/animations/universalSoftOff.ts +++ b/packages/webgal/src/Core/controller/stage/pixi/animations/universalSoftOff.ts @@ -1,6 +1,7 @@ import { WebGAL } from '@/Core/WebGAL'; +import { IAnimationObject } from '../PixiController'; -export function generateUniversalSoftOffAnimationObj(targetKey: string, duration: number) { +export function generateUniversalSoftOffAnimationObj(targetKey: string, duration: number): IAnimationObject { const target = WebGAL.gameplay.pixiStage!.getStageObjByKey(targetKey); let elapsedTime = 0; diff --git a/packages/webgal/src/Core/gameScripts/changeBg/index.ts b/packages/webgal/src/Core/gameScripts/changeBg/index.ts index abdfad2ef..e28a3a091 100644 --- a/packages/webgal/src/Core/gameScripts/changeBg/index.ts +++ b/packages/webgal/src/Core/gameScripts/changeBg/index.ts @@ -6,13 +6,11 @@ import { webgalStore } from '@/store/store'; import { setStage, stageActions } from '@/store/stageReducer'; import { getNumberArgByKey, getStringArgByKey } from '@/Core/util/getSentenceArg'; import { unlockCgInUserData } from '@/store/userDataReducer'; -import { logger } from '@/Core/util/logger'; -import { ITransform } from '@/store/stageInterface'; -import { generateTransformAnimationObj } from '@/Core/controller/stage/pixi/animations/generateTransformAnimationObj'; -import { AnimationFrame, IUserAnimation } from '@/Core/Modules/animations'; +import { baseTransform } from '@/store/stageInterface'; import cloneDeep from 'lodash/cloneDeep'; -import { getAnimateDuration } from '@/Core/Modules/animationFunctions'; +import { createEnterExitAnimation } from '@/Core/Modules/animationFunctions'; import { WebGAL } from '@/Core/WebGAL'; +import { STAGE_KEYS } from '@/Core/constants'; /** * 进行背景图片的切换 @@ -21,17 +19,23 @@ import { WebGAL } from '@/Core/WebGAL'; */ export const changeBg = (sentence: ISentence): IPerform => { const url = sentence.content; + const key = STAGE_KEYS.BG_MAIN; const unlockName = getStringArgByKey(sentence, 'unlockname') ?? ''; const series = getStringArgByKey(sentence, 'series') ?? 'default'; - const transformString = getStringArgByKey(sentence, 'transform'); let duration = getNumberArgByKey(sentence, 'duration') ?? 1000; - const ease = getStringArgByKey(sentence, 'ease') ?? ''; const dispatch = webgalStore.dispatch; if (unlockName !== '') { dispatch(unlockCgInUserData({ name: unlockName, url, series })); } + // 储存一下现有的 transform 给退场动画当起始帧用, 因为马上就要清除了 + const currentEffect = webgalStore.getState().stage.effects.find((e) => e.target === key); + let currentTransform = baseTransform; + if (currentEffect?.transform) { + currentTransform = cloneDeep(currentEffect.transform); + } + /** * 判断背景 URL 是否发生了变化 */ @@ -41,54 +45,10 @@ export const changeBg = (sentence: ISentence): IPerform => { * 删掉相关 Effects,因为已经移除了 */ if (isUrlChanged) { - dispatch(stageActions.removeEffectByTargetId(`bg-main`)); - } - - // 处理 transform 和 默认 transform - let animationObj: AnimationFrame[]; - if (transformString) { - try { - const frame = JSON.parse(transformString.toString()) as AnimationFrame; - animationObj = generateTransformAnimationObj('bg-main', frame, duration, ease); - // 因为是切换,必须把一开始的 alpha 改为 0 - animationObj[0].alpha = 0; - const animationName = (Math.random() * 10).toString(16); - const newAnimation: IUserAnimation = { name: animationName, effects: animationObj }; - WebGAL.animationManager.addAnimation(newAnimation); - duration = getAnimateDuration(animationName); - WebGAL.animationManager.nextEnterAnimationName.set('bg-main', animationName); - } catch (e) { - // 解析都错误了,歇逼吧 - applyDefaultTransform(); - } - } else { - applyDefaultTransform(); + dispatch(stageActions.removeEffectByTargetId(key)); } - function applyDefaultTransform() { - // 应用默认的 - const frame = {}; - animationObj = generateTransformAnimationObj('bg-main', frame as AnimationFrame, duration, ease); - // 因为是切换,必须把一开始的 alpha 改为 0 - animationObj[0].alpha = 0; - const animationName = (Math.random() * 10).toString(16); - const newAnimation: IUserAnimation = { name: animationName, effects: animationObj }; - WebGAL.animationManager.addAnimation(newAnimation); - duration = getAnimateDuration(animationName); - WebGAL.animationManager.nextEnterAnimationName.set('bg-main', animationName); - } - - // 应用动画的优先级更高一点 - const enterAnimation = getStringArgByKey(sentence, 'enter'); - const exitAnimation = getStringArgByKey(sentence, 'exit'); - if (enterAnimation) { - WebGAL.animationManager.nextEnterAnimationName.set('bg-main', enterAnimation); - duration = getAnimateDuration(enterAnimation); - } - if (exitAnimation) { - WebGAL.animationManager.nextExitAnimationName.set('bg-main-off', exitAnimation); - duration = getAnimateDuration(exitAnimation); - } + duration = createEnterExitAnimation(sentence, key, duration, currentTransform); /** * 背景状态后处理 @@ -104,11 +64,11 @@ export const changeBg = (sentence: ISentence): IPerform => { dispatch(setStage({ key: 'bgName', value: sentence.content })); return { - performName: `bg-main-${sentence.content}`, + performName: `${key}-${sentence.content}`, duration, isHoldOn: false, stopFunction: () => { - WebGAL.gameplay.pixiStage?.stopPresetAnimationOnTarget('bg-main'); + WebGAL.gameplay.pixiStage?.stopPresetAnimationOnTarget(key); }, blockingNext: () => false, blockingAuto: () => true, diff --git a/packages/webgal/src/Core/gameScripts/changeFigure.ts b/packages/webgal/src/Core/gameScripts/changeFigure.ts index 246ab520f..36c778811 100644 --- a/packages/webgal/src/Core/gameScripts/changeFigure.ts +++ b/packages/webgal/src/Core/gameScripts/changeFigure.ts @@ -4,15 +4,14 @@ import { webgalStore } from '@/store/store'; import { setStage, stageActions } from '@/store/stageReducer'; import cloneDeep from 'lodash/cloneDeep'; import { getBooleanArgByKey, getNumberArgByKey, getStringArgByKey } from '@/Core/util/getSentenceArg'; -import { IFreeFigure, IStageState, ITransform } from '@/store/stageInterface'; -import { AnimationFrame, IUserAnimation } from '@/Core/Modules/animations'; -import { generateTransformAnimationObj } from '@/Core/controller/stage/pixi/animations/generateTransformAnimationObj'; +import { baseTransform, IFreeFigure, IStageState } from '@/store/stageInterface'; import { assetSetter, fileType } from '@/Core/util/gameAssetsAccess/assetSetter'; -import { logger } from '@/Core/util/logger'; -import { getAnimateDuration } from '@/Core/Modules/animationFunctions'; +import { createEnterExitAnimation } from '@/Core/Modules/animationFunctions'; import { WebGAL } from '@/Core/WebGAL'; +import { STAGE_KEYS } from '../constants'; import { baseBlinkParam, baseFocusParam, BlinkParam, FocusParam } from '@/Core/live2DCore'; -import { DEFALUT_FIG_IN_DURATION, WEBGAL_NONE } from '../constants'; +import { DEFAULT_STAGE_OBJECT_ENTER_DURATION, WEBGAL_NONE } from '../constants'; +import { logger } from '../util/logger'; /** * 更改立绘 * @param sentence 语句 @@ -46,9 +45,17 @@ export function changeFigure(sentence: ISentence): IPerform { } // id 与 自由立绘 - let key = getStringArgByKey(sentence, 'id') ?? ''; - const isFreeFigure = key ? true : false; - const id = key ? key : `fig-${pos}`; + const idFromArgs = getStringArgByKey(sentence, 'id') ?? ''; + const isFreeFigure = idFromArgs ? true : false; + let key = idFromArgs; + if (!isFreeFigure) { + const positionMap = { + center: STAGE_KEYS.FIG_CENTER, + left: STAGE_KEYS.FIG_LEFT, + right: STAGE_KEYS.FIG_RIGHT, + }; + key = positionMap[pos]; + } // live2d 或 spine 相关 let motion = getStringArgByKey(sentence, 'motion') ?? ''; @@ -85,19 +92,15 @@ export function changeFigure(sentence: ISentence): IPerform { const animationFlag = getStringArgByKey(sentence, 'animationFlag') ?? ''; // 其他参数 - const transformString = getStringArgByKey(sentence, 'transform'); - const ease = getStringArgByKey(sentence, 'ease') ?? ''; - let duration = getNumberArgByKey(sentence, 'duration') ?? DEFALUT_FIG_IN_DURATION; - const enterAnimation = getStringArgByKey(sentence, 'enter'); - const exitAnimation = getStringArgByKey(sentence, 'exit'); + let duration = getNumberArgByKey(sentence, 'duration') ?? DEFAULT_STAGE_OBJECT_ENTER_DURATION; let zIndex = getNumberArgByKey(sentence, 'zIndex') ?? -1; const dispatch = webgalStore.dispatch; const currentFigureAssociatedAnimation = webgalStore.getState().stage.figureAssociatedAnimation; - const filteredFigureAssociatedAnimation = currentFigureAssociatedAnimation.filter((item) => item.targetId !== id); + const filteredFigureAssociatedAnimation = currentFigureAssociatedAnimation.filter((item) => item.targetId !== key); const newFigureAssociatedAnimationItem = { - targetId: id, + targetId: key, animationFlag: animationFlag, mouthAnimation: { open: mouthOpen, @@ -116,7 +119,7 @@ export function changeFigure(sentence: ISentence): IPerform { * 如果 url 没变,不移除 */ let isUrlChanged = true; - if (key !== '') { + if (isFreeFigure) { const figWithKey = webgalStore.getState().stage.freeFigure.find((e) => e.key === key); if (figWithKey) { if (figWithKey.name === sentence.content) { @@ -140,61 +143,26 @@ export function changeFigure(sentence: ISentence): IPerform { } } } + + // 储存一下现有的 transform 给退场动画当起始帧用, 因为马上就要清除了 + const currentEffect = webgalStore.getState().stage.effects.find((e) => e.target === key); + let currentTransform = baseTransform; + if (currentEffect?.transform) { + currentTransform = cloneDeep(currentEffect.transform); + } + /** * 处理 Effects */ if (isUrlChanged) { - webgalStore.dispatch(stageActions.removeEffectByTargetId(id)); - const oldStageObject = WebGAL.gameplay.pixiStage?.getStageObjByKey(id); + webgalStore.dispatch(stageActions.removeEffectByTargetId(key)); + const oldStageObject = WebGAL.gameplay.pixiStage?.getStageObjByKey(key); if (oldStageObject) { oldStageObject.isExiting = true; } } - const setAnimationNames = (key: string, sentence: ISentence) => { - // 处理 transform 和 默认 transform - let animationObj: AnimationFrame[]; - if (transformString) { - console.log(transformString); - try { - const frame = JSON.parse(transformString) as AnimationFrame; - animationObj = generateTransformAnimationObj(key, frame, duration, ease); - // 因为是切换,必须把一开始的 alpha 改为 0 - animationObj[0].alpha = 0; - const animationName = (Math.random() * 10).toString(16); - const newAnimation: IUserAnimation = { name: animationName, effects: animationObj }; - WebGAL.animationManager.addAnimation(newAnimation); - duration = getAnimateDuration(animationName); - WebGAL.animationManager.nextEnterAnimationName.set(key, animationName); - } catch (e) { - // 解析都错误了,歇逼吧 - applyDefaultTransform(); - } - } else { - applyDefaultTransform(); - } - - function applyDefaultTransform() { - // 应用默认的 - const frame = {}; - animationObj = generateTransformAnimationObj(key, frame as AnimationFrame, duration, ease); - // 因为是切换,必须把一开始的 alpha 改为 0 - animationObj[0].alpha = 0; - const animationName = (Math.random() * 10).toString(16); - const newAnimation: IUserAnimation = { name: animationName, effects: animationObj }; - WebGAL.animationManager.addAnimation(newAnimation); - duration = getAnimateDuration(animationName); - WebGAL.animationManager.nextEnterAnimationName.set(key, animationName); - } - if (enterAnimation) { - WebGAL.animationManager.nextEnterAnimationName.set(key, enterAnimation); - duration = getAnimateDuration(enterAnimation); - } - if (exitAnimation) { - WebGAL.animationManager.nextExitAnimationName.set(key + '-off', exitAnimation); - duration = getAnimateDuration(exitAnimation); - } - }; + duration = createEnterExitAnimation(sentence, key, duration, currentTransform); function postFigureStateSet() { if (isUrlChanged) { @@ -237,26 +205,17 @@ export function changeFigure(sentence: ISentence): IPerform { * 下面的代码是设置自由立绘的 */ const freeFigureItem: IFreeFigure = { key, name: content, basePosition: pos }; - setAnimationNames(key, sentence); postFigureStateSet(); dispatch(stageActions.setFreeFigureByKey(freeFigureItem)); } else { /** * 下面的代码是设置与位置关联的立绘的 */ - const positionMap = { - center: 'fig-center', - left: 'fig-left', - right: 'fig-right', - }; const dispatchMap: Record = { center: 'figName', left: 'figNameLeft', right: 'figNameRight', }; - - key = positionMap[pos]; - setAnimationNames(key, sentence); postFigureStateSet(); dispatch(setStage({ key: dispatchMap[pos], value: content })); } diff --git a/packages/webgal/src/Core/gameScripts/setAnimation.ts b/packages/webgal/src/Core/gameScripts/setAnimation.ts index 748b4498a..31dfee21a 100644 --- a/packages/webgal/src/Core/gameScripts/setAnimation.ts +++ b/packages/webgal/src/Core/gameScripts/setAnimation.ts @@ -44,7 +44,7 @@ export const setAnimation = (sentence: ISentence): IPerform => { setTimeout(() => { const endDialogKey = webgalStore.getState().stage.currentDialogKey; const isHasNext = startDialogKey !== endDialogKey; - WebGAL.gameplay.pixiStage?.removeAnimationWithSetEffects(key); + WebGAL.gameplay.pixiStage?.removeAnimation(key, true); }, 0); }; diff --git a/packages/webgal/src/Core/gameScripts/setComplexAnimation.ts b/packages/webgal/src/Core/gameScripts/setComplexAnimation.ts index bd0a15210..a2f63d25f 100644 --- a/packages/webgal/src/Core/gameScripts/setComplexAnimation.ts +++ b/packages/webgal/src/Core/gameScripts/setComplexAnimation.ts @@ -29,7 +29,7 @@ export const setComplexAnimation = (sentence: ISentence): IPerform => { stopFunction = () => { const endDialogKey = webgalStore.getState().stage.currentDialogKey; const isHasNext = startDialogKey !== endDialogKey; - WebGAL.gameplay.pixiStage?.removeAnimationWithSetEffects(key); + WebGAL.gameplay.pixiStage?.removeAnimation(key, true); }; } return { diff --git a/packages/webgal/src/Core/gameScripts/setTempAnimation.ts b/packages/webgal/src/Core/gameScripts/setTempAnimation.ts index 8da9635f7..ef0b4b83e 100644 --- a/packages/webgal/src/Core/gameScripts/setTempAnimation.ts +++ b/packages/webgal/src/Core/gameScripts/setTempAnimation.ts @@ -55,7 +55,7 @@ export const setTempAnimation = (sentence: ISentence): IPerform => { setTimeout(() => { const endDialogKey = webgalStore.getState().stage.currentDialogKey; const isHasNext = startDialogKey !== endDialogKey; - WebGAL.gameplay.pixiStage?.removeAnimationWithSetEffects(key); + WebGAL.gameplay.pixiStage?.removeAnimation(key, true); }, 0); }; diff --git a/packages/webgal/src/Core/gameScripts/setTransform.ts b/packages/webgal/src/Core/gameScripts/setTransform.ts index 149287293..e773a80af 100644 --- a/packages/webgal/src/Core/gameScripts/setTransform.ts +++ b/packages/webgal/src/Core/gameScripts/setTransform.ts @@ -34,7 +34,7 @@ export const setTransform = (sentence: ISentence): IPerform => { try { const frame = JSON.parse(animationString) as AnimationFrame; - animationObj = generateTransformAnimationObj(target, frame, duration, ease); + animationObj = generateTransformAnimationObj(target, frame, duration, ease, 'normal'); console.log('animationObj:', animationObj); } catch (e) { // 解析都错误了,歇逼吧 @@ -65,12 +65,12 @@ export const setTransform = (sentence: ISentence): IPerform => { }, 0); const stopFunction = () => { if (keep) { - WebGAL.gameplay.pixiStage?.removeAnimationWithoutSetEndState(key); + WebGAL.gameplay.pixiStage?.removeAnimation(key, false, false); keepAnimationStopped = true; return; } setTimeout(() => { - WebGAL.gameplay.pixiStage?.removeAnimationWithSetEffects(key); + WebGAL.gameplay.pixiStage?.removeAnimation(key, true); }, 0); }; diff --git a/packages/webgal/src/Core/gameScripts/setTransition.ts b/packages/webgal/src/Core/gameScripts/setTransition.ts index 049218227..3ce2fdbe3 100644 --- a/packages/webgal/src/Core/gameScripts/setTransition.ts +++ b/packages/webgal/src/Core/gameScripts/setTransition.ts @@ -19,7 +19,7 @@ export const setTransition = (sentence: ISentence): IPerform => { WebGAL.animationManager.nextEnterAnimationName.set(key, enterAnimation); } if (exitAnimation) { - WebGAL.animationManager.nextExitAnimationName.set(key + '-off', exitAnimation); + WebGAL.animationManager.nextExitAnimationName.set(key, exitAnimation); } return { performName: 'none', diff --git a/packages/webgal/src/Stage/MainStage/useSetBg.ts b/packages/webgal/src/Stage/MainStage/useSetBg.ts index 26b888646..d6c8a07d5 100644 --- a/packages/webgal/src/Stage/MainStage/useSetBg.ts +++ b/packages/webgal/src/Stage/MainStage/useSetBg.ts @@ -4,8 +4,9 @@ import { logger } from '@/Core/util/logger'; import { IStageObject } from '@/Core/controller/stage/pixi/PixiController'; import { setEbg } from '@/Core/gameScripts/changeBg/setEbg'; -import { getEnterExitAnimation } from '@/Core/Modules/animationFunctions'; +import { addOrRemoveStageObject } from '@/Core/Modules/animationFunctions'; import { WebGAL } from '@/Core/WebGAL'; +import { STAGE_KEYS } from '@/Core/constants'; export function useSetBg(stageState: IStageState) { const bgName = stageState.bgName; @@ -14,44 +15,14 @@ export function useSetBg(stageState: IStageState) { * 设置背景 */ useEffect(() => { - const thisBgKey = 'bg-main'; - if (bgName !== '') { - const currentBg = WebGAL.gameplay.pixiStage?.getStageObjByKey(thisBgKey); - if (currentBg) { - if (currentBg.sourceUrl !== bgName) { - removeBg(currentBg); - } - } + const thisBgKey = STAGE_KEYS.BG_MAIN; + addOrRemoveStageObject(thisBgKey, bgName, stageState.effects, () => { addBg(undefined, thisBgKey, bgName); setEbg(bgName); - logger.debug('重设背景'); - const { duration, animation } = getEnterExitAnimation('bg-main', 'enter', true); - WebGAL.gameplay.pixiStage!.registerPresetAnimation(animation, 'bg-main-softin', thisBgKey, stageState.effects); - setTimeout(() => WebGAL.gameplay.pixiStage!.removeAnimationWithSetEffects('bg-main-softin'), duration); - } else { - const currentBg = WebGAL.gameplay.pixiStage?.getStageObjByKey(thisBgKey); - if (currentBg) { - removeBg(currentBg); - } - } + }); }, [bgName]); } -function removeBg(bgObject: IStageObject) { - WebGAL.gameplay.pixiStage?.removeAnimationWithSetEffects('bg-main-softin'); - const oldBgKey = bgObject.key; - bgObject.key = 'bg-main-off' + String(new Date().getTime()); - const bgKey = bgObject.key; - const bgAniKey = bgObject.key + '-softoff'; - WebGAL.gameplay.pixiStage?.removeStageObjectByKey(oldBgKey); - const { duration, animation } = getEnterExitAnimation('bg-main-off', 'exit', true, bgKey); - WebGAL.gameplay.pixiStage!.registerAnimation(animation, bgAniKey, bgKey); - setTimeout(() => { - WebGAL.gameplay.pixiStage?.removeAnimation(bgAniKey); - WebGAL.gameplay.pixiStage?.removeStageObjectByKey(bgKey); - }, duration); -} - function addBg(type?: 'image' | 'spine', ...args: any[]) { const url: string = args[1]; if (['mp4', 'webm', 'mkv'].some((e) => url.toLocaleLowerCase().endsWith(e))) { diff --git a/packages/webgal/src/Stage/MainStage/useSetFigure.ts b/packages/webgal/src/Stage/MainStage/useSetFigure.ts index 951bee319..37df0bb2f 100644 --- a/packages/webgal/src/Stage/MainStage/useSetFigure.ts +++ b/packages/webgal/src/Stage/MainStage/useSetFigure.ts @@ -5,8 +5,13 @@ import { generateUniversalSoftInAnimationObj } from '@/Core/controller/stage/pix import { IStageObject } from '@/Core/controller/stage/pixi/PixiController'; import { generateUniversalSoftOffAnimationObj } from '@/Core/controller/stage/pixi/animations/universalSoftOff'; -import { getEnterExitAnimation } from '@/Core/Modules/animationFunctions'; +import { + addOrRemoveStageObject, + getOldTargetSuffix, + removeStageObjectWithAnimationByKey, +} from '@/Core/Modules/animationFunctions'; import { WebGAL } from '@/Core/WebGAL'; +import { STAGE_KEYS } from '@/Core/constants'; export function useSetFigure(stageState: IStageState) { const { @@ -76,86 +81,30 @@ export function useSetFigure(stageState: IStageState) { /** * 特殊处理:中间立绘 */ - const thisFigKey = 'fig-center'; - const softInAniKey = 'fig-center-softin'; - if (figName !== '') { - const currentFigCenter = WebGAL.gameplay.pixiStage?.getStageObjByKey(thisFigKey); - if (currentFigCenter) { - if (currentFigCenter.sourceUrl !== figName) { - removeFig(currentFigCenter, softInAniKey, stageState.effects); - } - } + const thisFigKey = STAGE_KEYS.FIG_CENTER; + addOrRemoveStageObject(thisFigKey, figName, stageState.effects, () => { addFigure(undefined, thisFigKey, figName, 'center'); - logger.debug('中立绘已重设'); - const { duration, animation } = getEnterExitAnimation(thisFigKey, 'enter'); - WebGAL.gameplay.pixiStage!.registerPresetAnimation(animation, softInAniKey, thisFigKey, stageState.effects); - setTimeout(() => WebGAL.gameplay.pixiStage!.removeAnimationWithSetEffects(softInAniKey), duration); - } else { - logger.debug('移除中立绘'); - const currentFigCenter = WebGAL.gameplay.pixiStage?.getStageObjByKey(thisFigKey); - if (currentFigCenter) { - if (currentFigCenter.sourceUrl !== figName) { - removeFig(currentFigCenter, softInAniKey, stageState.effects); - } - } - } + }); }, [figName]); useEffect(() => { /** * 特殊处理:左侧立绘 */ - const thisFigKey = 'fig-left'; - const softInAniKey = 'fig-left-softin'; - if (figNameLeft !== '') { - const currentFigLeft = WebGAL.gameplay.pixiStage?.getStageObjByKey(thisFigKey); - if (currentFigLeft) { - if (currentFigLeft.sourceUrl !== figNameLeft) { - removeFig(currentFigLeft, softInAniKey, stageState.effects); - } - } + const thisFigKey = STAGE_KEYS.FIG_LEFT; + addOrRemoveStageObject(thisFigKey, figNameLeft, stageState.effects, () => { addFigure(undefined, thisFigKey, figNameLeft, 'left'); - logger.debug('左立绘已重设'); - const { duration, animation } = getEnterExitAnimation(thisFigKey, 'enter'); - WebGAL.gameplay.pixiStage!.registerPresetAnimation(animation, softInAniKey, thisFigKey, stageState.effects); - setTimeout(() => WebGAL.gameplay.pixiStage!.removeAnimationWithSetEffects(softInAniKey), duration); - } else { - logger.debug('移除左立绘'); - const currentFigLeft = WebGAL.gameplay.pixiStage?.getStageObjByKey(thisFigKey); - if (currentFigLeft) { - if (currentFigLeft.sourceUrl !== figNameLeft) { - removeFig(currentFigLeft, softInAniKey, stageState.effects); - } - } - } + }); }, [figNameLeft]); useEffect(() => { /** * 特殊处理:右侧立绘 */ - const thisFigKey = 'fig-right'; - const softInAniKey = 'fig-right-softin'; - if (figNameRight !== '') { - const currentFigRight = WebGAL.gameplay.pixiStage?.getStageObjByKey(thisFigKey); - if (currentFigRight) { - if (currentFigRight.sourceUrl !== figNameRight) { - removeFig(currentFigRight, softInAniKey, stageState.effects); - } - } + const thisFigKey = STAGE_KEYS.FIG_RIGHT; + addOrRemoveStageObject(thisFigKey, figNameRight, stageState.effects, () => { addFigure(undefined, thisFigKey, figNameRight, 'right'); - logger.debug('右立绘已重设'); - const { duration, animation } = getEnterExitAnimation(thisFigKey, 'enter'); - WebGAL.gameplay.pixiStage!.registerPresetAnimation(animation, softInAniKey, thisFigKey, stageState.effects); - setTimeout(() => WebGAL.gameplay.pixiStage!.removeAnimationWithSetEffects(softInAniKey), duration); - } else { - const currentFigRight = WebGAL.gameplay.pixiStage?.getStageObjByKey(thisFigKey); - if (currentFigRight) { - if (currentFigRight.sourceUrl !== figNameRight) { - removeFig(currentFigRight, softInAniKey, stageState.effects); - } - } - } + }); }, [figNameRight]); useEffect(() => { @@ -165,36 +114,9 @@ export function useSetFigure(stageState: IStageState) { * 特殊处理:自由立绘 */ const thisFigKey = `${fig.key}`; - const softInAniKey = `${fig.key}-softin`; - /** - * 非空 - */ - if (fig.name !== '') { - const currentFigThisKey = WebGAL.gameplay.pixiStage?.getStageObjByKey(thisFigKey); - if (currentFigThisKey) { - if (currentFigThisKey.sourceUrl !== fig.name) { - removeFig(currentFigThisKey, softInAniKey, stageState.effects); - addFigure(undefined, thisFigKey, fig.name, fig.basePosition); - logger.debug(`${fig.key}立绘已重设`); - const { duration, animation } = getEnterExitAnimation(thisFigKey, 'enter'); - WebGAL.gameplay.pixiStage!.registerPresetAnimation(animation, softInAniKey, thisFigKey, stageState.effects); - setTimeout(() => WebGAL.gameplay.pixiStage!.removeAnimationWithSetEffects(softInAniKey), duration); - } - } else { - addFigure(undefined, thisFigKey, fig.name, fig.basePosition); - logger.debug(`${fig.key}立绘已重设`); - const { duration, animation } = getEnterExitAnimation(thisFigKey, 'enter'); - WebGAL.gameplay.pixiStage!.registerPresetAnimation(animation, softInAniKey, thisFigKey, stageState.effects); - setTimeout(() => WebGAL.gameplay.pixiStage!.removeAnimationWithSetEffects(softInAniKey), duration); - } - } else { - const currentFigThisKey = WebGAL.gameplay.pixiStage?.getStageObjByKey(thisFigKey); - if (currentFigThisKey) { - if (currentFigThisKey.sourceUrl !== fig.name) { - removeFig(currentFigThisKey, softInAniKey, stageState.effects); - } - } - } + addOrRemoveStageObject(thisFigKey, fig.name, stageState.effects, () => { + addFigure(undefined, thisFigKey, fig.name, fig.basePosition); + }); } /** @@ -204,18 +126,17 @@ export function useSetFigure(stageState: IStageState) { if (currentFigures) { for (const existFigure of currentFigures) { if ( - existFigure.key === 'fig-left' || - existFigure.key === 'fig-center' || - existFigure.key === 'fig-right' || - existFigure.key.endsWith('-off') + existFigure.key === STAGE_KEYS.FIG_LEFT || + existFigure.key === STAGE_KEYS.FIG_CENTER || + existFigure.key === STAGE_KEYS.FIG_RIGHT || + existFigure.key.endsWith(getOldTargetSuffix()) ) { // 什么也不做 } else { const existKey = existFigure.key; const existFigInState = freeFigure.findIndex((fig) => fig.key === existKey); if (existFigInState < 0) { - const softInAniKey = `${existFigure.key}-softin`; - removeFig(existFigure, softInAniKey, stageState.effects); + removeStageObjectWithAnimationByKey(existKey, stageState.effects); } } } @@ -223,28 +144,6 @@ export function useSetFigure(stageState: IStageState) { }, [freeFigure]); } -function removeFig(figObj: IStageObject, enterTikerKey: string, effects: IEffect[]) { - WebGAL.gameplay.pixiStage?.removeAnimationWithSetEffects(enterTikerKey); - // 快进,跳过退出动画 - if (WebGAL.gameplay.isFast) { - logger.debug('快速模式,立刻关闭立绘'); - WebGAL.gameplay.pixiStage?.removeStageObjectByKey(figObj.key); - return; - } - const oldFigKey = figObj.key; - const figLeaveAniKey = oldFigKey + '-off'; - figObj.key = oldFigKey + String(new Date().getTime()) + '-off'; - const figKey = figObj.key; - WebGAL.gameplay.pixiStage?.removeStageObjectByKey(oldFigKey); - const leaveKey = figKey + '-softoff'; - const { duration, animation } = getEnterExitAnimation(figLeaveAniKey, 'exit', false, figKey); - WebGAL.gameplay.pixiStage!.registerPresetAnimation(animation, leaveKey, figKey, effects); - setTimeout(() => { - WebGAL.gameplay.pixiStage?.removeAnimation(leaveKey); - WebGAL.gameplay.pixiStage?.removeStageObjectByKey(figKey); - }, duration); -} - function addFigure(type?: 'image' | 'live2D' | 'spine', ...args: any[]) { const url = args[1]; const baseUrl = window.location.origin; diff --git a/packages/webgal/src/store/stageReducer.ts b/packages/webgal/src/store/stageReducer.ts index a764af4cf..2ce566034 100644 --- a/packages/webgal/src/store/stageReducer.ts +++ b/packages/webgal/src/store/stageReducer.ts @@ -109,10 +109,10 @@ const stageSlice = createSlice({ // 如果找不到目标,不能设置 transform const activeTargets = [ STAGE_KEYS.STAGE_MAIN, - STAGE_KEYS.BGMAIN, - STAGE_KEYS.FIG_C, - STAGE_KEYS.FIG_L, - STAGE_KEYS.FIG_R, + STAGE_KEYS.BG_MAIN, + STAGE_KEYS.FIG_CENTER, + STAGE_KEYS.FIG_LEFT, + STAGE_KEYS.FIG_RIGHT, ...state.freeFigure.map((figure) => figure.key), ]; if (!activeTargets.includes(target)) return;