You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

282 lines
7.3 KiB

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<T extends Task>(type: string, execute: () => Promise<void>, 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
}
}