Skip to content
Merged
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
47 changes: 46 additions & 1 deletion packages/webgal/src/Core/Modules/stage/stageInterface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,49 @@ export const baseTransform: ITransform = {
radiusAlphaFilter: 0,
};

/**
* 立绘的预设位置,left / right 为靠边定位,其余均为中心定位
*/
export const FIGURE_POSITIONS = ['center', 'left', 'right', 'left13', 'right13', 'left14', 'right14'] as const;

export type IFigurePosition = (typeof FIGURE_POSITIONS)[number];

export const FIGURE_KEYS = FIGURE_POSITIONS.map((position) => `fig-${position}`);

export const figureStateKeyByPosition = {
center: 'figName',
left: 'figNameLeft',
right: 'figNameRight',
left13: 'figNameLeft13',
right13: 'figNameRight13',
left14: 'figNameLeft14',
right14: 'figNameRight14',
} as const satisfies Record<IFigurePosition, keyof IStageState>;

/**
* 计算立绘的基准 X 坐标
*/
export function getFigureBaseX(position: IFigurePosition, stageWidth: number, targetWidth: number): number {
switch (position) {
case 'left':
return targetWidth / 2;
case 'right':
return stageWidth - targetWidth / 2;
case 'left13':
return stageWidth / 3;
case 'right13':
return (stageWidth * 2) / 3;
case 'left14':
return stageWidth / 4;
case 'right14':
return (stageWidth * 3) / 4;
default:
return stageWidth / 2;
}
}

export interface IFreeFigure {
basePosition: 'left' | 'center' | 'right';
basePosition: IFigurePosition;
name: string;
key: string;
}
Expand Down Expand Up @@ -169,6 +210,10 @@ export interface IStageState {
figName: string; // 立绘_中 文件地址(相对或绝对)
figNameLeft: string; // 立绘_左 文件地址(相对或绝对)
figNameRight: string; // 立绘_右 文件地址(相对或绝对)
figNameLeft13: string; // 立绘_左 1/3 文件地址(相对或绝对)
figNameRight13: string; // 立绘_右 1/3 文件地址(相对或绝对)
figNameLeft14: string; // 立绘_左 1/4 文件地址(相对或绝对)
figNameRight14: string; // 立绘_右 1/4 文件地址(相对或绝对)
// 自由立绘
freeFigure: Array<IFreeFigure>;
figureAssociatedAnimation: Array<IFigureAssociatedAnimation>;
Expand Down
9 changes: 6 additions & 3 deletions packages/webgal/src/Core/Modules/stage/stageStateManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { STAGE_KEYS } from '@/Core/constants';
import { baseBlinkParam, baseFocusParam } from '@/Core/live2DCore';
import {
baseTransform,
FIGURE_KEYS,
IEffect,
IFigureMetadata,
IFreeFigure,
Expand Down Expand Up @@ -41,6 +42,10 @@ export const initState: IStageState = {
figName: '',
figNameLeft: '',
figNameRight: '',
figNameLeft13: '',
figNameRight13: '',
figNameLeft14: '',
figNameRight14: '',
freeFigure: [],
figureAssociatedAnimation: [],
isRead: false,
Expand Down Expand Up @@ -143,9 +148,7 @@ export class StageStateManager {
const activeTargets = [
STAGE_KEYS.STAGE_MAIN,
STAGE_KEYS.BGMAIN,
STAGE_KEYS.FIG_C,
STAGE_KEYS.FIG_L,
STAGE_KEYS.FIG_R,
...FIGURE_KEYS,
...state.freeFigure.map((figure) => figure.key),
];
if (!activeTargets.includes(target)) return;
Expand Down
3 changes: 0 additions & 3 deletions packages/webgal/src/Core/constants.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
export const STAGE_KEYS = {
STAGE_MAIN: 'stage-main',
BGMAIN: 'bg-main',
FIG_C: 'fig-center',
FIG_L: 'fig-left',
FIG_R: 'fig-right',
};

export const WEBGAL_NONE = 'none';
Expand Down
31 changes: 12 additions & 19 deletions packages/webgal/src/Core/controller/stage/pixi/PixiController.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
import { IEffect, IFigureAssociatedAnimation, IFigureMetadata, ITransform } from '@/Core/Modules/stage/stageInterface';
import {
getFigureBaseX,
IEffect,
IFigureAssociatedAnimation,
IFigureMetadata,
IFigurePosition,
ITransform,
} from '@/Core/Modules/stage/stageInterface';
import { Live2D, WebGAL } from '@/Core/WebGAL';
import { baseBlinkParam, baseFocusParam, BlinkParam, FocusParam } from '@/Core/live2DCore';
import { isIOS } from '@/Core/initializeScript';
Expand Down Expand Up @@ -563,7 +570,7 @@ export default class PixiStage {
* @param url 立绘图片url
* @param presetPosition
*/
public addFigure(key: string, url: string, presetPosition: 'left' | 'center' | 'right' = 'center') {
public addFigure(key: string, url: string, presetPosition: IFigurePosition = 'center') {
const loader = this.assetLoader;
// 准备用于存放这个立绘的 Container
const thisFigureContainer = new WebGALPixiContainer();
Expand Down Expand Up @@ -623,15 +630,7 @@ export default class PixiStage {
if (targetHeight < this.stageHeight) {
thisFigureContainer.setBaseY(this.stageHeight / 2 + (this.stageHeight - targetHeight) / 2);
}
if (presetPosition === 'center') {
thisFigureContainer.setBaseX(this.stageWidth / 2);
}
if (presetPosition === 'left') {
thisFigureContainer.setBaseX(targetWidth / 2);
}
if (presetPosition === 'right') {
thisFigureContainer.setBaseX(this.stageWidth - targetWidth / 2);
}
thisFigureContainer.setBaseX(getFigureBaseX(presetPosition, this.stageWidth, targetWidth));
thisFigureContainer.pivot.set(0, this.stageHeight / 2);
thisFigureContainer.addChild(figureSprite);
this.notifyTargetReferenceBoxChanged(key);
Expand All @@ -657,7 +656,7 @@ export default class PixiStage {
* @param jsonPath
*/
// eslint-disable-next-line max-params
public addLive2dFigure(key: string, jsonPath: string, pos: string) {
public addLive2dFigure(key: string, jsonPath: string, pos: IFigurePosition) {
if (Live2D.isAvailable !== true) return;
try {
let stageWidth = this.stageWidth;
Expand Down Expand Up @@ -741,13 +740,7 @@ export default class PixiStage {
baseY = stageHeight / 2 + (stageHeight - targetHeight) / 2;
}
thisFigureContainer.setBaseY(baseY);
if (pos === 'center') {
thisFigureContainer.setBaseX(stageWidth / 2);
} else if (pos === 'left') {
thisFigureContainer.setBaseX(targetWidth / 2);
} else if (pos === 'right') {
thisFigureContainer.setBaseX(stageWidth - targetWidth / 2);
}
thisFigureContainer.setBaseX(getFigureBaseX(pos, stageWidth, targetWidth));

thisFigureContainer.pivot.set(0, stageHeight / 2);

Expand Down
13 changes: 3 additions & 10 deletions packages/webgal/src/Core/controller/stage/pixi/spine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import * as PIXI from 'pixi.js';
import PixiStage from '@/Core/controller/stage/pixi/PixiController';
import { logger } from '@/Core/util/logger';
import { stageStateManager } from '@/Core/Modules/stage/stageStateManager';
import { getFigureBaseX, IFigurePosition } from '@/Core/Modules/stage/stageInterface';
// utils/loadPixiSpine.ts
// @ts-ignore
let pixiSpineModule: typeof import('pixi-spine') | null = null;
Expand Down Expand Up @@ -56,7 +57,7 @@ export async function addSpineFigureImpl(
this: PixiStage,
key: string,
url: string,
presetPosition: 'left' | 'center' | 'right' = 'center',
presetPosition: IFigurePosition = 'center',
) {
const spineId = `spine-${url}`;
// 准备用于存放这个立绘的 Container
Expand Down Expand Up @@ -161,15 +162,7 @@ export async function addSpineFigureImpl(
if (targetHeight < this.stageHeight) {
thisFigureContainer.setBaseY(this.stageHeight / 2 + (this.stageHeight - targetHeight) / 2);
}
if (presetPosition === 'center') {
thisFigureContainer.setBaseX(this.stageWidth / 2);
}
if (presetPosition === 'left') {
thisFigureContainer.setBaseX(targetWidth / 2);
}
if (presetPosition === 'right') {
thisFigureContainer.setBaseX(this.stageWidth - targetWidth / 2);
}
thisFigureContainer.setBaseX(getFigureBaseX(presetPosition, this.stageWidth, targetWidth));
thisFigureContainer.pivot.set(0, this.stageHeight / 2);
thisFigureContainer.addChild(figureSprite);
this.notifyTargetReferenceBoxChanged(key);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { IEffect, IStageState, ITransform } from '@/Core/Modules/stage/stageInterface';
import type { IEffect, IFigurePosition, IStageState, ITransform } from '@/Core/Modules/stage/stageInterface';
import { FIGURE_KEYS, FIGURE_POSITIONS, figureStateKeyByPosition } from '@/Core/Modules/stage/stageInterface';
import type { IResolvedStageCommitOptions } from '@/Core/Modules/stage/stageStateManager';
import { DEFAULT_BG_OUT_DURATION } from '@/Core/constants';
import { WebGAL } from '@/Core/WebGAL';
Expand All @@ -11,7 +12,7 @@ import { applyTransformToPixiContainer } from '@/Core/controller/stage/pixi/stag
interface ISyncFigureSlotPayload {
key: string;
sourceUrl: string;
position: 'left' | 'center' | 'right';
position: IFigurePosition;
stageState: IStageState;
skipAnimation: boolean;
}
Expand Down Expand Up @@ -92,15 +93,15 @@ function syncBg(stageState: IStageState, skipAnimation: boolean) {
}

function syncFigures(stageState: IStageState, skipAnimation: boolean) {
syncFigureSlot({ key: 'fig-center', sourceUrl: stageState.figName, position: 'center', stageState, skipAnimation });
syncFigureSlot({ key: 'fig-left', sourceUrl: stageState.figNameLeft, position: 'left', stageState, skipAnimation });
syncFigureSlot({
key: 'fig-right',
sourceUrl: stageState.figNameRight,
position: 'right',
stageState,
skipAnimation,
});
for (const position of FIGURE_POSITIONS) {
syncFigureSlot({
key: `fig-${position}`,
sourceUrl: stageState[figureStateKeyByPosition[position]],
position,
stageState,
skipAnimation,
});
}

for (const fig of stageState.freeFigure) {
syncFigureSlot({ key: fig.key, sourceUrl: fig.name, position: fig.basePosition, stageState, skipAnimation });
Expand All @@ -110,12 +111,7 @@ function syncFigures(stageState: IStageState, skipAnimation: boolean) {
if (!currentFigures) return;
const freeFigureKeys = new Set(stageState.freeFigure.map((fig) => fig.key));
for (const existFigure of [...currentFigures]) {
if (
existFigure.key === 'fig-left' ||
existFigure.key === 'fig-center' ||
existFigure.key === 'fig-right' ||
existFigure.key.endsWith('-off')
) {
if (FIGURE_KEYS.includes(existFigure.key) || existFigure.key.endsWith('-off')) {
continue;
}
if (!freeFigureKeys.has(existFigure.key)) {
Expand All @@ -130,7 +126,8 @@ function syncFigureSlot({ key, sourceUrl, position, stageState, skipAnimation }:
const softInAniKey = `${key}-softin`;
const currentFigure = pixiStage.getStageObjByKey(key);

if (sourceUrl !== '') {
// 旧存档中可能没有新增位置的字段,这里同时容错 undefined
if (sourceUrl) {
if (currentFigure?.sourceUrl === sourceUrl) return;
if (currentFigure) {
removeFig(currentFigure, softInAniKey, { effects: stageState.effects, skipAnimation });
Expand Down Expand Up @@ -243,7 +240,7 @@ function addBg(key: string, url: string) {
}
}

function addFigure(key: string, url: string, position: 'left' | 'center' | 'right') {
function addFigure(key: string, url: string, position: IFigurePosition) {
const pixiStage = WebGAL.gameplay.pixiStage;
if (!pixiStage) return;
const baseUrl = window.location.origin;
Expand Down
58 changes: 12 additions & 46 deletions packages/webgal/src/Core/gameScripts/changeFigure.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
import { ISentence } from '@/Core/controller/scene/sceneInterface';
import { IPerform } from '@/Core/Modules/perform/performInterface';
import cloneDeep from 'lodash/cloneDeep';
import { getBooleanArgByKey, getNumberArgByKey, getStringArgByKey } from '@/Core/util/getSentenceArg';
import { IFreeFigure, IStageState, ITransform } from '@/Core/Modules/stage/stageInterface';
import {
getBooleanArgByKey,
getFigurePositionFromArgs,
getNumberArgByKey,
getStringArgByKey,
} from '@/Core/util/getSentenceArg';
import { figureStateKeyByPosition, IFreeFigure } from '@/Core/Modules/stage/stageInterface';
import { AnimationFrame, IUserAnimation } from '@/Core/Modules/animations';
import { generateTransformAnimationObj } from '@/Core/controller/stage/pixi/animations/generateTransformAnimationObj';
import { assetSetter, fileType } from '@/Core/util/gameAssetsAccess/assetSetter';
Expand All @@ -29,21 +34,7 @@ export function changeFigure(sentence: ISentence): IPerform {
}

// 根据参数设置指定位置
let pos: 'center' | 'left' | 'right' = 'center';
let mouthAnimationKey = 'mouthAnimation';
let eyesAnimationKey = 'blinkAnimation';
const leftFromArgs = getBooleanArgByKey(sentence, 'left') ?? false;
const rightFromArgs = getBooleanArgByKey(sentence, 'right') ?? false;
if (leftFromArgs) {
pos = 'left';
mouthAnimationKey = 'mouthAnimationLeft';
eyesAnimationKey = 'blinkAnimationLeft';
}
if (rightFromArgs) {
pos = 'right';
mouthAnimationKey = 'mouthAnimationRight';
eyesAnimationKey = 'blinkAnimationRight';
}
const pos = getFigurePositionFromArgs(sentence) || 'center';

// id 与 自由立绘
let key = getStringArgByKey(sentence, 'id') ?? '';
Expand Down Expand Up @@ -127,22 +118,8 @@ export function changeFigure(sentence: ISentence): IPerform {
isUrlChanged = false;
}
}
} else {
if (pos === 'center') {
if (stageStateManager.getCalculationStageState().figName === sentence.content) {
isUrlChanged = false;
}
}
if (pos === 'left') {
if (stageStateManager.getCalculationStageState().figNameLeft === sentence.content) {
isUrlChanged = false;
}
}
if (pos === 'right') {
if (stageStateManager.getCalculationStageState().figNameRight === sentence.content) {
isUrlChanged = false;
}
}
} else if (stageStateManager.getCalculationStageState()[figureStateKeyByPosition[pos]] === sentence.content) {
isUrlChanged = false;
}
/**
* 处理 Effects
Expand Down Expand Up @@ -265,21 +242,10 @@ export function changeFigure(sentence: ISentence): IPerform {
/**
* 下面的代码是设置与位置关联的立绘的
*/
const positionMap = {
center: 'fig-center',
left: 'fig-left',
right: 'fig-right',
};
const dispatchMap: Record<string, keyof IStageState> = {
center: 'figName',
left: 'figNameLeft',
right: 'figNameRight',
};

key = positionMap[pos];
key = `fig-${pos}`;
setAnimationNames(key, sentence);
postFigureStateSet();
stageStateManager.setStage(dispatchMap[pos], content);
stageStateManager.setStage(figureStateKeyByPosition[pos], content);
}

return {
Expand Down
10 changes: 2 additions & 8 deletions packages/webgal/src/Core/gameScripts/say.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { playVocal } from './vocal';
import { webgalStore } from '@/store/store';
import { useTextAnimationDuration, useTextDelay } from '@/hooks/useTextOptions';
import { getRandomPerformName } from '@/Core/Modules/perform/performController';
import { getBooleanArgByKey, getStringArgByKey } from '@/Core/util/getSentenceArg';
import { getBooleanArgByKey, getFigurePositionFromArgs, getStringArgByKey } from '@/Core/util/getSentenceArg';
import { textSize, voiceOption } from '@/store/userDataInterface';
import { WebGAL } from '@/Core/WebGAL';
import { compileSentence } from '@/Stage/TextBox/TextBox';
Expand Down Expand Up @@ -89,13 +89,7 @@ export const say = (sentence: ISentence): IPerform => {
// 模拟说话
let performSimulateVocalTimeout: ReturnType<typeof setTimeout> | null = null;

let pos: '' | 'center' | 'left' | 'right' = '';
const leftFromArgs = getBooleanArgByKey(sentence, 'left') ?? false;
const rightFromArgs = getBooleanArgByKey(sentence, 'right') ?? false;
const centerFromArgs = getBooleanArgByKey(sentence, 'center') ?? false;
if (leftFromArgs) pos = 'left';
if (rightFromArgs) pos = 'right';
if (centerFromArgs) pos = 'center';
const pos = getFigurePositionFromArgs(sentence);

let key = getStringArgByKey(sentence, 'figureId') ?? '';

Expand Down
Loading
Loading