Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] fix(player): spine 4.2 支持, close #271 #275

Merged
merged 12 commits into from
Jan 4, 2025
23 changes: 10 additions & 13 deletions common/config/rush/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 6 additions & 8 deletions lib/ba-story-player/lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import * as utils from "@/utils";
import eventBus from "@/eventBus";
import { initPrivateState, usePlayerStore } from "@/stores";
import { wait, getOtherSoundUrls } from "@/utils";
import { IEventData } from "pixi-spine";
import { EventData } from "@esotericsoftware/spine-pixi-v7";
import {
Application,
Assets,
Expand All @@ -23,20 +23,18 @@ import { disposeUiState, useUiState } from "@/stores/state";
import { PlayerConfigs, StoryUnit } from "@/types/common";
import { watch } from "vue";
import { excelApi } from "@/api";
import { retry, tryit } from "radash";

import { retry } from "radash";
// 注册全局 PIXI
import * as PIXI from "pixi.js";
// 注册全局 gsap pixi 插件
import gsap from "gsap";
import { PixiPlugin } from "gsap/PixiPlugin";

// 注册 howler 中间件
// @ts-ignore
// Howler 中间件
import { HowlerLoader } from "@/middlewares/howlerPixiLoader";

extensions.add(HowlerLoader);

// 注册
// 注册 gsap-pixi 插件
PixiPlugin.registerPIXI(PIXI);
gsap.registerPlugin(PixiPlugin);

Expand Down Expand Up @@ -736,7 +734,7 @@ export const resourcesLoader = {
/**
* 添加l2d语音
*/
loadL2dVoice(audioEvents: IEventData[]) {
loadL2dVoice(audioEvents: EventData[]) {
const audios = audioEvents
.filter(it => {
return (
Expand Down
7 changes: 4 additions & 3 deletions lib/ba-story-player/lib/layers/characterLayer/actionPlayer.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { usePlayerStore } from "@/stores";
import gsap from "gsap";
import type { Spine } from "pixi-spine";
import type { Spine } from "@esotericsoftware/spine-pixi-v7";
import actionOptions from "./options/actionOptions";
import {
CharacterEffectInstance,
Expand All @@ -10,6 +10,7 @@ import {
} from "@/types/characterLayer";
import { ColorOverlayFilter } from "@pixi/filter-color-overlay";
import { calcCharacterYAndScale, getStageSize } from "./index";
import { hasAnimation } from "@/utils";

const AnimationIdleTrack = 0; // 光环动画track index
const AnimationFaceTrack = 1; // 差分切换
Expand Down Expand Up @@ -39,7 +40,7 @@ const CharacterEffectPlayerInstance: CharacterEffectPlayer = {
const { x } = calcSpineStagePosition(characterInstance, instance.position);
characterInstance.x = x;
characterInstance.zIndex = Reflect.get(POS_INDEX_MAP, instance.position);
if (characterInstance.state.hasAnimation("Idle_01")) {
if (hasAnimation(characterInstance, "Idle_01")) {
characterInstance.state.setAnimation(AnimationIdleTrack, "Idle_01", true);
}
characterInstance.alpha = 1;
Expand Down Expand Up @@ -442,7 +443,7 @@ function initCharacter(instance: CharacterEffectInstance) {
characterInstance.x = x;
characterInstance.y = y;
characterInstance.zIndex = Reflect.get(POS_INDEX_MAP, instance.position);
if (characterInstance.state.hasAnimation("Idle_01")) {
if (hasAnimation(characterInstance, "Idle_01")) {
characterInstance.state.setAnimation(AnimationIdleTrack, "Idle_01", true);
}
characterInstance.visible = true;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import eventBus from "@/eventBus";
import { usePlayerStore } from "@/stores";
import gsap, { Power4 } from "gsap";
import { Spine } from "pixi-spine";
import { Spine } from "@esotericsoftware/spine-pixi-v7";
import { Container, Sprite } from "pixi.js";
import emotionOptions from "./options/emotionOptions";
import {
Expand Down Expand Up @@ -327,7 +327,7 @@ const CharacterEmotionPlayerInstance: CharacterEmotionPlayer = {
const scale = getRelativeScale(sprites[0], options);

for (let i = 0; i < 3; ++i) {
const respondImg = spine.newSprite(sprites[0].texture);
const respondImg = new Sprite(sprites[0].texture);
respondImg.angle = options.perImgSetting[i].angle;
respondImg.anchor.set(
options.perImgSetting[i].anchor.x,
Expand Down
44 changes: 22 additions & 22 deletions lib/ba-story-player/lib/layers/characterLayer/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@ import eventBus from "@/eventBus";
import { storyHandler } from "@/index";
import { usePlayerStore } from "@/stores";
import gsap, { Power0 } from "gsap";
import { IAnimationState, ISkeletonData, ITrackEntry, Spine } from "pixi-spine";
import {
AnimationState as IAnimationState,
SkeletonData as ISkeletonData,
TrackEntry as ITrackEntry,
Spine,
} from "@esotericsoftware/spine-pixi-v7";
import {
CharacterEffectInstance,
CharacterEffectPlayerInterface,
Expand Down Expand Up @@ -31,9 +36,7 @@ import CharacterEffectPlayerInstance, {
} from "./actionPlayer";
import CharacterEmotionPlayerInstance from "./emotionPlayer";
import CharacterFXPlayerInstance from "./fxPlayer";
import { Spine as SpinePixi } from "@esotericsoftware/spine-pixi";
import { tryit } from "radash";
import { Assets } from "pixi.js";
import { hasAnimation } from "@/utils";

const AnimationIdleTrack = 0; // 光环动画track index
const AnimationFaceTrack = 1; // 差分切换
Expand Down Expand Up @@ -130,21 +133,14 @@ export const CharacterLayerInstance: CharacterLayer = {
spineData: ISkeletonData,
alias: string
): Spine {
function createSpineFromAlias(alias: string) {
// console.warn("使用@esotericsoftware/spine-pixi加载");
const skelAlias = alias;
const atlasAlias = alias.replace(/\.skel$/, ".atlas");
const instance = SpinePixi.from(skelAlias, atlasAlias);
console.warn("Spine-Pixi:", instance);
return instance as unknown as Spine;
const isSpine42 = spineData.version?.startsWith("4.2");
// const instance = isSpine42
// ? createSpineFromAlias(alias)
// : new Spine(spineData);
const instance = new Spine(spineData);
if (isSpine42) {
console.warn("Spine v4.2 asset:",instance);
}
const isSpine42 = spineData.version.startsWith("4.2");
const instance = isSpine42
? // ? createSpineFromAlias(alias)
new Spine(spineData)
: new Spine(spineData);
if (isSpine42) console.warn(instance);
createSpineFromAlias(alias); // FIXME: 解析出来总是空的
instance.sortableChildren = true;
const id = character.CharacterName;
const { currentCharacterMap } = usePlayerStore();
Expand Down Expand Up @@ -294,8 +290,9 @@ export const CharacterLayerInstance: CharacterLayer = {
Promise.all(mapList.map(character => this.showOneCharacter(character)))
.then(this.characterDone)
.catch(reason => {
// FIXME: 1005302 体香播完表情之后有个错误,不影响播放,待查
if (reason.some((it: unknown) => it)) {
console.log(reason);
console.error(reason);
}
this.characterDone();
});
Expand All @@ -311,7 +308,10 @@ export const CharacterLayerInstance: CharacterLayer = {
}

// 表情
if (data.instance.state.hasAnimation(data.face))
const animationExists = data.instance.skeleton.data.animations.some(
animation => animation.name === data.face
);
if (hasAnimation(data.instance, data.face))
data.instance.state.setAnimation(AnimationFaceTrack, data.face, true);
data.instance.filters = [];

Expand Down Expand Up @@ -383,7 +383,7 @@ export const CharacterLayerInstance: CharacterLayer = {
const { x } = calcSpineStagePosition(chara, data.position);
chara.x = x;
chara.zIndex = Reflect.get(POS_INDEX_MAP, data.position);
if (chara.state.hasAnimation("Idle_01")) {
if (hasAnimation(chara, "Idle_01")) {
chara.state.setAnimation(AnimationIdleTrack, "Idle_01", true);
}
}
Expand Down Expand Up @@ -504,7 +504,7 @@ function getEffectPlayer(type: CharacterEffectType) {
*/
function wink(instance: CharacterInstance, first = true) {
//只在有眨眼动画时起作用
if (!instance.instance.state.hasAnimation("Eye_Close_01")) {
if (!hasAnimation(instance.instance, "Eye_Close_01")) {
return;
}
const face = instance.currentFace;
Expand Down
12 changes: 6 additions & 6 deletions lib/ba-story-player/lib/layers/l2dLayer/L2D.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { usePlayerStore } from "@/stores";
import { getResourcesUrl } from "@/utils";
import { Container } from "pixi.js";
import gsap from "gsap";
import { IEvent, ITrackEntry, Spine } from "pixi-spine";
import { Spine, Event, TrackEntry } from "@esotericsoftware/spine-pixi-v7";
import { IL2dPlayQue, IFilmAspectTransition } from "@/types/l2d";

let disposed = true;
Expand Down Expand Up @@ -54,7 +54,7 @@ export function L2DInit() {
// 接收动画消息
eventBus.on("changeAnimation", e => {
const temAnimation = e.replace(/_(A|M)/, "");
let talkAnimations = mainItem.spineData.animations.filter(i =>
let talkAnimations = mainItem.skeleton.data.animations.filter(i =>
i.name.includes(temAnimation)
);
const devAnimation = talkAnimations.find(i => /dev/i.test(i.name));
Expand Down Expand Up @@ -253,7 +253,7 @@ export function L2DInit() {
}
// 如果 customizeBones 存在,则覆盖 l2dSpineData 的对应骨骼属性
const currName = curL2dConfig?.name;
const { customizeBones } = curL2dConfig?.spineSettings[currName] || {};
const { customizeBones } = curL2dConfig?.spineSettings?.[currName] || {};
if (
!!customizeBones &&
Array.isArray(customizeBones) &&
Expand All @@ -274,7 +274,7 @@ export function L2DInit() {
}
}
mainItem = new Spine(l2dSpineData!);
function playL2dVoice(entry: ITrackEntry, event: IEvent) {
function playL2dVoice(entry: TrackEntry, event: Event) {
const eventName = event.data.name;
if (
// 正常情况下 spine 会 emit 声音事件,但是注意不要让 enableObject, disableObject 等开发事件干扰声音播放
Expand Down Expand Up @@ -310,7 +310,7 @@ export function L2DInit() {
if (curL2dConfig?.otherSpine) {
otherItems = curL2dConfig.otherSpine.map((i, idx) => {
const temItem = new Spine(
characterSpineData(getResourcesUrl("otherL2dSpine", i))
characterSpineData(undefined, getResourcesUrl("otherL2dSpine", i))
);
temItem.name = i;
setSpinePlayInfo({ item: temItem, zIndex: 100 + idx + 1 });
Expand All @@ -319,7 +319,7 @@ export function L2DInit() {
}
setSpinePlayInfo({ item: mainItem, zIndex: 100 });
// 注意!!! 起始动画中最后一个动作是塞入的待机动作
startAnimations = mainItem.spineData.animations
startAnimations = mainItem.skeleton.data.animations
.map(i => i.name)
.filter(i => i.startsWith("Start_Idle"))
.sort()
Expand Down
39 changes: 32 additions & 7 deletions lib/ba-story-player/lib/stores/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
PublicStates,
} from "@/types/store";
import { storyHandler } from "..";
import { Spine } from "@esotericsoftware/spine-pixi-v7";

// let characterNameTable = {
// '유우카 체육복ND': 3715128518,
Expand Down Expand Up @@ -152,6 +153,34 @@ let privateState: PrivateStates = {
bgEffectImgMap: new Map(Object.entries(bgEffectImgTable)),
};

function createSpineFromAlias(alias: string) {
const skelAlias = alias;
const atlasAlias = alias.replace(/\.skel$/, ".atlas");
// console.warn("alias:", skelAlias, atlasAlias);
// console.warn(
// "store:",
// Assets.cache.get(skelAlias),
// Assets.cache.get(atlasAlias)
// );
const instance = Spine.from({
skeleton: skelAlias,
atlas: atlasAlias,
});
// console.warn("Spine-Pixi:", instance);
return instance as unknown as Spine;
}

function getSketDataFromUrl(url: string) {
const cache = Spine.skeletonCache;
const skelAlias = url;
const atlasAlias = url.replace(/\.skel$/, ".atlas");
const key = `${skelAlias}-${atlasAlias}-1`;
if (!(key in cache)) {
createSpineFromAlias(url);
}
return cache[key];
}

const getterFunctions: GetterFunctions = {
app() {
if (privateState === null) {
Expand All @@ -160,10 +189,8 @@ const getterFunctions: GetterFunctions = {
return privateState.app!;
},

characterSpineData: () => (CharacterName: number, url: string) => {
// eslint-disable-next-line max-len
return (Assets.cache.has(String(CharacterName)) ? Assets.get(String(CharacterName)) : (Assets.get(url) || {})).spineData;
},
characterSpineData: () => (CharacterName: number, url: string) =>
getSketDataFromUrl(url),

/**
* 获取情绪动画的图片url, 按从底部到顶部, 从左到右排列资源.
Expand Down Expand Up @@ -198,9 +225,7 @@ const getterFunctions: GetterFunctions = {
},

l2dSpineData() {
return Assets.cache.has(privateState.l2dSpineUrl)
? Assets.get(privateState.l2dSpineUrl)?.spineData
: null;
return getSketDataFromUrl(privateState.l2dSpineUrl);
},
};

Expand Down
6 changes: 5 additions & 1 deletion lib/ba-story-player/lib/types/characterLayer.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import { Sprite } from "pixi.js";
import type { IAnimationStateListener, ISkeletonData, Spine } from "pixi-spine";
import type {
AnimationStateListener as IAnimationStateListener,
SkeletonData as ISkeletonData,
Spine,
} from "@esotericsoftware/spine-pixi-v7";
import { Character, CharacterInstance } from "@/types/common";
import { ShowCharacter } from "@/types/events";

Expand Down
2 changes: 1 addition & 1 deletion lib/ba-story-player/lib/types/common.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { Spine } from "pixi-spine";
import type { Spine } from "@esotericsoftware/spine-pixi-v7";
import {
PlayAudio,
PlayEffect,
Expand Down
Loading
Loading