import * as THREE from 'three' import gsap from 'gsap' import type { Object3DLike } from '@/types/ModelTypes.ts' import Viewport from '@/core/engine/Viewport.ts' import TaskQueue, { type Task } from '@/core/base/TaskQueue.ts' import type { MeshWrap } from '@/core/manager/InstanceMeshManager.ts' import { getRenderer } from '@/core/manager/ModuleManager.ts' /** * 物流实体基类 */ export default abstract class BaseEntity implements EntityIf, Loadable, Carry, Walk, ForkArm, LiftingArm { protected readonly viewport: Viewport protected readonly taskQueue: TaskQueue = new TaskQueue() readonly id: string readonly item: ItemJson readonly object: THREE.Object3D readonly t: string isArmExtended: boolean = false isArmRaised: boolean = false isCarrying: boolean = false isLoading: boolean = false isUnloading: boolean = false isWalking: boolean = false isPaused: boolean = false constructor(viewport: Viewport, id: string) { this.viewport = viewport this.id = id this.item = viewport.find(id) this.object = viewport.entityManager.findObjectById(id) as THREE.Object3D this.t = this.item.t } // 帮助方法:获取机械臂对象 getArmObject(): THREE.Object3D | undefined { console.error('getArmObject 方法未实现') return undefined } // 帮助方法:获取机械臂立柱 getArmPillar(): THREE.Object3D | undefined { console.error('getArmPillar 方法未实现') return undefined } // 待执行任务总数 get taskCount(): number { return this.taskQueue.length } // 是否正在执行任务 get taskIsRunning(): boolean { return this.taskQueue.isExecuting } // 取消所有任务 cancelTask(): void { this.taskQueue.cancelAll() } // 暂停任务 taskPause(): void { this.isPaused = true this.taskQueue.pause() } // 恢复任务 taskResume(): void { this.isPaused = false this.taskQueue.resume() } // 开始执行任务 taskStartRun(): void { this.taskQueue.start() } // 创建基础任务 protected createTask(type: string, execute: () => Promise, cancel?: () => void): T { return { type, execute, cancel, isCancelable: true } as T } // 上 addArmRaise(height: number): void { this.taskQueue.add(this.createTask('ARM_RAISE', async () => { return new Promise(resolve => { const arm = this.getArmObject() if (!arm) return resolve() gsap.to(arm.position, { y: height, duration: 3, ease: 'sine.inOut', onComplete: resolve }) }) }, () => { // 取消动画 gsap.killTweensOf(this.getArmObject()?.position) })) } // 下 addArmLower(): void { this.taskQueue.add(this.createTask('ARM_LOWER', async () => { return new Promise(resolve => { const arm = this.getArmObject() if (!arm) return resolve() gsap.to(arm.position, { y: 0, duration: 3, ease: 'sine.inOut', onComplete: resolve }) }) }, () => { gsap.killTweensOf(this.getArmObject()?.position) })) } // 伸 addArmExtender(z: number = 1.3): void { this.taskQueue.add(this.createTask('ARM_EXTEND', async () => { return new Promise(resolve => { const pillar = this.getArmPillar() if (!pillar) return resolve() gsap.to(pillar.position, { z: -1.2, duration: 3, ease: 'sine.inOut', onComplete: () => { this.isArmExtended = true resolve() } }) }) }, () => { gsap.killTweensOf(this.getArmPillar()?.position) })) } // 缩 addArmRetractor(): void { this.taskQueue.add(this.createTask('ARM_RETRACT', async () => { return new Promise(resolve => { const pillar = this.getArmPillar() if (!pillar) return resolve() gsap.to(pillar.position, { z: 0, duration: 3, ease: 'sine.inOut', onComplete: () => { this.isArmExtended = false resolve() } }) }) }, () => { gsap.killTweensOf(this.getArmPillar()?.position) })) } // 装 addLoad(item: string): void { this.taskQueue.add(this.createTask('LOAD', async () => { // 实际业务中应包含装载逻辑 this.isCarrying = true throw new Error(`装载物品 ${item} 的逻辑未实现`) })) } // 卸 addUnload(item: string, rack: string, bay?: number, level?: number): void { this.taskQueue.add(this.createTask('UNLOAD', async () => { // 实际业务中应包含卸载逻辑 this.isCarrying = false throw new Error(`装载物品 ${item} 的逻辑未实现`) })) } // 转 addRotation(angle: number): void { this.taskQueue.add(this.createTask('ROTATION', async () => { return new Promise(resolve => { const time = Math.abs(angle - THREE.MathUtils.radToDeg(this.object.rotation.y)) / 45 const duration = Math.max(0.5, time) gsap.to(this.object.rotation, { y: THREE.MathUtils.degToRad(angle), duration, ease: 'none', onComplete: resolve }) }) }, () => { gsap.killTweensOf(this.object.rotation) })) } // 走 addTravel(pos: Vector3IF | string): void { this.taskQueue.add(this.createTask('TRAVEL', async () => { return new Promise(resolve => { const fromPos = this.object.position let position: Vector3IF if (pos instanceof String || typeof pos === 'string') { pos = Model.getPositionByEntityId(pos as string) } const toPos = new THREE.Vector3(pos.x, pos.y, pos.z) const distance = fromPos.distanceTo(toPos) const speed = 2 // 2m/s const duration = Math.max(1.5, distance / speed) this.isWalking = true gsap.to(this.object.position, { x: toPos.x, y: toPos.y, z: toPos.z, duration, ease: 'sine.inOut', onComplete: () => { this.isWalking = false resolve() } }) }) }, () => { this.isWalking = false gsap.killTweensOf(this.object.position) })) } /** * 拾取物品 */ pickupItem(id: string): THREE.Object3D { // 找到物品所在的地方, 删除他 const item = this.viewport.entityManager.findItemById(id) const wrap = this.viewport.entityManager.findObjectById(id) as MeshWrap if (wrap.type !== 'MeshWrap') { throw new Error(`无法拾取物品 ${id},它不是一个有效的 MeshWrap`) } item.dt.storeAt = { item: this.id } const mesh = wrap.manager.wrapToObject3D(wrap) this.viewport.entityManager.replaceObject(id, mesh) return mesh } /** * 卸下物品 */ dropItem(item: ItemJson, storeItemId: string, bay?: number, level?: number, cell?: number): void { item.dt.storeAt = { item: storeItemId, bay: bay, level: level, cell: cell } const itemRenderer = getRenderer(item.t) if (!itemRenderer) { throw new Error(`Renderer for type ${item.t} not found`) } this.getArmObject().clear() itemRenderer.tempViewport = this.viewport itemRenderer.createOrUpdatePointForRuntime(item) itemRenderer.tempViewport = null } }