Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
135 changes: 80 additions & 55 deletions packages/webgal/src/Core/controller/stage/pixi/PixiController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,15 +85,14 @@ export default class PixiStage {
public readonly mainStageContainer: WebGALPixiContainer;
public readonly foregroundEffectsContainer: PIXI.Container;
public readonly backgroundEffectsContainer: PIXI.Container;
public frameDuration = 16.67;
public notUpdateBacklogEffects = false;
public readonly figureContainer: PIXI.Container;
public figureObjects: Array<IStageObject> = [];
public figureObjects = this.createReactiveList<IStageObject>([]);
public stageWidth = SCREEN_CONSTANTS.width;
public stageHeight = SCREEN_CONSTANTS.height;
public assetLoader = new PIXI.Loader();
public readonly backgroundContainer: PIXI.Container;
public backgroundObjects: Array<IStageObject> = [];
public backgroundObjects = this.createReactiveList<IStageObject>([]);
public mainStageObject: IStageObject;
/**
* 添加 Spine 立绘
Expand All @@ -104,11 +103,15 @@ export default class PixiStage {
public addSpineFigure = addSpineFigureImpl.bind(this);
public addSpineBg = addSpineBgImpl.bind(this);
// 注册到 Ticker 上的函数
private stageAnimations: Array<IStageAnimationObject> = [];
private stageAnimations = this.createReactiveList<IStageAnimationObject>([]);
private loadQueue: { url: string; callback: () => void; name?: string }[] = [];
private live2dFigureRecorder: Array<ILive2DRecord> = [];
// 锁定变换对象(对象可能正在执行动画,不能应用变换)
private lockTransformTarget: Array<string> = [];
// 手动请求渲染防抖标记
private isRenderPending = false;
// 更新 ticker 状态的防抖标记
private isTickerUpdatePending = false;

/**
* 暂时没用上,以后可能用
Expand All @@ -121,6 +124,7 @@ export default class PixiStage {
const app = new PIXI.Application({
backgroundAlpha: 0,
preserveDrawingBuffer: true,
autoStart: false,
});
// @ts-ignore

Expand Down Expand Up @@ -182,19 +186,26 @@ export default class PixiStage {
this.backgroundContainer,
);
this.currentApp = app;
// 每 5s 获取帧率,并且防 loader 死
const update = () => {
this.updateFps();
setTimeout(update, 10000);
};
update();
// loader 防死
const reload = () => {
setTimeout(reload, 500);
this.callLoader();
};
reload();
this.initialize().then(() => {});
this.initialize();
this.requestRender();
}

public requestRender() {
if (this.isRenderPending) return;
this.isRenderPending = true;

requestAnimationFrame(() => {
this.isRenderPending = false;
if (!this.currentApp?.ticker.started) {
this.currentApp?.render();
}
});
}

public getFigureObjects() {
Expand Down Expand Up @@ -346,6 +357,7 @@ export default class PixiStage {
return;
}
sprite.texture = texture;
this.requestRender();
});
}

Expand Down Expand Up @@ -374,6 +386,7 @@ export default class PixiStage {
return;
}
sprite.texture = texture;
this.requestRender();
});
}

Expand Down Expand Up @@ -436,6 +449,7 @@ export default class PixiStage {

// 挂载
thisBgContainer.addChild(bgSprite);
this.requestRender();
}
}, 0);
};
Expand Down Expand Up @@ -610,6 +624,7 @@ export default class PixiStage {
}
thisFigureContainer.pivot.set(0, this.stageHeight / 2);
thisFigureContainer.addChild(figureSprite);
this.requestRender();
}
}, 0);
};
Expand Down Expand Up @@ -1075,13 +1090,6 @@ export default class PixiStage {
}
}

private updateFps() {
getScreenFps?.(120).then((fps) => {
this.frameDuration = 1000 / (fps as number);
// logger.info('当前帧率', fps);
});
}

private lockStageObject(targetName: string) {
this.lockTransformTarget.push(targetName);
}
Expand All @@ -1100,6 +1108,60 @@ export default class PixiStage {
console.error('Failed to load figureCash:', error);
}
}

private createReactiveList<T extends object>(array: T[]): T[] {
return new Proxy(array, {
// eslint-disable-next-line max-params
set: (target, property, value, receiver) => {
const result = Reflect.set(target, property, value, receiver);
this.updateTickerStatus();
return result;
},
deleteProperty: (target, property) => {
const result = Reflect.deleteProperty(target, property);
this.updateTickerStatus();
return result;
},
});
}

private updateTickerStatus() {
if (this.isTickerUpdatePending) return;
this.isTickerUpdatePending = true;

Promise.resolve().then(() => {
this.isTickerUpdatePending = false;
const app = this.currentApp;
if (!app) return;

const hasActiveAnimations = this.stageAnimations.length > 0;
const allObjects = [...this.figureObjects, ...this.backgroundObjects];
const hasDynamicObjects = allObjects.some(
(obj) =>
obj.sourceType === 'live2d' ||
obj.sourceType === 'spine' ||
obj.sourceType === 'video' ||
obj.sourceType === 'gif',
);

const shouldRun = hasActiveAnimations || hasDynamicObjects;

if (shouldRun) {
if (!app.ticker.started) {
app.ticker.start();
logger.debug('Ticker: STARTED');
}
} else {
if (app.ticker.started) {
app.ticker.stop();
this.currentApp?.render();
logger.debug('Ticker: STOPPED');
} else {
this.requestRender();
}
}
});
}
}

function updateCurrentBacklogEffects(newEffects: IEffect[]) {
Expand All @@ -1112,40 +1174,3 @@ function updateCurrentBacklogEffects(newEffects: IEffect[]) {

webgalStore.dispatch(setStage({ key: 'effects', value: newEffects }));
}

/**
* @param {number} targetCount 不小于1的整数,表示经过targetCount帧之后返回结果
* @return {Promise<number>}
*/
const getScreenFps = (() => {
// 先做一下兼容性处理
const nextFrame = [
window.requestAnimationFrame,
// @ts-ignore
window.webkitRequestAnimationFrame,
// @ts-ignore
window.mozRequestAnimationFrame,
].find((fn) => fn);
if (!nextFrame) {
console.error('requestAnimationFrame is not supported!');
return;
}
return (targetCount = 60) => {
// 判断参数是否合规
if (targetCount < 1) throw new Error('targetCount cannot be less than 1.');
const beginDate = Date.now();
let count = 0;
return new Promise((resolve) => {
(function log() {
nextFrame(() => {
if (++count >= targetCount) {
const diffDate = Date.now() - beginDate;
const fps = (count / diffDate) * 1000;
return resolve(fps);
}
log();
});
})();
});
};
})();
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,13 @@ export function generateTemplateAnimationObj(targetKey: string, duration: number
/**
* 在此书写为动画设置初态的操作
*/
function setStartState() {}
function setStartState() { }

// TODO:通用终态设置
/**
* 在此书写为动画设置终态的操作
*/
function setEndState() {}
function setEndState() { }

/**
* 在此书写动画每一帧执行的函数
Expand All @@ -31,7 +31,7 @@ export function generateTemplateAnimationObj(targetKey: string, duration: number
// 要操控的精灵
const sprite = target.pixiContainer;
// 每一帧的时间
const baseDuration = WebGAL.gameplay.pixiStage!.frameDuration;
const currentDeltaMS = WebGAL.gameplay.pixiStage!.currentApp!.ticker.deltaMS;

/**
* 在下面书写具体的动画
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ export function generateTestblurAnimationObj(targetKey: string, duration: number
function tickerFunc(delta: number) {
if (target) {
const container = target.pixiContainer;
const baseDuration = WebGAL.gameplay.pixiStage!.frameDuration;
const currentAddOplityDelta = (duration / baseDuration) * delta;
const currentDeltaMS = WebGAL.gameplay.pixiStage!.currentApp!.ticker.deltaMS;
const currentAddOplityDelta = (duration / currentDeltaMS) * delta;
const increasement = 1 / currentAddOplityDelta;
const decreasement = 5 / currentAddOplityDelta;
if (container)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,9 @@ export function generateUniversalSoftInAnimationObj(targetKey: string, duration:
function tickerFunc(delta: number) {
if (target) {
const sprite = target.pixiContainer;
const baseDuration = WebGAL.gameplay.pixiStage!.frameDuration;
const currentDeltaMS = WebGAL.gameplay.pixiStage!.currentApp!.ticker.deltaMS;

elapsedTime += baseDuration;
elapsedTime += currentDeltaMS;

const realElapsedTime = Math.min(elapsedTime, duration);
const progress = realElapsedTime / duration;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,9 @@ export function generateUniversalSoftOffAnimationObj(targetKey: string, duration
function tickerFunc(delta: number) {
if (target) {
const targetContainer = target.pixiContainer;
const baseDuration = WebGAL.gameplay.pixiStage!.frameDuration;
const currentDeltaMS = WebGAL.gameplay.pixiStage!.currentApp!.ticker.deltaMS;

elapsedTime += baseDuration;
elapsedTime += currentDeltaMS;

const realElapsedTime = Math.min(elapsedTime, duration);
const progress = realElapsedTime / duration;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,4 +77,7 @@ export const jumpFromBacklog = (index: number, refetchScene = true) => {

// 重新显示 TextBox
dispatch(setVisibility({ component: 'showTextBox', visibility: true }));

// 重新渲染
setTimeout(() => WebGAL.gameplay.pixiStage?.requestRender(), 100);
};