mirror of
				https://github.com/unanmed/HumanBreak.git
				synced 2025-10-31 20:32:58 +08:00 
			
		
		
		
	feat: 动画
This commit is contained in:
		
							parent
							
								
									63765ab626
								
							
						
					
					
						commit
						d0ff2f1dc5
					
				| @ -1,5 +1,6 @@ | |||||||
| type AdapterFunction<T> = (item: T, ...params: any[]) => Promise<any>; | type AdapterFunction<T> = (item: T, ...params: any[]) => Promise<any>; | ||||||
| type SyncAdapterFunction<T> = (item: T, ...params: any[]) => any; | type SyncAdapterFunction<T> = (item: T, ...params: any[]) => any; | ||||||
|  | type GlobalAdapterFunction = (...params: any[]) => Promise<any>; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * 渲染适配器,用作渲染层与数据层沟通的桥梁,用于在数据层等待渲染层执行,常用与动画等。 |  * 渲染适配器,用作渲染层与数据层沟通的桥梁,用于在数据层等待渲染层执行,常用与动画等。 | ||||||
| @ -16,6 +17,7 @@ export class RenderAdapter<T> { | |||||||
| 
 | 
 | ||||||
|     private execute: Map<string, AdapterFunction<T>> = new Map(); |     private execute: Map<string, AdapterFunction<T>> = new Map(); | ||||||
|     private syncExecutes: Map<string, SyncAdapterFunction<T>> = new Map(); |     private syncExecutes: Map<string, SyncAdapterFunction<T>> = new Map(); | ||||||
|  |     private globalExecutes: Map<string, GlobalAdapterFunction> = new Map(); | ||||||
| 
 | 
 | ||||||
|     constructor(id: string) { |     constructor(id: string) { | ||||||
|         this.id = id; |         this.id = id; | ||||||
| @ -36,11 +38,20 @@ export class RenderAdapter<T> { | |||||||
|         this.items.delete(item); |         this.items.delete(item); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * 定义一个类似于静态函数的函数,只会调用一次,不会对每个元素执行 | ||||||
|  |      * @param id 函数名称 | ||||||
|  |      * @param fn 执行的函数 | ||||||
|  |      */ | ||||||
|  |     receiveGlobal(id: string, fn: GlobalAdapterFunction) { | ||||||
|  |         this.globalExecutes.set(id, fn); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     /** |     /** | ||||||
|      * 设置执行函数 |      * 设置执行函数 | ||||||
|      * @param fn 对于每个元素执行的函数 |      * @param fn 对于每个元素执行的函数 | ||||||
|      */ |      */ | ||||||
|     recieve(id: string, fn: AdapterFunction<T>): void { |     receive(id: string, fn: AdapterFunction<T>): void { | ||||||
|         this.execute.set(id, fn); |         this.execute.set(id, fn); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -48,7 +59,7 @@ export class RenderAdapter<T> { | |||||||
|      * 设置同步执行函数 |      * 设置同步执行函数 | ||||||
|      * @param fn 对于每个元素执行的函数 |      * @param fn 对于每个元素执行的函数 | ||||||
|      */ |      */ | ||||||
|     recieveSync(id: string, fn: SyncAdapterFunction<T>): void { |     receiveSync(id: string, fn: SyncAdapterFunction<T>): void { | ||||||
|         this.syncExecutes.set(id, fn); |         this.syncExecutes.set(id, fn); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -96,6 +107,18 @@ export class RenderAdapter<T> { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * 调用一个全局函数 | ||||||
|  |      */ | ||||||
|  |     global<R = any>(id: string, ...params: any[]): Promise<R> { | ||||||
|  |         const execute = this.globalExecutes.get(id); | ||||||
|  |         if (!execute) { | ||||||
|  |             return Promise.reject(); | ||||||
|  |         } else { | ||||||
|  |             return execute(...params); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     /** |     /** | ||||||
|      * 销毁这个adapter |      * 销毁这个adapter | ||||||
|      */ |      */ | ||||||
|  | |||||||
| @ -6,6 +6,7 @@ import { LayerGroup, FloorLayer } from './preset/layer'; | |||||||
| import { MotaRenderer } from './render'; | import { MotaRenderer } from './render'; | ||||||
| import { LayerShadowExtends } from '../fx/shadow'; | import { LayerShadowExtends } from '../fx/shadow'; | ||||||
| import { LayerGroupFilter } from '@/plugin/fx/gameCanvas'; | import { LayerGroupFilter } from '@/plugin/fx/gameCanvas'; | ||||||
|  | import { LayerGroupAnimate } from './preset/animate'; | ||||||
| 
 | 
 | ||||||
| let main: MotaRenderer; | let main: MotaRenderer; | ||||||
| 
 | 
 | ||||||
| @ -31,12 +32,14 @@ Mota.require('var', 'loading').once('loaded', () => { | |||||||
|     const door = new LayerDoorAnimate(); |     const door = new LayerDoorAnimate(); | ||||||
|     const shadow = new LayerShadowExtends(); |     const shadow = new LayerShadowExtends(); | ||||||
|     const filter = new LayerGroupFilter(); |     const filter = new LayerGroupFilter(); | ||||||
|  |     const animate = new LayerGroupAnimate(); | ||||||
|     layer.extends(damage); |     layer.extends(damage); | ||||||
|     layer.extends(detail); |     layer.extends(detail); | ||||||
|     layer.extends(filter); |     layer.extends(filter); | ||||||
|     layer.getLayer('event')?.extends(hero); |     layer.getLayer('event')?.extends(hero); | ||||||
|     layer.getLayer('event')?.extends(door); |     layer.getLayer('event')?.extends(door); | ||||||
|     layer.getLayer('event')?.extends(shadow); |     layer.getLayer('event')?.extends(shadow); | ||||||
|  |     layer.extends(animate); | ||||||
| 
 | 
 | ||||||
|     render.appendChild(layer); |     render.appendChild(layer); | ||||||
| }); | }); | ||||||
|  | |||||||
							
								
								
									
										262
									
								
								src/core/render/preset/animate.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										262
									
								
								src/core/render/preset/animate.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,262 @@ | |||||||
|  | import { logger } from '@/core/common/logger'; | ||||||
|  | import { RenderAdapter } from '../adapter'; | ||||||
|  | import { Sprite } from '../sprite'; | ||||||
|  | import { HeroRenderer } from './hero'; | ||||||
|  | import { ILayerGroupRenderExtends, LayerGroup } from './layer'; | ||||||
|  | import { MotaOffscreenCanvas2D } from '@/core/fx/canvas2d'; | ||||||
|  | import { transformCanvas } from '../item'; | ||||||
|  | 
 | ||||||
|  | export class LayerGroupAnimate implements ILayerGroupRenderExtends { | ||||||
|  |     static animateList: Set<LayerGroupAnimate> = new Set(); | ||||||
|  |     id: string = 'animate'; | ||||||
|  | 
 | ||||||
|  |     group!: LayerGroup; | ||||||
|  |     hero!: HeroRenderer; | ||||||
|  |     animate!: Animate; | ||||||
|  | 
 | ||||||
|  |     private animation: Set<AnimateData> = new Set(); | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * 绘制一个跟随勇士的动画 | ||||||
|  |      * @param name 动画id | ||||||
|  |      */ | ||||||
|  |     async drawHeroAnimate(name: AnimationIds) { | ||||||
|  |         const animate = this.animate.animate(name, 0, 0); | ||||||
|  |         this.updatePosition(animate); | ||||||
|  |         await this.animate.draw(animate); | ||||||
|  |         this.animation.delete(animate); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private updatePosition(animate: AnimateData) { | ||||||
|  |         if (!this.hero.renderable) return; | ||||||
|  |         const { x, y } = this.hero.renderable; | ||||||
|  |         const cell = this.group.cellSize; | ||||||
|  |         const half = cell / 2; | ||||||
|  |         animate.centerX = x * cell + half; | ||||||
|  |         animate.centerY = y * cell + half; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private onMoveTick = (x: number, y: number) => { | ||||||
|  |         const cell = this.group.cellSize; | ||||||
|  |         const half = cell / 2; | ||||||
|  |         const ax = x * cell + half; | ||||||
|  |         const ay = y * cell + half; | ||||||
|  |         this.animation.forEach(v => { | ||||||
|  |             v.centerX = ax; | ||||||
|  |             v.centerY = ay; | ||||||
|  |         }); | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     private listen() { | ||||||
|  |         this.hero.on('moveTick', this.onMoveTick); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     awake(group: LayerGroup): void { | ||||||
|  |         this.group = group; | ||||||
|  |         const ex = group.getLayer('event')?.getExtends('floor-hero'); | ||||||
|  |         if (ex instanceof HeroRenderer) { | ||||||
|  |             this.hero = ex; | ||||||
|  |             this.animate = new Animate(); | ||||||
|  |             this.animate.size(group.width, group.height); | ||||||
|  |             this.animate.setHD(true); | ||||||
|  |             this.animate.setZIndex(100); | ||||||
|  |             group.appendChild(this.animate); | ||||||
|  |             LayerGroupAnimate.animateList.add(this); | ||||||
|  |             this.listen(); | ||||||
|  |         } else { | ||||||
|  |             logger.error( | ||||||
|  |                 14, | ||||||
|  |                 `Animate extends needs 'floor-hero' extends as dependency.` | ||||||
|  |             ); | ||||||
|  |             group.removeExtends('animate'); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     onDestroy(group: LayerGroup): void { | ||||||
|  |         this.hero.off('moveTick', this.onMoveTick); | ||||||
|  |         LayerGroupAnimate.animateList.delete(this); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | interface AnimateData { | ||||||
|  |     obj: globalThis.Animate; | ||||||
|  |     /** 第一帧是全局第几帧 */ | ||||||
|  |     readonly start: number; | ||||||
|  |     /** 当前是第几帧 */ | ||||||
|  |     index: number; | ||||||
|  |     /** 是否需要播放音频 */ | ||||||
|  |     sound: boolean; | ||||||
|  |     centerX: number; | ||||||
|  |     centerY: number; | ||||||
|  |     onEnd?: () => void; | ||||||
|  |     readonly absolute: boolean; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export class Animate extends Sprite { | ||||||
|  |     /** 绝对位置的动画 */ | ||||||
|  |     private absoluteAnimates: Set<AnimateData> = new Set(); | ||||||
|  |     /** 静态位置的动画 */ | ||||||
|  |     private staticAnimates: Set<AnimateData> = new Set(); | ||||||
|  | 
 | ||||||
|  |     private delegation: number; | ||||||
|  |     private frame: number = 0; | ||||||
|  |     private lastTime: number = 0; | ||||||
|  | 
 | ||||||
|  |     constructor() { | ||||||
|  |         super('absolute', false); | ||||||
|  | 
 | ||||||
|  |         this.setRenderFn((canvas, transform) => { | ||||||
|  |             const { ctx } = canvas; | ||||||
|  |             ctx.save(); | ||||||
|  |             this.drawAnimates(this.absoluteAnimates, canvas); | ||||||
|  |             transformCanvas(canvas, transform); | ||||||
|  |             this.drawAnimates(this.staticAnimates, canvas); | ||||||
|  |             ctx.restore(); | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |         this.delegation = this.delegateTicker(time => { | ||||||
|  |             if (time - this.lastTime < 50) return; | ||||||
|  |             this.lastTime = time; | ||||||
|  |             this.frame++; | ||||||
|  |             if ( | ||||||
|  |                 this.absoluteAnimates.size > 0 || | ||||||
|  |                 this.staticAnimates.size > 0 | ||||||
|  |             ) { | ||||||
|  |                 this.update(this); | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |         adapter.add(this); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private drawAnimates( | ||||||
|  |         data: Set<AnimateData>, | ||||||
|  |         canvas: MotaOffscreenCanvas2D | ||||||
|  |     ) { | ||||||
|  |         if (data.size === 0) return; | ||||||
|  |         const { ctx } = canvas; | ||||||
|  |         const toDelete = new Set<AnimateData>(); | ||||||
|  |         data.forEach(v => { | ||||||
|  |             const obj = v.obj; | ||||||
|  |             const index = v.index; | ||||||
|  |             const frame = obj.frames[index]; | ||||||
|  |             const ratio = obj.ratio; | ||||||
|  |             if (!v.sound) { | ||||||
|  |                 const se = (index % obj.frame) + 1; | ||||||
|  |                 core.playSound(v.obj.se[se], v.obj.pitch[se]); | ||||||
|  |                 v.sound = true; | ||||||
|  |             } | ||||||
|  |             const centerX = v.centerX; | ||||||
|  |             const centerY = v.centerY; | ||||||
|  | 
 | ||||||
|  |             frame.forEach(v => { | ||||||
|  |                 const img = obj.images[v.index]; | ||||||
|  |                 if (!img) return; | ||||||
|  | 
 | ||||||
|  |                 const realWidth = (img.width * ratio * v.zoom) / 100; | ||||||
|  |                 const realHeight = (img.height * ratio * v.zoom) / 100; | ||||||
|  |                 ctx.globalAlpha = v.opacity / 255; | ||||||
|  | 
 | ||||||
|  |                 const cx = centerX + v.x; | ||||||
|  |                 const cy = centerY + v.y; | ||||||
|  | 
 | ||||||
|  |                 const ix = -realWidth / 2; | ||||||
|  |                 const iy = -realHeight / 2; | ||||||
|  |                 const angle = v.angle ? (-v.angle * Math.PI) / 180 : 0; | ||||||
|  | 
 | ||||||
|  |                 ctx.save(); | ||||||
|  |                 ctx.translate(cx, cy); | ||||||
|  |                 if (v.mirror) { | ||||||
|  |                     ctx.scale(-1, 1); | ||||||
|  |                 } | ||||||
|  |                 ctx.rotate(angle); | ||||||
|  |                 ctx.drawImage(img, ix, iy, realWidth, realHeight); | ||||||
|  |                 ctx.restore(); | ||||||
|  |             }); | ||||||
|  |             const now = this.frame - v.start; | ||||||
|  |             if (now !== v.index) v.sound = true; | ||||||
|  |             v.index = now; | ||||||
|  |             if (v.index === v.obj.frame) { | ||||||
|  |                 toDelete.add(v); | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|  |         toDelete.forEach(v => { | ||||||
|  |             data.delete(v); | ||||||
|  |             v.onEnd?.(); | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * 创建一个可以被执行的动画 | ||||||
|  |      * @param name 动画名称 | ||||||
|  |      * @param absolute 是否是绝对定位,绝对定位不会受到transform的影响 | ||||||
|  |      */ | ||||||
|  |     animate( | ||||||
|  |         name: AnimationIds, | ||||||
|  |         x: number, | ||||||
|  |         y: number, | ||||||
|  |         absolute: boolean = false | ||||||
|  |     ) { | ||||||
|  |         const animate = core.material.animates[name]; | ||||||
|  |         const data: AnimateData = { | ||||||
|  |             index: 0, | ||||||
|  |             start: this.frame, | ||||||
|  |             obj: animate, | ||||||
|  |             centerX: x, | ||||||
|  |             centerY: y, | ||||||
|  |             absolute, | ||||||
|  |             sound: false | ||||||
|  |         }; | ||||||
|  |         return data; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * 绘制动画,动画结束时兑现返回的Promise | ||||||
|  |      * @param animate 动画信息 | ||||||
|  |      * @returns | ||||||
|  |      */ | ||||||
|  |     draw(animate: AnimateData): Promise<void> { | ||||||
|  |         return new Promise(res => { | ||||||
|  |             if (animate.absolute) { | ||||||
|  |                 this.absoluteAnimates.add(animate); | ||||||
|  |             } else { | ||||||
|  |                 this.staticAnimates.add(animate); | ||||||
|  |             } | ||||||
|  |             animate.onEnd = () => { | ||||||
|  |                 res(); | ||||||
|  |             }; | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * 根据动画名称、坐标、定位绘制动画 | ||||||
|  |      * @param name 动画名称 | ||||||
|  |      * @param absolute 是否是绝对定位 | ||||||
|  |      */ | ||||||
|  |     drawAnimate( | ||||||
|  |         name: AnimationIds, | ||||||
|  |         x: number, | ||||||
|  |         y: number, | ||||||
|  |         absolute: boolean = false | ||||||
|  |     ) { | ||||||
|  |         return this.draw(this.animate(name, x, y, absolute)); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     destroy(): void { | ||||||
|  |         super.destroy(); | ||||||
|  |         this.removeTicker(this.delegation); | ||||||
|  |         adapter.remove(this); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const adapter = new RenderAdapter<Animate>('animate'); | ||||||
|  | adapter.receive('drawAnimate', (item, name, x, y, absolute) => { | ||||||
|  |     return item.drawAnimate(name, x, y, absolute); | ||||||
|  | }); | ||||||
|  | adapter.receiveGlobal('drawHeroAnimate', name => { | ||||||
|  |     const execute: Promise<void>[] = []; | ||||||
|  |     LayerGroupAnimate.animateList.forEach(v => { | ||||||
|  |         execute.push(v.drawHeroAnimate(name)); | ||||||
|  |     }); | ||||||
|  |     return Promise.all(execute); | ||||||
|  | }); | ||||||
| @ -381,9 +381,9 @@ export class LayerDoorAnimate implements ILayerRenderExtends { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| const doorAdapter = new RenderAdapter<LayerDoorAnimate>('door-animate'); | const doorAdapter = new RenderAdapter<LayerDoorAnimate>('door-animate'); | ||||||
| doorAdapter.recieve('openDoor', (item, block: Block) => { | doorAdapter.receive('openDoor', (item, block: Block) => { | ||||||
|     return item.openDoor(block); |     return item.openDoor(block); | ||||||
| }); | }); | ||||||
| doorAdapter.recieve('closeDoor', (item, block: Block) => { | doorAdapter.receive('closeDoor', (item, block: Block) => { | ||||||
|     return item.closeDoor(block); |     return item.closeDoor(block); | ||||||
| }); | }); | ||||||
|  | |||||||
| @ -359,42 +359,42 @@ export class HeroRenderer | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| const adapter = new RenderAdapter<HeroRenderer>('hero-adapter'); | const adapter = new RenderAdapter<HeroRenderer>('hero-adapter'); | ||||||
| adapter.recieve('readyMove', item => { | adapter.receive('readyMove', item => { | ||||||
|     item.readyMove(); |     item.readyMove(); | ||||||
|     return Promise.resolve(); |     return Promise.resolve(); | ||||||
| }); | }); | ||||||
| adapter.recieve('move', (item, dir: Dir) => { | adapter.receive('move', (item, dir: Dir) => { | ||||||
|     return item.move(dir); |     return item.move(dir); | ||||||
| }); | }); | ||||||
| adapter.recieve('endMove', item => { | adapter.receive('endMove', item => { | ||||||
|     return item.endMove(); |     return item.endMove(); | ||||||
| }); | }); | ||||||
| adapter.recieve( | adapter.receive( | ||||||
|     'moveAs', |     'moveAs', | ||||||
|     (item, x: number, y: number, time: number, fn: TimingFn<3>) => { |     (item, x: number, y: number, time: number, fn: TimingFn<3>) => { | ||||||
|         return item.moveAs(x, y, time, fn); |         return item.moveAs(x, y, time, fn); | ||||||
|     } |     } | ||||||
| ); | ); | ||||||
| adapter.recieve('setMoveSpeed', (item, speed: number) => { | adapter.receive('setMoveSpeed', (item, speed: number) => { | ||||||
|     item.setMoveSpeed(speed); |     item.setMoveSpeed(speed); | ||||||
|     return Promise.resolve(); |     return Promise.resolve(); | ||||||
| }); | }); | ||||||
| adapter.recieve('setHeroLoc', (item, x?: number, y?: number) => { | adapter.receive('setHeroLoc', (item, x?: number, y?: number) => { | ||||||
|     item.setHeroLoc(x, y); |     item.setHeroLoc(x, y); | ||||||
|     return Promise.resolve(); |     return Promise.resolve(); | ||||||
| }); | }); | ||||||
| adapter.recieve('turn', (item, dir: Dir2) => { | adapter.receive('turn', (item, dir: Dir2) => { | ||||||
|     item.turn(dir); |     item.turn(dir); | ||||||
|     return Promise.resolve(); |     return Promise.resolve(); | ||||||
| }); | }); | ||||||
| adapter.recieve('setImage', (item, image: SizedCanvasImageSource) => { | adapter.receive('setImage', (item, image: SizedCanvasImageSource) => { | ||||||
|     item.setImage(image); |     item.setImage(image); | ||||||
|     return Promise.resolve(); |     return Promise.resolve(); | ||||||
| }); | }); | ||||||
| // 同步fallback,用于适配现在的样板,之后会删除
 | // 同步fallback,用于适配现在的样板,之后会删除
 | ||||||
| adapter.recieveSync('setHeroLoc', (item, x?: number, y?: number) => { | adapter.receiveSync('setHeroLoc', (item, x?: number, y?: number) => { | ||||||
|     item.setHeroLoc(x, y); |     item.setHeroLoc(x, y); | ||||||
| }); | }); | ||||||
| adapter.recieveSync('turn', (item, dir: Dir2) => { | adapter.receiveSync('turn', (item, dir: Dir2) => { | ||||||
|     item.turn(dir); |     item.turn(dir); | ||||||
| }); | }); | ||||||
|  | |||||||
| @ -1,26 +1,30 @@ | |||||||
| import type { RenderAdapter } from '@/core/render/adapter'; | import type { RenderAdapter } from '@/core/render/adapter'; | ||||||
|  | import type { LayerGroupAnimate } from '@/core/render/preset/animate'; | ||||||
| import type { LayerDoorAnimate } from '@/core/render/preset/floor'; | import type { LayerDoorAnimate } from '@/core/render/preset/floor'; | ||||||
| import type { HeroRenderer } from '@/core/render/preset/hero'; | import type { HeroRenderer } from '@/core/render/preset/hero'; | ||||||
| import { hook } from '@/game/game'; |  | ||||||
| 
 | 
 | ||||||
| interface Adapters { | interface Adapters { | ||||||
|     'hero-adapter'?: RenderAdapter<HeroRenderer>; |     'hero-adapter'?: RenderAdapter<HeroRenderer>; | ||||||
|     'door-animate'?: RenderAdapter<LayerDoorAnimate>; |     'door-animate'?: RenderAdapter<LayerDoorAnimate>; | ||||||
|  |     animate?: RenderAdapter<LayerGroupAnimate>; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| const adapters: Adapters = {}; | const adapters: Adapters = {}; | ||||||
| 
 | 
 | ||||||
| export function init() { | export function init() { | ||||||
|     const hook = Mota.require('var', 'hook'); |     const hook = Mota.require('var', 'hook'); | ||||||
|  |     const loading = Mota.require('var', 'loading'); | ||||||
|     let fallbackIds: number = 1e8; |     let fallbackIds: number = 1e8; | ||||||
| 
 | 
 | ||||||
|     if (!main.replayChecking && main.mode === 'play') { |     if (!main.replayChecking && main.mode === 'play') { | ||||||
|         const Adapter = Mota.require('module', 'Render').RenderAdapter; |         const Adapter = Mota.require('module', 'Render').RenderAdapter; | ||||||
|         const hero = Adapter.get<HeroRenderer>('hero-adapter'); |         const hero = Adapter.get<HeroRenderer>('hero-adapter'); | ||||||
|         const doorAnimate = Adapter.get<LayerDoorAnimate>('door-animate'); |         const doorAnimate = Adapter.get<LayerDoorAnimate>('door-animate'); | ||||||
|  |         const animate = Adapter.get<LayerGroupAnimate>('animate'); | ||||||
| 
 | 
 | ||||||
|         adapters['hero-adapter'] = hero; |         adapters['hero-adapter'] = hero; | ||||||
|         adapters['door-animate'] = doorAnimate; |         adapters['door-animate'] = doorAnimate; | ||||||
|  |         adapters['animate'] = animate; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     let moving: boolean = false; |     let moving: boolean = false; | ||||||
| @ -391,6 +395,87 @@ export function init() { | |||||||
|                 this._openDoor_animate(block, x, y, callback); |                 this._openDoor_animate(block, x, y, callback); | ||||||
|             } |             } | ||||||
|         }; |         }; | ||||||
|  | 
 | ||||||
|  |         // ----- animate & hero animate
 | ||||||
|  |         ////// 绘制动画 //////
 | ||||||
|  |         maps.prototype.drawAnimate = function ( | ||||||
|  |             name: AnimationIds, | ||||||
|  |             x: number, | ||||||
|  |             y: number, | ||||||
|  |             alignWindow?: boolean, | ||||||
|  |             callback?: () => void | ||||||
|  |         ) { | ||||||
|  |             // @ts-ignore
 | ||||||
|  |             name = core.getMappedName(name); | ||||||
|  | 
 | ||||||
|  |             // 正在播放录像:不显示动画
 | ||||||
|  |             if ( | ||||||
|  |                 core.isReplaying() || | ||||||
|  |                 !core.material.animates[name] || | ||||||
|  |                 x == null || | ||||||
|  |                 y == null | ||||||
|  |             ) { | ||||||
|  |                 if (callback) callback(); | ||||||
|  |                 return -1; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             adapters.animate | ||||||
|  |                 ?.all( | ||||||
|  |                     'drawAnimate', | ||||||
|  |                     name, | ||||||
|  |                     x * 32 + 16, | ||||||
|  |                     y * 32 + 16, | ||||||
|  |                     alignWindow ?? false | ||||||
|  |                 ) | ||||||
|  |                 .then(() => { | ||||||
|  |                     callback?.(); | ||||||
|  |                 }); | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         maps.prototype.drawHeroAnimate = function ( | ||||||
|  |             name: AnimationIds, | ||||||
|  |             callback?: () => void | ||||||
|  |         ) { | ||||||
|  |             // @ts-ignore
 | ||||||
|  |             name = core.getMappedName(name); | ||||||
|  | 
 | ||||||
|  |             // 正在播放录像或动画不存在:不显示动画
 | ||||||
|  |             if (core.isReplaying() || !core.material.animates[name]) { | ||||||
|  |                 if (callback) callback(); | ||||||
|  |                 return -1; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             adapters.animate?.global('drawHeroAnimate', name).then(() => { | ||||||
|  |                 callback?.(); | ||||||
|  |             }); | ||||||
|  | 
 | ||||||
|  |             // 开始绘制
 | ||||||
|  |             // var animate = core.material.animates[name];
 | ||||||
|  |             // animate.se = animate.se || {};
 | ||||||
|  |             // if (typeof animate.se == 'string') animate.se = { 1: animate.se };
 | ||||||
|  | 
 | ||||||
|  |             // var id = setTimeout(null);
 | ||||||
|  |             // core.status.animateObjs.push({
 | ||||||
|  |             //     name: name,
 | ||||||
|  |             //     id: id,
 | ||||||
|  |             //     animate: animate,
 | ||||||
|  |             //     hero: true,
 | ||||||
|  |             //     index: 0,
 | ||||||
|  |             //     callback: callback
 | ||||||
|  |             // });
 | ||||||
|  | 
 | ||||||
|  |             // return id;
 | ||||||
|  |         }; | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     loading.once('loaded', () => { | ||||||
|  |         for (const animate of Object.values(core.material.animates)) { | ||||||
|  |             animate.se ??= {}; | ||||||
|  |             if (typeof animate.se === 'string') { | ||||||
|  |                 animate.se = { 1: animate.se }; | ||||||
|  |             } | ||||||
|  |             animate.pitch ??= {}; | ||||||
|  |         } | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|     return { readyMove, endMove, move }; |     return { readyMove, endMove, move }; | ||||||
|  | |||||||
							
								
								
									
										5
									
								
								src/types/util.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								src/types/util.d.ts
									
									
									
									
										vendored
									
									
								
							| @ -753,7 +753,7 @@ type EventValuePreffix = | |||||||
| 
 | 
 | ||||||
| interface Animate { | interface Animate { | ||||||
|     /** |     /** | ||||||
|      * 动画的帧数s |      * 动画的帧数 | ||||||
|      */ |      */ | ||||||
|     frame: number; |     frame: number; | ||||||
| 
 | 
 | ||||||
| @ -775,7 +775,8 @@ interface Animate { | |||||||
|     /** |     /** | ||||||
|      * 音效 |      * 音效 | ||||||
|      */ |      */ | ||||||
|     se: string; |     se: any; | ||||||
|  |     pitch: any; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| type Save = DeepReadonly<{ | type Save = DeepReadonly<{ | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user