mirror of
				https://github.com/unanmed/HumanBreak.git
				synced 2025-10-31 12:12:58 +08:00 
			
		
		
		
	feat: 地图渲染基本完成
This commit is contained in:
		
							parent
							
								
									117bd94928
								
							
						
					
					
						commit
						78efe81423
					
				| @ -40,7 +40,7 @@ var enemys_fcae963b_31c9_42b4_b48c_bb48d09f3f80 = | ||||
| 	"angel": {"name":"天使","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]}, | ||||
| 	"elemental": {"name":"元素生物","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]}, | ||||
| 	"steelGuard": {"name":"铁守卫","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[18],"value":20}, | ||||
| 	"evilBat": {"name":"邪恶蝙蝠","hp":1000,"atk":800,"def":350,"money":1,"exp":40,"point":0,"special":[2],"bigImage":"bear.png"}, | ||||
| 	"evilBat": {"name":"邪恶蝙蝠","hp":1000,"atk":800,"def":350,"money":1,"exp":40,"point":0,"special":[2],"bigImage":null}, | ||||
| 	"frozenSkeleton": {"name":"冻死骨","hp":7500,"atk":2500,"def":1000,"money":2,"exp":90,"point":0,"special":[1,20],"crit":500,"ice":10,"description":"弱小,无助,哀嚎,这就是残酷的现实。哪怕你身处极寒之中,也难有人对你伸出援手。人类总会帮助他人,但在这绝望的环境下,人类的本性便暴露无遗。这一个个精致却又无情的骷髅,便是那些在极寒之中死去的冤魂。"}, | ||||
| 	"silverSlimelord": {"name":"银怪王","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]}, | ||||
| 	"goldSlimelord": {"name":"金怪王","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]}, | ||||
|  | ||||
| @ -32,6 +32,7 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = { | ||||
|             // 初始化地图
 | ||||
|             core.status.floorId = floorId; | ||||
|             core.status.maps = maps; | ||||
|             core.extractBlocks(floorId); | ||||
|             core.maps._resetFloorImages(); | ||||
|             // 初始化怪物和道具
 | ||||
|             core.material.enemys = core.enemys.getEnemys(); | ||||
|  | ||||
| @ -73,7 +73,7 @@ var maps_90f36752_8815_4be8_b32b_d7fad1d0542e = | ||||
| 	"83": {"cls":"animates","id":"redDoor","trigger":"openDoor","animate":1,"doorInfo":{"time":160,"openSound":"door.mp3","closeSound":"door.mp3","keys":{"redKey":1}},"name":"红门"}, | ||||
| 	"84": {"cls":"animates","id":"greenDoor","trigger":"openDoor","animate":1,"doorInfo":{"time":160,"openSound":"door.mp3","closeSound":"door.mp3","keys":{"greenKey":1}},"name":"绿门"}, | ||||
| 	"85": {"cls":"animates","id":"specialDoor","trigger":"openDoor","animate":1,"doorInfo":{"time":160,"openSound":"door.mp3","closeSound":"door.mp3","keys":{"specialKey":1}},"name":"机关门"}, | ||||
| 	"86": {"cls":"animates","id":"steelDoor","trigger":"openDoor","animate":1,"doorInfo":{"time":160,"openSound":"door.mp3","closeSound":"door.mp3","keys":{"steelKey":1}},"name":"铁门","bigImage":"bear.png"}, | ||||
| 	"86": {"cls":"animates","id":"steelDoor","trigger":"openDoor","animate":1,"doorInfo":{"time":160,"openSound":"door.mp3","closeSound":"door.mp3","keys":{"steelKey":1}},"name":"铁门","bigImage":null}, | ||||
| 	"87": {"cls":"terrains","id":"upFloor","canPass":true}, | ||||
| 	"88": {"cls":"terrains","id":"downFloor","canPass":true}, | ||||
| 	"89": {"cls":"animates","id":"portal","canPass":true}, | ||||
|  | ||||
| @ -1,5 +1,7 @@ | ||||
| import { EmitableEvent, EventEmitter } from '../common/eventEmitter'; | ||||
| import { logger } from '../common/logger'; | ||||
| import { MotaOffscreenCanvas2D } from '../fx/canvas2d'; | ||||
| import { SizedCanvasImageSource } from './preset/misc'; | ||||
| 
 | ||||
| // 经过测试(https://www.measurethat.net/Benchmarks/Show/30741/1/drawimage-img-vs-canvas-vs-bitmap-cropping-fix-loading)
 | ||||
| // 得出结论,ImageBitmap和Canvas的绘制性能不如Image,于是直接画Image就行,所以缓存基本上就是存Image
 | ||||
| @ -39,6 +41,27 @@ interface TextureRequire { | ||||
|     images: Record<ImageIds, HTMLImageElement>; | ||||
| } | ||||
| 
 | ||||
| interface RenderableDataBase { | ||||
|     /** 图块的总帧数 */ | ||||
|     frame: number; | ||||
|     /** 对应图块属性的动画帧数,-1表示没有设定 */ | ||||
|     animate: number; | ||||
|     /** 是否是大怪物 */ | ||||
|     bigImage: boolean; | ||||
|     render: [x: number, y: number, width: number, height: number][]; | ||||
| } | ||||
| 
 | ||||
| export interface RenderableData extends RenderableDataBase { | ||||
|     image: SizedCanvasImageSource; | ||||
|     autotile: false; | ||||
| } | ||||
| 
 | ||||
| export interface AutotileRenderable extends RenderableDataBase { | ||||
|     image: Record<string, SizedCanvasImageSource>; | ||||
|     autotile: true; | ||||
|     bigImage: false; | ||||
| } | ||||
| 
 | ||||
| interface TextureCacheEvent extends EmitableEvent {} | ||||
| 
 | ||||
| class TextureCache extends EventEmitter<TextureCacheEvent> { | ||||
| @ -49,6 +72,9 @@ class TextureCache extends EventEmitter<TextureCacheEvent> { | ||||
| 
 | ||||
|     idNumberMap!: IdToNumber; | ||||
| 
 | ||||
|     /** 渲染信息 */ | ||||
|     renderable: Map<number, RenderableData | AutotileRenderable> = new Map(); | ||||
| 
 | ||||
|     constructor() { | ||||
|         super(); | ||||
|         this.material = imageMap as Record<ImageMapKeys, HTMLImageElement>; | ||||
| @ -64,6 +90,7 @@ class TextureCache extends EventEmitter<TextureCacheEvent> { | ||||
|             this.tileset = core.material.images.tilesets; | ||||
|             this.autotile = splitAutotiles(this.idNumberMap); | ||||
|             this.images = core.material.images.images; | ||||
|             this.calRenderable(); | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
| @ -78,6 +105,191 @@ class TextureCache extends EventEmitter<TextureCacheEvent> { | ||||
|     ): TextureRequire[T][K] { | ||||
|         return this[type][key]; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * 计算每个图块的可渲染信息 | ||||
|      */ | ||||
|     calRenderable() { | ||||
|         const map = maps_90f36752_8815_4be8_b32b_d7fad1d0542e; | ||||
|         for (const [key, data] of Object.entries(map)) { | ||||
|             this.calRenderableByNum(parseInt(key)); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     calRenderableByNum( | ||||
|         num: number | ||||
|     ): RenderableData | AutotileRenderable | null { | ||||
|         const map = maps_90f36752_8815_4be8_b32b_d7fad1d0542e; | ||||
|         const enemys = enemys_fcae963b_31c9_42b4_b48c_bb48d09f3f80; | ||||
|         const icons = icons_4665ee12_3a1f_44a4_bea3_0fccba634dc1; | ||||
| 
 | ||||
|         /** 特判空图块与空气墙 */ | ||||
|         if (num === 0 || num === 17) return null; | ||||
| 
 | ||||
|         // 额外素材
 | ||||
|         if (num >= 10000) { | ||||
|             const offset = core.getTilesetOffset(num); | ||||
|             if (!offset) return null; | ||||
|             const { image, x, y } = offset; | ||||
|             const data: RenderableData = { | ||||
|                 image: this.tileset[image], | ||||
|                 frame: 1, | ||||
|                 render: [[x * 32, y * 32, 32, 32]], | ||||
|                 animate: 0, | ||||
|                 autotile: false, | ||||
|                 bigImage: false | ||||
|             }; | ||||
|             this.renderable.set(num, data); | ||||
|             return data; | ||||
|         } | ||||
| 
 | ||||
|         const data = map[num as Exclude<AllNumbers, 0>]; | ||||
|         // 地狱般的分支if
 | ||||
|         if (data) { | ||||
|             let { cls, faceIds, bigImage, id, animate } = data; | ||||
|             if (cls === 'enemys' || cls === 'enemy48') { | ||||
|                 // 怪物需要特殊处理,因为它的大怪物信息不在 maps 里面
 | ||||
|                 ({ bigImage, faceIds } = enemys[id as EnemyIds]); | ||||
|             } | ||||
|             if (bigImage) { | ||||
|                 const image = core.material.images.images[bigImage]; | ||||
|                 if (!image) { | ||||
|                     logger.warn( | ||||
|                         10, | ||||
|                         `Cannot resolve big image of enemy '${id}'.` | ||||
|                     ); | ||||
|                     return null; | ||||
|                 } | ||||
|                 let line = 0; | ||||
|                 if (faceIds) { | ||||
|                     const arr = ['down', 'left', 'right', 'up']; | ||||
|                     for (let i = 0; i < arr.length; i++) { | ||||
|                         if (faceIds[arr[i] as Dir] === id) { | ||||
|                             line = i; | ||||
|                             break; | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|                 const totalLines = image.width / image.height >= 2 ? 1 : 4; | ||||
|                 const w = Math.round(image.width / 4); | ||||
|                 const h = Math.round(image.height / totalLines); | ||||
|                 const y = h * line; | ||||
|                 const data: RenderableData = { | ||||
|                     image, | ||||
|                     frame: 4, | ||||
|                     render: [ | ||||
|                         [0, y, w, h], | ||||
|                         [w, y, w, h], | ||||
|                         [w * 2, y, w, h], | ||||
|                         [w * 3, y, w, h] | ||||
|                     ], | ||||
|                     animate: (animate ?? 0) - 1, | ||||
|                     autotile: false, | ||||
|                     bigImage: true | ||||
|                 }; | ||||
|                 this.renderable.set(num, data); | ||||
|                 return data; | ||||
|             } | ||||
|             // enemy48和npc48都应该视为大怪物
 | ||||
|             if (cls === 'enemy48' || cls === 'npc48') { | ||||
|                 const img = core.material.images[cls]; | ||||
|                 // @ts-ignore
 | ||||
|                 const line = icons[cls][id]; | ||||
|                 const w = 32; | ||||
|                 const h = 48; | ||||
|                 const y = h * line; | ||||
|                 const data: RenderableData = { | ||||
|                     image: img, | ||||
|                     frame: 4, | ||||
|                     render: [ | ||||
|                         [0, y, w, h], | ||||
|                         [w, y, w, h], | ||||
|                         [w * 2, y, w, h], | ||||
|                         [w * 3, y, w, h] | ||||
|                     ], | ||||
|                     animate: (animate ?? 0) - 1, | ||||
|                     autotile: false, | ||||
|                     bigImage: true | ||||
|                 }; | ||||
|                 this.renderable.set(num, data); | ||||
|                 return data; | ||||
|             } | ||||
|             // 自动元件
 | ||||
|             if (cls === 'autotile') { | ||||
|                 const auto = this.autotile[num as AllNumbersOf<'autotile'>]; | ||||
|                 const cell = 32; | ||||
|                 const render: [number, number, number, number][] = []; | ||||
|                 if (auto.frame >= 1) { | ||||
|                     render.push([0, 0, cell, cell]); | ||||
|                 } | ||||
|                 if (auto.frame >= 3) { | ||||
|                     render.push( | ||||
|                         [cell, 0, cell, cell], | ||||
|                         [cell * 2, 0, cell, cell] | ||||
|                     ); | ||||
|                 } | ||||
|                 if (auto.frame >= 4) { | ||||
|                     render.push([cell * 3, 0, cell, cell]); | ||||
|                 } | ||||
|                 const data: AutotileRenderable = { | ||||
|                     image: auto.cache, | ||||
|                     frame: auto.frame, | ||||
|                     render, | ||||
|                     autotile: true, | ||||
|                     bigImage: false, | ||||
|                     animate: (animate ?? 0) - 1 | ||||
|                 }; | ||||
|                 this.renderable.set(num, data); | ||||
|                 return data; | ||||
|             } else { | ||||
|                 const image = | ||||
|                     core.material.images[ | ||||
|                         cls as Exclude<Cls, 'tileset' | 'autotile'> | ||||
|                     ]; | ||||
|                 const frame = core.getAnimateFrames(cls); | ||||
|                 const cell = 32; | ||||
|                 // @ts-ignore
 | ||||
|                 const offset = (icons[cls][id] as number) * cell; | ||||
|                 const render: [number, number, number, number][] = [ | ||||
|                     [0, offset, cell, cell] | ||||
|                 ]; | ||||
|                 if (frame === 2) { | ||||
|                     render.push([cell, offset, cell, cell]); | ||||
|                 } | ||||
|                 if (frame === 4) { | ||||
|                     render.push( | ||||
|                         [cell, offset, cell, cell], | ||||
|                         [cell * 2, offset, cell, cell], | ||||
|                         [cell * 3, offset, cell, cell] | ||||
|                     ); | ||||
|                 } | ||||
|                 const data: RenderableData = { | ||||
|                     image, | ||||
|                     frame: frame, | ||||
|                     render, | ||||
|                     autotile: false, | ||||
|                     bigImage: false, | ||||
|                     animate: (animate ?? 0) - 1 | ||||
|                 }; | ||||
|                 this.renderable.set(num, data); | ||||
|                 return data; | ||||
|             } | ||||
|         } else { | ||||
|             logger.warn( | ||||
|                 11, | ||||
|                 `Cannot resolve material ${num}. Material not exists.` | ||||
|             ); | ||||
|             return null; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * 获取一个图块的渲染信息,自动元件会特别标明autotile属性 | ||||
|      * @param num 图块数字 | ||||
|      */ | ||||
|     getRenderable(num: number) { | ||||
|         return this.renderable.get(num) ?? this.calRenderableByNum(num); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| export const texture = new TextureCache(); | ||||
|  | ||||
| @ -26,6 +26,10 @@ export class Container extends RenderItem implements ICanvasCachedRenderItem { | ||||
|         camera: Camera | ||||
|     ): void { | ||||
|         this.emit('beforeRender'); | ||||
|         if (this.needUpdate) { | ||||
|             this.cache(this.writing); | ||||
|             this.needUpdate = false; | ||||
|         } | ||||
|         withCacheRender(this, canvas, ctx, camera, c => { | ||||
|             this.sortedChildren.forEach(v => { | ||||
|                 if (!v.antiAliasing) { | ||||
|  | ||||
| @ -77,7 +77,7 @@ interface IRenderConfig { | ||||
|     setHD(hd: boolean): void; | ||||
| 
 | ||||
|     /** | ||||
|      * 设置当前渲染原始是否启用抗锯齿 | ||||
|      * 设置当前渲染元素是否启用抗锯齿 | ||||
|      * @param anti 是否抗锯齿 | ||||
|      */ | ||||
|     setAntiAliasing(anti: boolean): void; | ||||
| @ -135,7 +135,7 @@ export abstract class RenderItem | ||||
|     constructor() { | ||||
|         super(); | ||||
| 
 | ||||
|         this.using = '@default'; | ||||
|         // this.using = '@default';
 | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @ -194,21 +194,7 @@ export abstract class RenderItem | ||||
|     update(item?: RenderItem): void { | ||||
|         if (this.needUpdate) return; | ||||
|         this.needUpdate = true; | ||||
|         requestAnimationFrame(() => { | ||||
|             this.needUpdate = false; | ||||
|             if (!this.parent) return; | ||||
|             this.cache(this.writing); | ||||
|             this.refresh(item); | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * 立刻更新这个组件,不延迟到下一个tick | ||||
|      */ | ||||
|     protected refresh(item?: RenderItem) { | ||||
|         this.emit('beforeUpdate', item); | ||||
|         this.parent?.refresh(item); | ||||
|         this.emit('afterUpdate', item); | ||||
|         this.parent?.update(item); | ||||
|     } | ||||
| 
 | ||||
|     setHD(hd: boolean): void { | ||||
| @ -223,7 +209,7 @@ export abstract class RenderItem | ||||
| 
 | ||||
|     setZIndex(zIndex: number) { | ||||
|         this.zIndex = zIndex; | ||||
|         (this.parent as Container).sortChildren?.(); | ||||
|         (this.parent as Container)?.sortChildren?.(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -5,8 +5,7 @@ import { Camera } from '../camera'; | ||||
| import { TimingFn } from 'mutate-animate'; | ||||
| import { IRenderDestroyable, RenderItem } from '../item'; | ||||
| import { logger } from '@/core/common/logger'; | ||||
| import { texture } from '../cache'; | ||||
| import { SizedCanvasImageSource } from './misc'; | ||||
| import { AutotileRenderable, RenderableData, texture } from '../cache'; | ||||
| import { glMatrix } from 'gl-matrix'; | ||||
| 
 | ||||
| interface LayerCacheItem { | ||||
| @ -14,24 +13,12 @@ interface LayerCacheItem { | ||||
|     canvas: HTMLCanvasElement; | ||||
| } | ||||
| 
 | ||||
| interface LayerRenderableData { | ||||
|     image: SizedCanvasImageSource; | ||||
|     frame: number; | ||||
|     render: [x: number, y: number, width: number, height: number][]; | ||||
| } | ||||
| 
 | ||||
| interface LayerMovingRenderable extends LayerRenderableData { | ||||
| interface LayerMovingRenderable extends RenderableData { | ||||
|     zIndex: number; | ||||
|     x: number; | ||||
|     y: number; | ||||
| } | ||||
| 
 | ||||
| interface BigImageData { | ||||
|     image: HTMLImageElement; | ||||
|     line: number; | ||||
|     totalLines: number; | ||||
| } | ||||
| 
 | ||||
| interface NeedRenderData { | ||||
|     /** 需要渲染的地图内容 */ | ||||
|     res: Set<number>; | ||||
| @ -82,7 +69,7 @@ interface MovingBlock { | ||||
|     /** 目标纵坐标 */ | ||||
|     y: number; | ||||
|     /** 渲染信息 */ | ||||
|     render: LayerRenderableData; | ||||
|     render: RenderableData | AutotileRenderable; | ||||
|     /** 当前的纵深 */ | ||||
|     nowZ: number; | ||||
| } | ||||
| @ -124,10 +111,6 @@ export class Layer extends Container implements IRenderDestroyable { | ||||
|     layer?: FloorLayer; | ||||
|     /** 渲染数据 */ | ||||
|     renderData: number[] = []; | ||||
|     /** 可以直接被渲染的内容 */ | ||||
|     renderable: Map<number, LayerRenderableData> = new Map(); | ||||
|     /** 移动层中可以直接被渲染的内容 */ | ||||
|     movingRenderable: LayerMovingRenderable[] = []; | ||||
|     /** 自动元件的连接信息,键表示图块在渲染数据中的索引,值表示连接信息,是个8位二进制 */ | ||||
|     autotiles: Record<number, number> = {}; | ||||
|     /** 楼层宽度 */ | ||||
| @ -136,8 +119,6 @@ export class Layer extends Container implements IRenderDestroyable { | ||||
|     mapHeight: number = 0; | ||||
|     /** 每个图块的大小 */ | ||||
|     cellSize: number = 32; | ||||
|     /** moving层的缓存信息,从低位到高位依次是第1帧至第4帧 */ | ||||
|     movingCached: number = 0b0000; | ||||
| 
 | ||||
|     /** 背景图块 */ | ||||
|     background: AllNumbers = 0; | ||||
| @ -152,28 +133,33 @@ export class Layer extends Container implements IRenderDestroyable { | ||||
|         restWidth: 0 | ||||
|     }; | ||||
|     blockSize: number = core._WIDTH_; | ||||
| 
 | ||||
|     /** 正在移动的图块 */ | ||||
|     moving: MovingBlock[] = []; | ||||
|     /** 大怪物(大图块)信息,键是图块在渲染数据中的索引,值是大怪物所用图片 */ | ||||
|     bigImage: Map<number, BigImageData> = new Map(); | ||||
|     /** 大怪物渲染信息 */ | ||||
|     bigImages: Map<number, LayerMovingRenderable> = new Map(); | ||||
|     /** 移动层的渲染信息 */ | ||||
|     movingRenderable: LayerMovingRenderable[] = []; | ||||
|     /** 下一此渲染时是否需要更新移动层的渲染信息 */ | ||||
|     needUpdateMoving: boolean = false; | ||||
| 
 | ||||
|     constructor() { | ||||
|         super('absolute'); | ||||
| 
 | ||||
|         this.setHD(false); | ||||
|         // this.setHD(false);
 | ||||
|         this.setAntiAliasing(false); | ||||
|         this.size(core._PX_, core._PY_); | ||||
| 
 | ||||
|         this.staticMap.setHD(false); | ||||
|         this.staticMap.setAntiAliasing(false); | ||||
|         // this.staticMap.setAntiAliasing(false);
 | ||||
|         this.staticMap.withGameScale(false); | ||||
|         this.staticMap.size(core._PX_, core._PY_); | ||||
|         this.movingMap.setHD(false); | ||||
|         this.movingMap.setAntiAliasing(false); | ||||
|         // this.movingMap.setAntiAliasing(false);
 | ||||
|         this.movingMap.withGameScale(false); | ||||
|         this.movingMap.size(core._PX_, core._PY_); | ||||
|         this.backMap.setHD(false); | ||||
|         this.backMap.setAntiAliasing(false); | ||||
|         // this.backMap.setAntiAliasing(false);
 | ||||
|         this.backMap.withGameScale(false); | ||||
|         this.backMap.size(core._PX_, core._PY_); | ||||
|         this.main.setAntiAliasing(false); | ||||
| @ -211,7 +197,7 @@ export class Layer extends Container implements IRenderDestroyable { | ||||
|     generateBackground() { | ||||
|         const num = this.background; | ||||
| 
 | ||||
|         const data = this.getRenderableByNum(num); | ||||
|         const data = texture.getRenderable(num); | ||||
|         this.backImage = []; | ||||
|         if (!data) return; | ||||
| 
 | ||||
| @ -231,7 +217,7 @@ export class Layer extends Container implements IRenderDestroyable { | ||||
|             temp.withGameScale(false); | ||||
|             temp.size(w, h); | ||||
| 
 | ||||
|             const img = data.image; | ||||
|             const img = data.autotile ? data.image[0b11111111] : data.image; | ||||
|             tempCtx.drawImage(img, sx, sy, w, h, 0, 0, w, h); | ||||
|             const pattern = ctx.createPattern(temp.canvas, 'repeat'); | ||||
|             if (!pattern) continue; | ||||
| @ -256,8 +242,6 @@ export class Layer extends Container implements IRenderDestroyable { | ||||
|         y: number = 0, | ||||
|         calAutotile: boolean = true | ||||
|     ) { | ||||
|         console.trace(); | ||||
| 
 | ||||
|         if (data.length % width !== 0) { | ||||
|             logger.warn( | ||||
|                 8, | ||||
| @ -286,251 +270,38 @@ export class Layer extends Container implements IRenderDestroyable { | ||||
|             } | ||||
|         } | ||||
|         if (calAutotile) this.calAutotiles(x, y, width, height); | ||||
|         this.updateBigImages(x, y, width, height); | ||||
|         this.updateRenderableData(x, y, width, height); | ||||
|         // this.updateBigImages(x, y, width, height);
 | ||||
|         // this.updateRenderableData(x, y, width, height);
 | ||||
|         this.updateBlocks(x, y, width, height); | ||||
|         this.update(this); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * 更新给定区域内的大怪物信息 | ||||
|      * 更新大怪物的渲染信息 | ||||
|      */ | ||||
|     updateBigImages(x: number, y: number, width: number, height: number) { | ||||
|         const ex = Math.min(x + width, this.mapWidth); | ||||
|         const ey = Math.min(y + height, this.mapHeight); | ||||
|         const size = this.blockSize; | ||||
|         const images = this.bigImage; | ||||
|         const ex = x + width; | ||||
|         const ey = y + height; | ||||
|         const w = this.mapWidth; | ||||
|         const data = this.renderData; | ||||
|         const enemys = enemys_fcae963b_31c9_42b4_b48c_bb48d09f3f80; | ||||
|         const icons = icons_4665ee12_3a1f_44a4_bea3_0fccba634dc1; | ||||
|         const map = maps_90f36752_8815_4be8_b32b_d7fad1d0542e; | ||||
| 
 | ||||
|         for (let nx = Math.max(x, 0); nx < ex; nx++) { | ||||
|             for (let ny = Math.max(y, 0); ny < ey; ny++) { | ||||
|                 const index = ny * size + nx; | ||||
|                 images.delete(index); | ||||
|         for (let nx = x; nx < ex; nx++) { | ||||
|             for (let ny = y; ny < ey; ny++) { | ||||
|                 const index = ny * w + nx; | ||||
|                 this.bigImages.delete(index); | ||||
|                 const num = data[index]; | ||||
| 
 | ||||
|                 // 如果不存在图块或图块是空气墙,跳过
 | ||||
|                 if (num === 0 || num === 17 || num >= 10000) continue; | ||||
| 
 | ||||
|                 let { cls, id, bigImage, faceIds } = | ||||
|                     map[num as Exclude<AllNumbers, 0>]; | ||||
|                 if (cls === 'enemys' || cls === 'enemy48') { | ||||
|                     // 怪物需要特殊处理,因为它的大怪物信息不在 maps 里面
 | ||||
|                     ({ bigImage, faceIds } = enemys[id as EnemyIds]); | ||||
|                 } | ||||
|                 if (bigImage) { | ||||
|                     const image = core.material.images.images[bigImage]; | ||||
|                     if (!image) { | ||||
|                         logger.warn( | ||||
|                             10, | ||||
|                             `Cannot resolve big image of enemy '${id}'.` | ||||
|                         ); | ||||
|                         continue; | ||||
|                     } | ||||
|                     let line = 0; | ||||
|                     if (faceIds) { | ||||
|                         const arr = ['down', 'left', 'right', 'up']; | ||||
|                         for (let i = 0; i < arr.length; i++) { | ||||
|                             if (faceIds[arr[i] as Dir] === id) { | ||||
|                                 line = i; | ||||
|                                 break; | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|                     const totalLines = image.width / image.height >= 2 ? 1 : 4; | ||||
|                     images.set(index, { | ||||
|                         image, | ||||
|                         line, | ||||
|                         totalLines | ||||
|                     }); | ||||
|                 } | ||||
|                 if (cls === 'enemy48' || cls === 'npc48') { | ||||
|                     // 32 * 48 视为大怪物
 | ||||
|                     const img = core.material.images[cls]; | ||||
|                     const totalLines = Math.round(img.height / 48); | ||||
|                     // @ts-ignore
 | ||||
|                     const line = icons[cls][id]; | ||||
|                     images.set(index, { | ||||
|                         image: img, | ||||
|                         line, | ||||
|                         totalLines | ||||
|                     }); | ||||
|                 } | ||||
|                 const renderable = texture.getRenderable(num); | ||||
|                 if (!renderable || !renderable.bigImage) continue; | ||||
|                 this.bigImages.set(index, { | ||||
|                     ...renderable, | ||||
|                     x: nx, | ||||
|                     y: ny, | ||||
|                     zIndex: ny | ||||
|                 }); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * 根据图块数字获取渲染信息 | ||||
|      * @param num 图块数字 | ||||
|      */ | ||||
|     getRenderableByNum(num: number): LayerRenderableData | null { | ||||
|         const cell = this.cellSize; | ||||
|         const map = maps_90f36752_8815_4be8_b32b_d7fad1d0542e; | ||||
|         const icons = icons_4665ee12_3a1f_44a4_bea3_0fccba634dc1; | ||||
|         const auto = texture.autotile; | ||||
| 
 | ||||
|         if (num >= 10000) { | ||||
|             // 额外素材
 | ||||
|             const offset = core.getTilesetOffset(num); | ||||
|             if (!offset) return null; | ||||
|             const { image, x, y } = offset; | ||||
|             return { | ||||
|                 image: core.material.images.tilesets[image], | ||||
|                 frame: 1, | ||||
|                 render: [[x * cell, y * cell, cell, cell]] | ||||
|             }; | ||||
|         } else { | ||||
|             if (num === 0 || num === 17) return null; | ||||
|             const { cls, id } = map[num as Exclude<AllNumbers, 0>]; | ||||
|             // 普通素材
 | ||||
|             if (cls !== 'autotile') { | ||||
|                 const image = | ||||
|                     core.material.images[ | ||||
|                         cls as Exclude<Cls, 'tileset' | 'autotile'> | ||||
|                     ]; | ||||
|                 const frame = core.getAnimateFrames(cls); | ||||
|                 // @ts-ignore
 | ||||
|                 const offset = (icons[cls][id] as number) * cell; | ||||
|                 const render: [number, number, number, number][] = [ | ||||
|                     [0, offset, cell, cell] | ||||
|                 ]; | ||||
|                 if (frame === 2) { | ||||
|                     render.push([cell, offset, cell, cell]); | ||||
|                 } | ||||
|                 if (frame === 4) { | ||||
|                     render.push( | ||||
|                         [cell, offset, cell, cell], | ||||
|                         [cell * 2, offset, cell, cell], | ||||
|                         [cell * 3, offset, cell, cell] | ||||
|                     ); | ||||
|                 } | ||||
|                 return { | ||||
|                     image, | ||||
|                     frame, | ||||
|                     render | ||||
|                 }; | ||||
|             } else { | ||||
|                 // 自动元件
 | ||||
|                 const tile = auto[num as AllNumbersOf<'autotile'>]; | ||||
|                 const image = tile.cache[0b11111111]; | ||||
|                 const frame = tile.frame; | ||||
|                 const render: [number, number, number, number][] = [ | ||||
|                     [0, 0, cell, cell] | ||||
|                 ]; | ||||
|                 if (frame === 4) { | ||||
|                     render.push( | ||||
|                         [cell, 0, cell, cell], | ||||
|                         [cell * 2, 0, cell, cell], | ||||
|                         [cell * 3, 0, cell, cell] | ||||
|                     ); | ||||
|                 } | ||||
|                 return { | ||||
|                     image, | ||||
|                     frame, | ||||
|                     render | ||||
|                 }; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * 根据索引及横坐标获取对应的位置图块的渲染信息 | ||||
|      * @param x 横坐标 | ||||
|      * @param index 索引 | ||||
|      */ | ||||
|     getRenderableData( | ||||
|         x: number, | ||||
|         _y: number, | ||||
|         index: number | ||||
|     ): LayerRenderableData | LayerMovingRenderable | null { | ||||
|         const data = this.renderData; | ||||
|         const cell = this.cellSize; | ||||
|         const map = maps_90f36752_8815_4be8_b32b_d7fad1d0542e; | ||||
|         const icons = icons_4665ee12_3a1f_44a4_bea3_0fccba634dc1; | ||||
|         const auto = texture.autotile; | ||||
| 
 | ||||
|         const bigImage = this.bigImage.get(index); | ||||
|         if (bigImage) { | ||||
|             // 对于大怪物
 | ||||
|             const img = bigImage.image; | ||||
|             const w = Math.round(img.width / 4); | ||||
|             const h = Math.round(img.height / bigImage.totalLines); | ||||
|             const y = h * bigImage.line; | ||||
|             return { | ||||
|                 image: bigImage.image, | ||||
|                 frame: 4, | ||||
|                 render: [ | ||||
|                     [0, y, w, h], | ||||
|                     [w, y, w, h], | ||||
|                     [w * 2, y, w, h], | ||||
|                     [w * 3, y, w, h] | ||||
|                 ], | ||||
|                 x: x, | ||||
|                 y: y, | ||||
|                 zIndex: y | ||||
|             }; | ||||
|         } else { | ||||
|             // 对于普通图块
 | ||||
|             const num = data[index]; | ||||
|             if (num >= 10000) { | ||||
|                 // 额外素材
 | ||||
|                 return this.getRenderableByNum(num); | ||||
|             } else { | ||||
|                 if (num === 0 || num === 17) return null; | ||||
|                 const { cls } = map[num as Exclude<AllNumbers, 0>]; | ||||
|                 // 普通素材
 | ||||
|                 if (cls !== 'autotile') { | ||||
|                     return this.getRenderableByNum(num); | ||||
|                 } else { | ||||
|                     // 自动元件
 | ||||
|                     const tile = auto[num as AllNumbersOf<'autotile'>]; | ||||
|                     const link = this.autotiles[index]; | ||||
|                     const image = tile.cache[link]; | ||||
|                     const frame = tile.frame; | ||||
|                     const render: [number, number, number, number][] = [ | ||||
|                         [0, 0, cell, cell] | ||||
|                     ]; | ||||
|                     if (frame === 4) { | ||||
|                         render.push( | ||||
|                             [cell, 0, cell, cell], | ||||
|                             [cell * 2, 0, cell, cell], | ||||
|                             [cell * 3, 0, cell, cell] | ||||
|                         ); | ||||
|                     } | ||||
|                     return { | ||||
|                         image, | ||||
|                         frame, | ||||
|                         render | ||||
|                     }; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * 更新给定区域内的绘制信息 | ||||
|      */ | ||||
|     updateRenderableData(x: number, y: number, width: number, height: number) { | ||||
|         const ex = Math.min(x + width, this.mapWidth); | ||||
|         const ey = Math.min(y + height, this.mapHeight); | ||||
|         const size = this.blockSize; | ||||
| 
 | ||||
|         for (let nx = Math.max(x, 0); nx < ex; nx++) { | ||||
|             for (let ny = Math.max(y, 0); ny < ey; ny++) { | ||||
|                 const index = ny * size + nx; | ||||
|                 const bigImage = this.bigImage.get(index); | ||||
|                 const data = this.getRenderableData(nx, ny, index); | ||||
|                 if (!data) continue; | ||||
|                 if (bigImage) { | ||||
|                     this.movingRenderable.push(data as LayerMovingRenderable); | ||||
|                 } else { | ||||
|                     this.renderable.set(index, data); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         this.needUpdateMoving = true; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @ -590,12 +361,11 @@ export class Layer extends Container implements IRenderDestroyable { | ||||
| 
 | ||||
|         for (let nx = x; nx < ex; nx++) { | ||||
|             for (let ny = y; ny < ey; ny++) { | ||||
|                 if (nx > w || ny > w) continue; | ||||
|                 const index = nx + ny * h; | ||||
|                 if (nx > w || ny > h) continue; | ||||
|                 const index = nx + ny * w; | ||||
|                 const num = data[index]; | ||||
|                 // 特判空气墙与空图块
 | ||||
|                 if (num === 17 || num >= 10000 || num <= 0) continue; | ||||
|                 console.log(this); | ||||
|                 if (num === 0 || num === 17 || num >= 10000) continue; | ||||
| 
 | ||||
|                 const info = map[num as Exclude<AllNumbers, 0>]; | ||||
|                 const { cls } = info; | ||||
| @ -683,8 +453,8 @@ export class Layer extends Container implements IRenderDestroyable { | ||||
|         const size = this.blockSize; | ||||
| 
 | ||||
|         this.blockData = { | ||||
|             width: Math.floor(this.mapWidth / size), | ||||
|             height: Math.floor(this.mapHeight / size), | ||||
|             width: Math.ceil(this.mapWidth / size), | ||||
|             height: Math.ceil(this.mapHeight / size), | ||||
|             restWidth: this.mapWidth % size, | ||||
|             restHeight: this.mapHeight % size | ||||
|         }; | ||||
| @ -808,21 +578,31 @@ export class Layer extends Container implements IRenderDestroyable { | ||||
|      * @param camera 摄像机 | ||||
|      */ | ||||
|     calNeedRender(camera: Camera): NeedRenderData { | ||||
|         const w = core._WIDTH_; | ||||
|         const h = core._HEIGHT_; | ||||
|         const size = this.blockSize; | ||||
|         const { width } = this.blockData; | ||||
|         const cell = this.cellSize; | ||||
|         const w = (core._WIDTH_ * cell) / 2; | ||||
|         const h = (core._HEIGHT_ * cell) / 2; | ||||
|         const size = this.blockSize; | ||||
| 
 | ||||
|         const [x1, y1] = Camera.transformed(camera, 0, 0); | ||||
|         const [x2, y2] = Camera.transformed(camera, w * cell, 0); | ||||
|         const [x3, y3] = Camera.transformed(camera, w * cell, h * cell); | ||||
|         const [x4, y4] = Camera.transformed(camera, 0, h * cell); | ||||
|         // -1是因为宽度是core._PX_,从0开始的话,末尾索引就是core._PX_ - 1
 | ||||
|         const [x1, y1] = Camera.untransformed(camera, -w, -h); | ||||
|         const [x2, y2] = Camera.untransformed(camera, w - 1, -h); | ||||
|         const [x3, y3] = Camera.untransformed(camera, w - 1, h - 1); | ||||
|         const [x4, y4] = Camera.untransformed(camera, -w, h - 1); | ||||
| 
 | ||||
|         const res: Set<number> = new Set(); | ||||
|         /** 一个纵坐标对应的所有横坐标,用于填充 */ | ||||
|         const xyMap: Map<number, number[]> = new Map(); | ||||
| 
 | ||||
|         const pushXY = (x: number, y: number) => { | ||||
|             let arr = xyMap.get(y); | ||||
|             if (!arr) { | ||||
|                 arr = []; | ||||
|                 xyMap.set(y, arr); | ||||
|             } | ||||
|             arr.push(x); | ||||
|             return arr; | ||||
|         }; | ||||
| 
 | ||||
|         [ | ||||
|             [x1, y1, x2, y2], | ||||
|             [x2, y2, x3, y3], | ||||
| @ -837,43 +617,26 @@ export class Layer extends Container implements IRenderDestroyable { | ||||
|             const dy = ty - fy; | ||||
|             const k = dy / dx; | ||||
| 
 | ||||
|             // console.log(fx, fy, tx, ty);
 | ||||
| 
 | ||||
|             // 斜率无限的时候,竖直
 | ||||
|             if (!isFinite(k)) { | ||||
|                 const min = k < 0 ? ty : fy; | ||||
|                 const max = k < 0 ? fy : ty; | ||||
|                 const [x, y] = this.getBlockXYByLoc(fx, min); | ||||
| 
 | ||||
|                 // 在地图左侧或右侧时,将每个纵坐标对应的横坐标填充为0
 | ||||
| 
 | ||||
|                 const p = x < 0 ? 0 : x >= width ? width - 1 : x; | ||||
|                 const [, ey] = this.getBlockXYByLoc(fx, max); | ||||
|                 for (let i = y; i <= ey; i++) { | ||||
|                     let arr = xyMap.get(i); | ||||
|                     if (!arr) { | ||||
|                         arr = []; | ||||
|                         xyMap.set(i, arr); | ||||
|                     } | ||||
|                     arr.push(p); | ||||
|                     pushXY(x, i); | ||||
|                 } | ||||
|                 // console.log(y, ey, p);
 | ||||
| 
 | ||||
|                 return; | ||||
|             } | ||||
| 
 | ||||
|             const [fbx, fby] = this.getBlockXYByLoc(fx, fy); | ||||
| 
 | ||||
|             // 当斜率为0时
 | ||||
|             if (glMatrix.equals(k, 0)) { | ||||
|                 const [ex] = this.getBlockXYByLoc(tx, fy); | ||||
|                 let arr = xyMap.get(fby); | ||||
|                 if (!arr) { | ||||
|                     arr = []; | ||||
|                     xyMap.set(fby, arr); | ||||
|                 } | ||||
|                 arr.push(fbx, ex); | ||||
|                 // console.log(fbx, ex);
 | ||||
| 
 | ||||
|                 pushXY(fby, fbx).push(ex); | ||||
|                 return; | ||||
|             } | ||||
| 
 | ||||
| @ -881,54 +644,52 @@ export class Layer extends Container implements IRenderDestroyable { | ||||
|             if (Math.abs(k) >= 1) { | ||||
|                 // 斜率大于一,y方向递增
 | ||||
|                 const d = Math.sign(dy) * size; | ||||
|                 const f = dx > 0 ? fby * size : (fby + 1) * size; | ||||
|                 const f = fby * size; | ||||
|                 const dir = dy > 0; | ||||
| 
 | ||||
|                 const ex = Math.floor(tx / size); | ||||
|                 const ey = Math.floor(ty / size); | ||||
|                 pushXY(ex, ey); | ||||
| 
 | ||||
|                 let now = f; | ||||
|                 let last = fbx; | ||||
|                 let ny = fby; | ||||
|                 do { | ||||
|                     const bx = Math.floor(fx + (now - fy) / k); | ||||
|                     let arr = xyMap.get(ny); | ||||
|                     if (!arr) { | ||||
|                         arr = []; | ||||
|                         xyMap.set(ny, arr); | ||||
|                     } | ||||
|                     const bx = Math.floor((fx + (now - fy) / k) / size); | ||||
|                     pushXY(bx, ny); | ||||
|                     if (bx !== last) { | ||||
|                         arr.push(last); | ||||
|                         if (dir) pushXY(bx, ny - Math.sign(dy)); | ||||
|                         else pushXY(last, ny); | ||||
|                     } | ||||
|                     arr.push(bx); | ||||
| 
 | ||||
|                     last = bx; | ||||
|                     ny++; | ||||
|                 } while (dir ? (now += d) < ty : (now += d) > ty); | ||||
|                     ny += Math.sign(dy); | ||||
|                     now += d; | ||||
|                 } while (dir ? ny <= ey : ny >= ey); | ||||
|             } else { | ||||
|                 // 斜率小于一,x方向递增
 | ||||
|                 const d = Math.sign(dx) * size; | ||||
|                 const f = dx > 0 ? fbx * size : (fbx + 1) * size; | ||||
|                 const f = fbx * size; | ||||
|                 const dir = dx > 0; | ||||
| 
 | ||||
|                 const ex = Math.floor(tx / size); | ||||
|                 const ey = Math.floor(ty / size); | ||||
|                 pushXY(ex, ey); | ||||
| 
 | ||||
|                 let now = f; | ||||
|                 let last = fby; | ||||
|                 let nx = fbx; | ||||
|                 do { | ||||
|                     const by = Math.floor(fy + k * (now - fx)); | ||||
| 
 | ||||
|                     const by = Math.floor((fy + k * (now - fx)) / size); | ||||
|                     if (by !== last) { | ||||
|                         let arr = xyMap.get(last); | ||||
|                         if (!arr) { | ||||
|                             arr = []; | ||||
|                             xyMap.set(last, arr); | ||||
|                         } | ||||
|                         arr.push(nx); | ||||
|                         if (dir) pushXY(nx - Math.sign(dx), by); | ||||
|                         else pushXY(nx, last); | ||||
|                     } | ||||
|                     let arr = xyMap.get(nx); | ||||
|                     if (!arr) { | ||||
|                         arr = []; | ||||
|                         xyMap.set(by, arr); | ||||
|                     } | ||||
|                     arr.push(nx); | ||||
|                     nx++; | ||||
|                 } while (dir ? (now += d) < tx : (now += d) > tx); | ||||
|                     pushXY(nx, by); | ||||
|                     last = by; | ||||
|                     nx += Math.sign(dx); | ||||
|                     now += d; | ||||
|                 } while (dir ? nx <= ex : nx >= ex); | ||||
|             } | ||||
|         }); | ||||
| 
 | ||||
| @ -939,7 +700,8 @@ export class Layer extends Container implements IRenderDestroyable { | ||||
|             if (x.length === 1) { | ||||
|                 const index = y * bw + x[0]; | ||||
| 
 | ||||
|                 back.push([x[0], y]); | ||||
|                 if (!back.some(v => v[0] === x[0] && v[1] === y)) | ||||
|                     back.push([x[0], y]); | ||||
|                 if (index < 0 || index >= bw * bh) return; | ||||
|                 res.add(index); | ||||
|             } | ||||
| @ -949,17 +711,44 @@ export class Layer extends Container implements IRenderDestroyable { | ||||
|             for (let i = min; i <= max; i++) { | ||||
|                 const index = y * bw + i; | ||||
| 
 | ||||
|                 back.push([i, y]); | ||||
|                 if (!back.some(v => v[0] === i && v[1] === y)) | ||||
|                     back.push([i, y]); | ||||
|                 if (index < 0 || index >= bw * bh) continue; | ||||
|                 res.add(index); | ||||
|             } | ||||
|         }); | ||||
| 
 | ||||
|         // console.log([...res], xyMap);
 | ||||
| 
 | ||||
|         return { res, back }; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * 更新移动层的渲染信息 | ||||
|      */ | ||||
|     updateMovingRenderable() { | ||||
|         this.movingRenderable = []; | ||||
|         this.movingRenderable.push(...this.bigImages.values()); | ||||
|         this.moving.forEach(v => { | ||||
|             if (!v.render.autotile) { | ||||
|                 this.movingRenderable.push({ | ||||
|                     ...v.render, | ||||
|                     x: v.x, | ||||
|                     y: v.y, | ||||
|                     zIndex: v.nowZ | ||||
|                 }); | ||||
|             } else { | ||||
|                 this.movingRenderable.push({ | ||||
|                     ...v.render, | ||||
|                     x: v.x, | ||||
|                     y: v.y, | ||||
|                     zIndex: v.nowZ, | ||||
|                     image: v.render.image[0b00000000], | ||||
|                     autotile: false | ||||
|                 }); | ||||
|             } | ||||
|         }); | ||||
|         this.movingRenderable.sort((a, b) => a.zIndex - b.zIndex); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * 渲染当前地图 | ||||
|      */ | ||||
| @ -968,6 +757,8 @@ export class Layer extends Container implements IRenderDestroyable { | ||||
|         this.movingMap.clear(); | ||||
|         this.backMap.clear(); | ||||
| 
 | ||||
|         if (this.needUpdateMoving) this.updateMovingRenderable(); | ||||
| 
 | ||||
|         this.renderBack(camera, need); | ||||
|         this.renderStatic(camera, need); | ||||
|         this.renderMoving(camera); | ||||
| @ -981,10 +772,9 @@ export class Layer extends Container implements IRenderDestroyable { | ||||
|     protected renderBack(camera: Camera, need: NeedRenderData) { | ||||
|         const cell = this.cellSize; | ||||
|         const frame = (RenderItem.animatedFrame % 4) + 1; | ||||
|         const { width } = this.blockData; | ||||
|         const blockSize = this.blockSize; | ||||
|         const { back } = need; | ||||
|         const { ctx, canvas } = this.backMap; | ||||
|         const { ctx } = this.backMap; | ||||
| 
 | ||||
|         const mat = camera.mat; | ||||
|         const a = mat[0]; | ||||
| @ -1020,16 +810,14 @@ export class Layer extends Container implements IRenderDestroyable { | ||||
|      */ | ||||
|     protected renderStatic(camera: Camera, need: NeedRenderData) { | ||||
|         const cell = this.cellSize; | ||||
|         const renderable = this.renderable; | ||||
|         const frame = (RenderItem.animatedFrame % 4) + 1; | ||||
|         const { width } = this.blockData; | ||||
|         const blockSize = this.blockSize; | ||||
|         const { ctx, canvas } = this.staticMap; | ||||
|         const { ctx } = this.staticMap; | ||||
| 
 | ||||
|         ctx.save(); | ||||
| 
 | ||||
|         const { res: render } = need; | ||||
|         // console.log(render);
 | ||||
|         const mat = camera.mat; | ||||
|         const a = mat[0]; | ||||
|         const b = mat[1]; | ||||
| @ -1061,8 +849,8 @@ export class Layer extends Container implements IRenderDestroyable { | ||||
|                 return; | ||||
|             } | ||||
| 
 | ||||
|             const ex = sx + blockSize; | ||||
|             const ey = sy + blockSize; | ||||
|             const ex = Math.min(sx + blockSize, this.mapWidth); | ||||
|             const ey = Math.min(sy + blockSize, this.mapHeight); | ||||
| 
 | ||||
|             const temp = new MotaOffscreenCanvas2D(); | ||||
|             temp.setAntiAliasing(false); | ||||
| @ -1074,17 +862,28 @@ export class Layer extends Container implements IRenderDestroyable { | ||||
|             for (let nx = sx; nx < ex; nx++) { | ||||
|                 for (let ny = sy; ny < ey; ny++) { | ||||
|                     const blockIndex = nx + ny * this.mapWidth; | ||||
|                     const data = renderable.get(blockIndex); | ||||
|                     if (!data) continue; | ||||
| 
 | ||||
|                     const num = this.renderData[blockIndex]; | ||||
|                     if (num === 0 || num === 17) continue; | ||||
|                     const data = texture.getRenderable(num); | ||||
|                     if (!data || data.bigImage) continue; | ||||
|                     const f = frame % data.frame; | ||||
|                     const i = frame === 4 && data.frame === 3 ? 1 : f; | ||||
|                     const [sx, sy, w, h] = data.render[i]; | ||||
|                     const px = nx * cell; | ||||
|                     const py = ny * cell; | ||||
|                     const image = data.image; | ||||
| 
 | ||||
|                     temp.ctx.drawImage(image, sx, sy, w, h, px, py, w, h); | ||||
|                     const i = | ||||
|                         data.animate === -1 | ||||
|                             ? frame === 4 && data.frame === 3 | ||||
|                                 ? 1 | ||||
|                                 : f | ||||
|                             : data.animate; | ||||
|                     const [isx, isy, w, h] = data.render[i]; | ||||
|                     const px = (nx - sx) * cell; | ||||
|                     const py = (ny - sy) * cell; | ||||
|                     const { image, autotile } = data; | ||||
|                     if (!autotile) { | ||||
|                         temp.ctx.drawImage(image, isx, isy, w, h, px, py, w, h); | ||||
|                     } else { | ||||
|                         const link = this.autotiles[blockIndex]; | ||||
|                         const i = image[link]; | ||||
|                         temp.ctx.drawImage(i, isx, isy, w, h, px, py, w, h); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             ctx.drawImage( | ||||
| @ -1126,10 +925,6 @@ export class Layer extends Container implements IRenderDestroyable { | ||||
|         const r = | ||||
|             Math.max(a, b, c, d) ** 2 * Math.max(core._PX_, core._PY_) * 2; | ||||
| 
 | ||||
|         this.movingRenderable.sort((a, b) => { | ||||
|             return a.zIndex - b.zIndex; | ||||
|         }); | ||||
| 
 | ||||
|         this.movingRenderable.forEach(v => { | ||||
|             const { x, y, image, frame: blockFrame, render } = v; | ||||
|             const f = frame % 4; | ||||
|  | ||||
| @ -149,11 +149,11 @@ Mota.require('var', 'hook').once('reset', () => { | ||||
|     const camera = render.camera; | ||||
|     render.mount(); | ||||
| 
 | ||||
|     layer.zIndex = 2; | ||||
|     bgLayer.zIndex = 1; | ||||
|     layer.setZIndex(2); | ||||
|     bgLayer.setZIndex(1); | ||||
|     render.appendChild([layer, bgLayer]); | ||||
|     layer.bindThis('event', true); | ||||
|     bgLayer.bindThis('bg', true); | ||||
|     layer.bindThis('event'); | ||||
|     bgLayer.bindThis('bg'); | ||||
|     bgLayer.setBackground(650); | ||||
| 
 | ||||
|     const ani = new Animation(); | ||||
| @ -161,17 +161,35 @@ Mota.require('var', 'hook').once('reset', () => { | ||||
|     ani.ticker.add(() => { | ||||
|         camera.reset(); | ||||
|         camera.rotate((ani.angle / 180) * Math.PI); | ||||
|         camera.move(240, 240); | ||||
|         camera.move(ani.x, ani.y); | ||||
|         camera.scale(ani.size); | ||||
|         render.update(render); | ||||
|     }); | ||||
| 
 | ||||
|     camera.rotate(Math.PI * 1.23); | ||||
|     camera.move(230, 380); | ||||
|     camera.scale(0.7); | ||||
|     render.update(); | ||||
| 
 | ||||
|     // sleep(2000).then(() => {
 | ||||
|     //     render.update();
 | ||||
|     // });
 | ||||
| 
 | ||||
|     sleep(1000).then(() => { | ||||
|         ani.mode(hyper('sin', 'out')).time(100).absolute().rotate(30); | ||||
|         ani.mode(hyper('sin', 'out')) | ||||
|             .time(100) | ||||
|             .absolute() | ||||
|             .rotate(30) | ||||
|             .move(240, 240); | ||||
|         sleep(100).then(() => { | ||||
|             ani.time(3000).rotate(0); | ||||
|         }); | ||||
|         sleep(3100).then(() => { | ||||
|             ani.time(5000).mode(hyper('tan', 'in-out')).rotate(3600); | ||||
|             ani.time(5000) | ||||
|                 .mode(hyper('sin', 'in-out')) | ||||
|                 .rotate(360) | ||||
|                 .move(200, 480) | ||||
|                 .scale(0.5); | ||||
|         }); | ||||
|         // ani.mode(shake2(5, hyper('sin', 'in-out')), true)
 | ||||
|         //     .time(5000)
 | ||||
|  | ||||
| @ -25,6 +25,10 @@ export class Sprite extends RenderItem implements ICanvasCachedRenderItem { | ||||
|         camera: Camera | ||||
|     ): void { | ||||
|         this.emit('beforeRender'); | ||||
|         if (this.needUpdate) { | ||||
|             this.cache(this.writing); | ||||
|             this.needUpdate = false; | ||||
|         } | ||||
|         withCacheRender(this, canvas, ctx, camera, canvas => { | ||||
|             this.renderFn(canvas, camera); | ||||
|         }); | ||||
|  | ||||
							
								
								
									
										2
									
								
								src/types/core.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								src/types/core.d.ts
									
									
									
									
										vendored
									
									
								
							| @ -1419,6 +1419,8 @@ interface MapDataOf<T extends keyof NumberToId> { | ||||
|     bigImage?: ImageIds; | ||||
| 
 | ||||
|     faceIds?: Record<Dir, AllIds>; | ||||
| 
 | ||||
|     animate?: number; | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user