Skip to content
31 changes: 31 additions & 0 deletions packages/webgal/public/game/scene/demo_parallel_animation.txt
Original file line number Diff line number Diff line change
@@ -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
17 changes: 16 additions & 1 deletion packages/webgal/src/Core/Modules/animationFunctions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -19,12 +21,25 @@ import { stageActions } from '@/store/stageReducer';
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<string>();
const unionScaleKeys = new Set<string>();
const unionPositionKeys = new Set<string>();
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 || {}, (s, 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: '' });
}
Expand Down
45 changes: 33 additions & 12 deletions packages/webgal/src/Core/Modules/perform/performController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,19 @@ export class PerformController {
public performList: Array<IPerform> = [];

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--;
}
}
}
}
Expand All @@ -50,7 +52,7 @@ export class PerformController {
// perform.isOver = true;
if (!perform.isHoldOn) {
// 如果不是保持演出,清除
this.unmountPerform(perform.performName);
this.softUnmountPerformObject(perform);
}
}, perform.duration);

Expand Down Expand Up @@ -103,6 +105,25 @@ 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));
}
Expand Down
2 changes: 2 additions & 0 deletions packages/webgal/src/Core/Modules/perform/performInterface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ export interface IPerform {
arrangePerformPromise?: Promise<IPerform>;
// 跳过由 nextSentence 函数引发的演出回收
skipNextCollect?: boolean;
//
isParallel?: boolean;
}

// next之后,可以被打断的演出会被打断,不能被打断的演出会继续,阻塞next的演出会阻止next被响应。
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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;
if (convertAlpha) {
Expand Down
Original file line number Diff line number Diff line change
@@ -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<AnimationFrame>;
Expand All @@ -10,6 +11,7 @@ export function generateTransformAnimationObj(
applyFrame: AnimationFrame,
duration: number | string | boolean | null,
ease: string,
writeFullEffect = true,
): AnimationObj {
let animationObj;
// 获取那个 target 的当前变换
Expand All @@ -25,8 +27,21 @@ 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 || {}, (sr, 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 };
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,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));
Expand Down Expand Up @@ -71,11 +71,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)) {
Expand All @@ -98,11 +98,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)) {
Expand Down
4 changes: 3 additions & 1 deletion packages/webgal/src/Core/gameScripts/setAnimation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(() => {
Expand Down Expand Up @@ -56,5 +57,6 @@ export const setAnimation = (sentence: ISentence): IPerform => {
blockingNext: () => false,
blockingAuto: () => !keep,
stopTimeout: undefined, // 暂时不用,后面会交给自动清除
isParallel: parallel,
};
};
7 changes: 5 additions & 2 deletions packages/webgal/src/Core/gameScripts/setTempAnimation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,15 @@ import { baseTransform } from '@/store/stageInterface';
import { IUserAnimation } from '../Modules/animations';
import { getAnimateDuration, getAnimationObject } from '@/Core/Modules/animationFunctions';
import { WebGAL } from '@/Core/WebGAL';
import { v4 as uuid } from 'uuid';

/**
* 设置临时动画
* @param sentence
*/
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 {
Expand All @@ -31,11 +32,12 @@ export const setTempAnimation = (sentence: ISentence): IPerform => {
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(() => {
Expand Down Expand Up @@ -67,5 +69,6 @@ export const setTempAnimation = (sentence: ISentence): IPerform => {
blockingNext: () => false,
blockingAuto: () => !keep,
stopTimeout: undefined, // 暂时不用,后面会交给自动清除
isParallel: parallel,
};
};
12 changes: 7 additions & 5 deletions packages/webgal/src/Core/gameScripts/setTransform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,14 @@ 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 { 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[];

Expand All @@ -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) {
// 解析都错误了,歇逼吧
Expand All @@ -44,7 +46,6 @@ export const setTransform = (sentence: ISentence): IPerform => {
const newAnimation: IUserAnimation = { name: animationName, effects: animationObj };
WebGAL.animationManager.addAnimation(newAnimation);
const animationDuration = getAnimateDuration(animationName);

const key = `${target}-${animationName}-${animationDuration}`;
let keepAnimationStopped = false;
setTimeout(() => {
Expand Down Expand Up @@ -82,5 +83,6 @@ export const setTransform = (sentence: ISentence): IPerform => {
blockingNext: () => false,
blockingAuto: () => !keep,
stopTimeout: undefined, // 暂时不用,后面会交给自动清除
isParallel: parallel,
};
};
3 changes: 2 additions & 1 deletion packages/webgal/src/Stage/MainStage/useSetEffects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
}
Loading