mirror of
				https://github.com/unanmed/HumanBreak.git
				synced 2025-11-01 04:42:58 +08:00 
			
		
		
		
	feat: 勇士进入传送门效果
This commit is contained in:
		
							parent
							
								
									292e17ed53
								
							
						
					
					
						commit
						0568542cec
					
				| @ -13,6 +13,7 @@ type HeroMovingStatus = 'stop' | 'moving' | 'moving-as'; | ||||
| interface HeroRenderEvent { | ||||
|     stepEnd: []; | ||||
|     moveTick: [x: number, y: number]; | ||||
|     append: [renderable: LayerMovingRenderable[]]; | ||||
| } | ||||
| 
 | ||||
| export class HeroRenderer | ||||
| @ -40,6 +41,7 @@ export class HeroRenderer | ||||
|     /** 勇士移动速度 */ | ||||
|     speed: number = 100; | ||||
|     /** 当前勇士朝向 */ | ||||
|     // todo: 删了这个属性
 | ||||
|     dir: Dir = 'down'; | ||||
| 
 | ||||
|     /** 勇士移动定时器id */ | ||||
| @ -47,7 +49,7 @@ export class HeroRenderer | ||||
|     /** 上一次帧数切换的时间 */ | ||||
|     private lastFrameTime: number = 0; | ||||
|     /** 当前的移动方向 */ | ||||
|     private moveDir: Move2 = 'down'; | ||||
|     moveDir: Move2 = 'down'; | ||||
|     /** 上一步走到格子上的时间 */ | ||||
|     private lastStepTime: number = 0; | ||||
|     /** 执行当前步移动的Promise */ | ||||
| @ -58,7 +60,7 @@ export class HeroRenderer | ||||
|      * 这一步的移动方向,与{@link moveDir}不同的是,在这一步走完之前,它都不会变, | ||||
|      * 当这一步走完之后,才会将其设置为{@link moveDir}的值 | ||||
|      */ | ||||
|     private stepDir: Dir2 = 'down'; | ||||
|     stepDir: Dir2 = 'down'; | ||||
|     /** 每步的格子增量 */ | ||||
|     private stepDelta: Loc = { x: 0, y: 1 }; | ||||
|     /** 动画显示的方向,用于适配后退 */ | ||||
| @ -354,7 +356,10 @@ export class HeroRenderer | ||||
|     } | ||||
| 
 | ||||
|     onMovingUpdate(layer: Layer, renderable: LayerMovingRenderable[]): void { | ||||
|         if (this.renderable) renderable.push(this.renderable); | ||||
|         if (this.renderable) { | ||||
|             renderable.push(this.renderable); | ||||
|             this.emit('append', renderable); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -1,4 +1,4 @@ | ||||
| import { has } from '@/plugin/game/utils'; | ||||
| import { backDir, has } from '@/plugin/game/utils'; | ||||
| import { loading } from '../game'; | ||||
| 
 | ||||
| export namespace BluePalace { | ||||
| @ -90,6 +90,16 @@ export namespace BluePalace { | ||||
|         toDir: Dir; | ||||
|     } | ||||
| 
 | ||||
|     export interface PortalTo { | ||||
|         x: number; | ||||
|         y: number; | ||||
|         dir: Dir; | ||||
|     } | ||||
| 
 | ||||
|     type PortalMap = Map<FloorIds, Map<number, Partial<Record<Dir, PortalTo>>>>; | ||||
| 
 | ||||
|     export const portalMap: PortalMap = new Map(); | ||||
| 
 | ||||
|     export const portals: Partial<Record<FloorIds, Portal[]>> = { | ||||
|         MT75: [ | ||||
|             { fx: 7, fy: 7, dir: 'left', tx: 9, ty: 9, toDir: 'down' }, | ||||
| @ -101,7 +111,103 @@ export namespace BluePalace { | ||||
|     }; | ||||
|     loading.once('coreInit', initPortals); | ||||
| 
 | ||||
|     function generatePortalMap() { | ||||
|         const delta: Record<Dir, [[number, number], [number, number]]> = { | ||||
|             // 方向:[正向, 逆向]<进出>
 | ||||
|             left: [ | ||||
|                 [0, 0], | ||||
|                 [-1, 0] | ||||
|             ], | ||||
|             down: [ | ||||
|                 [0, 0], | ||||
|                 [0, -1] | ||||
|             ], | ||||
|             right: [ | ||||
|                 [0, 0], | ||||
|                 [1, 0] | ||||
|             ], | ||||
|             up: [ | ||||
|                 [0, 0], | ||||
|                 [0, 1] | ||||
|             ] | ||||
|         }; | ||||
|         for (const [floor, p] of Object.entries(portals)) { | ||||
|             const width = core.floors[floor as FloorIds].width; | ||||
|             const map = new Map<number, Partial<Record<Dir, PortalTo>>>(); | ||||
|             portalMap.set(floor as FloorIds, map); | ||||
| 
 | ||||
|             // 正向映射
 | ||||
|             p.forEach(v => { | ||||
|                 const [[fdx, fdy], [tdx, tdy]] = delta[v.dir]; | ||||
|                 const [[toFdx, toFdy], [toTdx, toTdy]] = | ||||
|                     delta[backDir(v.toDir)]; | ||||
|                 const fx = v.fx + fdx; | ||||
|                 const fy = v.fy + fdy; | ||||
|                 const tx = v.fx + tdx; | ||||
|                 const ty = v.fy + tdy; | ||||
|                 const index = fx + fy * width; | ||||
|                 const backIndex = tx + ty * width; | ||||
|                 let data = map.get(index); | ||||
|                 let backData = map.get(backIndex); | ||||
|                 if (!data) { | ||||
|                     data = {}; | ||||
|                     map.set(index, data); | ||||
|                 } | ||||
|                 if (!backData) { | ||||
|                     backData = {}; | ||||
|                     map.set(backIndex, backData); | ||||
|                 } | ||||
| 
 | ||||
|                 data[v.dir] = { | ||||
|                     x: v.tx + toFdx, | ||||
|                     y: v.ty + toFdy, | ||||
|                     dir: backDir(v.toDir) | ||||
|                 }; | ||||
|                 backData[backDir(v.dir)] = { | ||||
|                     x: v.tx + toTdx, | ||||
|                     y: v.ty + toTdy, | ||||
|                     dir: v.toDir | ||||
|                 }; | ||||
|             }); | ||||
|             // 逆向映射
 | ||||
|             p.forEach(v => { | ||||
|                 const [[fdx, fdy], [tdx, tdy]] = delta[backDir(v.toDir)]; | ||||
|                 const [[toFdx, toFdy], [toTdx, toTdy]] = delta[v.dir]; | ||||
|                 const fx = v.tx + fdx; | ||||
|                 const fy = v.ty + fdy; | ||||
|                 const tx = v.tx + tdx; | ||||
|                 const ty = v.ty + tdy; | ||||
|                 const index = fx + fy * width; | ||||
|                 const backIndex = tx + ty * width; | ||||
| 
 | ||||
|                 let data = map.get(index); | ||||
|                 let backData = map.get(backIndex); | ||||
|                 if (!data) { | ||||
|                     data = {}; | ||||
|                     map.set(index, data); | ||||
|                 } | ||||
|                 if (!backData) { | ||||
|                     backData = {}; | ||||
|                     map.set(backIndex, backData); | ||||
|                 } | ||||
| 
 | ||||
|                 data[v.toDir] = { | ||||
|                     x: v.fx + toFdx, | ||||
|                     y: v.fy + toFdy, | ||||
|                     dir: backDir(v.dir) | ||||
|                 }; | ||||
|                 backData[backDir(v.toDir)] = { | ||||
|                     x: v.fx + toTdx, | ||||
|                     y: v.fy + toTdy, | ||||
|                     dir: v.dir | ||||
|                 }; | ||||
|             }); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     function initPortals() { | ||||
|         // 主要是复写勇士绘制以及传送判定,还有自动寻路
 | ||||
|         generatePortalMap(); | ||||
|         console.log(portalMap); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -2,6 +2,8 @@ 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 { HeroRenderer } from '@/core/render/preset/hero'; | ||||
| import type { LayerMovingRenderable } from '@/core/render/preset/layer'; | ||||
| import { BluePalace } from '@/game/mechanism/misc'; | ||||
| 
 | ||||
| interface Adapters { | ||||
|     'hero-adapter'?: RenderAdapter<HeroRenderer>; | ||||
| @ -33,6 +35,11 @@ export function init() { | ||||
|     let stepDir: Dir; | ||||
|     let moveEnding: Promise<any[]>; | ||||
| 
 | ||||
|     /** 传送门信息,下一步传送到哪 */ | ||||
|     let portalData: BluePalace.PortalTo | undefined; | ||||
|     /** 下一步是否步入传送门 */ | ||||
|     let portal: boolean = false; | ||||
| 
 | ||||
|     const pressedArrow: Set<Dir> = new Set(); | ||||
|     Mota.r(() => { | ||||
|         const gameKey = Mota.require('var', 'gameKey'); | ||||
| @ -141,7 +148,7 @@ export function init() { | ||||
|                 if (stopChian || core.status.lockControl) break; | ||||
|                 stepDir = moveDir; | ||||
|                 if (!checkCanMoveStatus(callback)) break; | ||||
| 
 | ||||
|                 if (portal) renderHeroSwap(); | ||||
|                 await adapter.all('move', moveDir); | ||||
|                 onMoveEnd(false, callback); | ||||
|             } | ||||
| @ -153,8 +160,9 @@ export function init() { | ||||
|     function checkCanMoveStatus(callback?: () => void) { | ||||
|         core.setHeroLoc('direction', stepDir); | ||||
|         const { noPass, canMove } = checkCanMove(); | ||||
|         checkPortal(); | ||||
| 
 | ||||
|         if (noPass || !canMove) { | ||||
|         if (!portal && (noPass || !canMove)) { | ||||
|             onCannotMove(canMove, callback); | ||||
|             if (moving) endMove(); | ||||
|             return false; | ||||
| @ -199,7 +207,13 @@ export function init() { | ||||
|     } | ||||
| 
 | ||||
|     function onMoveEnd(noPass: boolean, callback?: () => void) { | ||||
|         if (!noPass) { | ||||
|         if (portal && portalData) { | ||||
|             const { x, y, dir } = portalData; | ||||
|             core.setHeroLoc('x', x); | ||||
|             core.setHeroLoc('y', y); | ||||
|             core.setHeroLoc('direction', dir); | ||||
|             portal = false; | ||||
|         } else if (!noPass) { | ||||
|             const { nx, ny } = getNextLoc(); | ||||
|             core.setHeroLoc('x', nx, true); | ||||
|             core.setHeroLoc('y', ny, true); | ||||
| @ -214,6 +228,130 @@ export function init() { | ||||
|         callback?.(); | ||||
|     } | ||||
| 
 | ||||
|     // ----- 移动 - 传送门
 | ||||
|     function checkPortal() { | ||||
|         const map = BluePalace.portalMap.get(core.status.floorId); | ||||
|         if (!map) { | ||||
|             portal = false; | ||||
|             portalData = void 0; | ||||
|             return; | ||||
|         } | ||||
|         const width = core.status.thisMap.width; | ||||
|         const { x, y, direction } = core.status.hero.loc; | ||||
|         const index = x + y * width; | ||||
|         const data = map?.get(index); | ||||
|         if (!data) { | ||||
|             portal = false; | ||||
|             portalData = void 0; | ||||
|             return; | ||||
|         } | ||||
|         const to = data[direction]; | ||||
|         if (to) { | ||||
|             portal = true; | ||||
|             portalData = to; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     const end: Record<Dir, [number, number]> = { | ||||
|         left: [-1, 0], | ||||
|         right: [1, 0], | ||||
|         down: [0, 1], | ||||
|         up: [0, -1] | ||||
|     }; | ||||
| 
 | ||||
|     /** | ||||
|      * 对勇士进行切割渲染,分成两个renderable进行渲染 | ||||
|      */ | ||||
|     function renderHeroSwap() { | ||||
|         if (!portal || !portalData) return; | ||||
|         const list = adapters['hero-adapter']?.items; | ||||
|         if (!list) return; | ||||
|         const { x: tx, y: ty, dir: toDir } = portalData; | ||||
|         const { x, y, direction } = core.status.hero.loc; | ||||
|         const [dx, dy] = end[direction]; | ||||
|         const [tdx] = end[toDir]; | ||||
|         const checkX = x + dx; | ||||
|         const checkY = y + dy; | ||||
| 
 | ||||
|         list.forEach(v => { | ||||
|             if (!v.renderable) return; | ||||
|             const renderable = { ...v.renderable }; | ||||
|             renderable.render = v.getRenderFromDir(toDir); | ||||
|             renderable.zIndex = ty; | ||||
|             const heroDir = v.moveDir; | ||||
| 
 | ||||
|             const width = v.renderable.render[0][2]; | ||||
|             const height = v.renderable.render[0][3]; | ||||
|             const cell = v.layer.cellSize; | ||||
|             const restHeight = height - cell; | ||||
|             if (!width || !height) return; | ||||
| 
 | ||||
|             const originFrom = structuredClone(v.renderable.render); | ||||
|             const originTo = structuredClone(renderable.render); | ||||
|             v.layer.updateMovingRenderable(); | ||||
|             const append = (r: LayerMovingRenderable[]) => { | ||||
|                 r.push(renderable); | ||||
|             }; | ||||
|             v.on('append', append); | ||||
|             v.on('moveTick', function func() { | ||||
|                 const progress = | ||||
|                     heroDir === 'left' || heroDir === 'right' | ||||
|                         ? 1 - Math.abs(checkX - v.renderable!.x) | ||||
|                         : 1 - Math.abs(checkY - v.renderable!.y); | ||||
|                 if (progress >= 1 || !portal) { | ||||
|                     v.renderable!.render = originFrom; | ||||
|                     v.off('moveTick', func); | ||||
|                     v.off('append', append); | ||||
|                     return; | ||||
|                 } | ||||
|                 const clipWidth = cell * progress; | ||||
|                 const clipHeight = cell * progress; | ||||
|                 const beforeWidth = width - clipWidth; | ||||
|                 const beforeHeight = height - clipHeight; | ||||
| 
 | ||||
|                 v.renderable!.x = x; | ||||
|                 v.renderable!.y = y; | ||||
|                 if (heroDir === 'left' || heroDir === 'right') { | ||||
|                     v.renderable!.x = x + (clipWidth / 2 / cell) * dx; | ||||
|                     v.renderable?.render.forEach((v, i) => { | ||||
|                         v[2] = beforeWidth; | ||||
|                         if (heroDir === 'left') { | ||||
|                             v[0] = originFrom[i][0] + clipWidth; | ||||
|                         } | ||||
|                     }); | ||||
|                 } else { | ||||
|                     v.renderable?.render.forEach((v, i) => { | ||||
|                         v[3] = beforeHeight; | ||||
|                         if (heroDir === 'up') { | ||||
|                             v[1] = originFrom[i][1] + clipHeight + restHeight; | ||||
|                         } | ||||
|                     }); | ||||
|                 } | ||||
| 
 | ||||
|                 renderable.x = tx; | ||||
|                 renderable.y = ty; | ||||
|                 if (toDir === 'left' || toDir === 'right') { | ||||
|                     renderable.x = tx + (clipWidth / 2 / cell - 0.5) * tdx; | ||||
|                     renderable.render.forEach((v, i) => { | ||||
|                         v[2] = clipWidth; | ||||
|                         if (toDir === 'right') { | ||||
|                             v[0] = originTo[i][0] + beforeWidth; | ||||
|                         } | ||||
|                     }); | ||||
|                 } else { | ||||
|                     if (toDir === 'down') renderable.y = ty - 1 + progress; | ||||
|                     renderable.render.forEach((v, i) => { | ||||
|                         v[3] = clipHeight + restHeight; | ||||
|                         if (toDir === 'down') { | ||||
|                             v[1] = originTo[i][1] + clipHeight + restHeight; | ||||
|                             v[3] = clipHeight; | ||||
|                         } | ||||
|                     }); | ||||
|                 } | ||||
|             }); | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     // ----- 勇士移动相关
 | ||||
| 
 | ||||
|     Mota.r(() => { | ||||
|  | ||||
| @ -28,6 +28,8 @@ const backDirMap: Record<Dir2, Dir2> = { | ||||
|     rightdown: 'leftup' | ||||
| }; | ||||
| 
 | ||||
| export function backDir(dir: Dir): Dir; | ||||
| export function backDir(dir: Dir2): Dir2; | ||||
| export function backDir(dir: Dir2): Dir2 { | ||||
|     return backDirMap[dir]; | ||||
| } | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user