import { instantiate, Node, sp, Sprite, SpriteFrame, Texture2D } from "cc"; import { DRESS_PART, DRESS_SOURCE_TYPE, IDressInfo, SPINE_SLOT, SPINE_SOCKET } from "./Types"; import ResManager from "@max-studio/core/res/ResManager"; import { loadDressSocketNode } from "./PetUtils"; import { StringUtils } from "@max-studio/core/utils/StringUtils"; export class BasePart { protected skeleton: sp.Skeleton = null; protected info: IDressInfo = null; protected socketName: string = null; protected part: DRESS_PART = null; protected faceSkeleton: sp.Skeleton = null; protected socketMountNode: Node = null; protected socketCache: Map = new Map(); protected socketNode: Node = null; constructor(ske: sp.Skeleton, part: DRESS_PART) { this.skeleton = ske; this.part = part; this.socketName = SPINE_SOCKET[part]; this.faceSkeleton = ske.node.getChildByName("face")?.getComponent(sp.Skeleton); } public async putOn(info: IDressInfo) { this.takeOff(); this.info = info; if (info.sourceLocationType == DRESS_SOURCE_TYPE.SLOT) { await this.putOnBySlot(info); } else { await this.putOnBySocket(info); } } public takeOff() { if (this.info == null) { return; } const partSkeleton = this.getPartSkeleton(); if (this.info.sourceLocationType == DRESS_SOURCE_TYPE.SLOT) { const slotName = SPINE_SLOT[this.part]; if (StringUtils.isEmpty(slotName) || partSkeleton == null) { console.error("宠物_槽位_空", slotName); return; } const slot = partSkeleton.findSlot(slotName); if (!slot) { console.error("宠物_槽位_不存在", slotName); return; } if (slot.texture) { ResManager.getInstance().releaseAsset(slot.texture); slot.texture = null; } partSkeleton.setSlotTexture(slotName, null, true); } else { if (!this.socketNode) { return; } const socketSprite = this.socketNode.getOrAddComponent(Sprite, "Texture"); if (socketSprite && socketSprite.spriteFrame) { ResManager.getInstance().releaseAsset(socketSprite.spriteFrame); socketSprite.spriteFrame = null; } const spine = this.socketNode.getOrAddComponent(sp.Skeleton, "Spine"); if (spine && spine.skeletonData) { ResManager.getInstance().releaseAsset(spine.skeletonData); spine.skeletonData = undefined; spine.animation = undefined; } } this.info = null; } private getPartSkeleton() { if ( this.part == DRESS_PART.GLASSES || this.part == DRESS_PART.ACCESS || this.info.sourceLocationType == DRESS_SOURCE_TYPE.SOCKET_TEX_FACE || this.info.sourceLocationType == DRESS_SOURCE_TYPE.SOCKET_SPINE_FACE ) { return this.faceSkeleton; } return this.skeleton; } private getPartBonePath(skeleton: sp.Skeleton, name: string) { let path = ""; console.log("获取宠物_骨骼_路径:", name); let bone = skeleton.findBone(name); if (bone) { path = bone.data.name; while (bone.parent) { bone = bone.parent; path = `${bone.data.name}/${path}`; if ("root" == bone.data.name) { break; } } } return path; } private getSocketNode(ske: sp.Skeleton, path: string): sp.SpineSocket { let socket = this.socketCache.get(path); if (socket == null) { const sockets = ske.sockets as sp.SpineSocket[]; socket = sockets.find((s) => s.path == path); if (!socket) { console.error("宠物_挂点_不存在 创建挂点:", path); socket = new sp.SpineSocket(path); sockets.push(socket); ske.sockets = sockets; } this.socketCache.set(path, socket); } return socket; } private async putOnBySlot(info: IDressInfo) { const partSkeleton = this.getPartSkeleton(); const slotName = SPINE_SLOT[this.part]; if (StringUtils.isEmpty(slotName) || partSkeleton == null) { console.error("宠物_槽位_空", slotName); return; } const slot = partSkeleton.findSlot(slotName); if (!slot) { console.error("宠物_槽位_不存在", slotName); return; } if (!info) { if (slot.texture) { ResManager.getInstance().releaseAsset(slot.texture); slot.texture = null; } partSkeleton.setSlotTexture(slotName, null, true); return; } const { err, asset } = await ResManager.getInstance().loadAsset({ bundle: "dress-spine", path: `DressTexture/${info.sourceName}`, type: Texture2D, }); if (err) { console.error("加载宠物_槽位_失败", err); return; } if (this.info != info) { ResManager.getInstance().releaseAsset(asset); return; } if (slot.texture != null) { ResManager.getInstance().releaseAsset(slot.texture); slot.texture = null; } slot.texture = asset; partSkeleton.setSlotTexture(slotName, asset, true); } private async putOnBySocket(info: IDressInfo) { const partSkeleton = this.getPartSkeleton(); const bonePath = this.getPartBonePath(partSkeleton, this.socketName); if (this.socketNode == null) { const socketPrefab = await loadDressSocketNode(); if (socketPrefab == null) { return; } this.socketNode = instantiate(socketPrefab); } if ( info.sourceLocationType == DRESS_SOURCE_TYPE.SOCKET_TEX_FACE || info.sourceLocationType == DRESS_SOURCE_TYPE.SOCKET_TEX ) { const loadInfo = await ResManager.getInstance().loadAsset({ bundle: "dress-spine", path: `DressTexture/${info.sourceName}`, type: SpriteFrame, }); if (loadInfo.err) { console.error("加载宠物_socket_tex失败", loadInfo.err); } else { if (this.info == info) { const socketSprite = this.socketNode.getOrAddComponent(Sprite, "Texture"); socketSprite.spriteFrame = loadInfo.asset as SpriteFrame; } else { console.warn("加载宠物_socket_tex失败,但是不是当前宠物"); ResManager.getInstance().releaseAsset(loadInfo.asset); } } } else if ( info.sourceLocationType == DRESS_SOURCE_TYPE.SOCKET_SPINE || info.sourceLocationType == DRESS_SOURCE_TYPE.SOCKET_SPINE_FACE ) { const loadInfo = await ResManager.getInstance().loadAsset({ bundle: "dress-spine", path: `DressSpine/${info.sourceName}/${info.sourceName}`, type: sp.SkeletonData, }); if (loadInfo.err) { console.error("加载宠物_socket_spine失败", loadInfo.err); } else { if (this.info == info) { const spine = this.socketNode.getOrAddComponent(sp.Skeleton, "Spine"); spine.animation = undefined; spine.skeletonData = loadInfo.asset as sp.SkeletonData; spine.setAnimation(0, "idle", true); } else { console.warn("加载宠物_socket_spine失败,但是不是当前宠物"); ResManager.getInstance().releaseAsset(loadInfo.asset); } } } this.socketNode.setParent(partSkeleton.node); const socket = this.getSocketNode(partSkeleton, bonePath); this.socketNode.layer = partSkeleton.node.layer; socket.target = this.socketNode; const socketSpine = this.socketNode.getOrAddComponent(sp.Skeleton); if (socketSpine && socketSpine.skeletonData) { socketSpine.setAnimation(0, "idle", true); } // eslint-disable-next-line no-self-assign partSkeleton.sockets = partSkeleton.sockets; this.setSocketZIndex(); } protected setSocketZIndex() { if (this.socketNode) { this.socketNode.setSiblingIndex(0); } } }