mirror of
				https://github.com/unanmed/HumanBreak.git
				synced 2025-10-27 00:42:58 +08:00 
			
		
		
		
	feat: 视角控制系统 & fix: moveViewport
This commit is contained in:
		
							parent
							
								
									7cc4be606a
								
							
						
					
					
						commit
						5970c85dff
					
				| @ -1533,7 +1533,7 @@ control.prototype.chooseReplayFile = function () { | ||||
|         function (obj) { | ||||
|             if (obj.name != core.firstData.name) | ||||
|                 return alert('存档和游戏不一致!'); | ||||
|             if (!obj.route) return alert('无效的录像!'); | ||||
|             if (!obj.route) return core.drawTip('无效的录像!'); | ||||
|             var _replay = function () { | ||||
|                 core.startGame( | ||||
|                     core.flags.startUsingCanvas ? '' : obj.hard || '', | ||||
| @ -2370,7 +2370,7 @@ control.prototype._doSL_load = function (id, callback) { | ||||
|             }, | ||||
|             function (err) { | ||||
|                 console.error(err); | ||||
|                 alert('无效的存档'); | ||||
|                 core.drawTip('无效的存档'); | ||||
|             } | ||||
|         ); | ||||
|     } | ||||
| @ -2393,7 +2393,7 @@ control.prototype._doSL_reload = function (id, callback) { | ||||
| }; | ||||
| 
 | ||||
| control.prototype._doSL_load_afterGet = function (id, data) { | ||||
|     if (!data) return alert('无效的存档'); | ||||
|     if (!data) return core.drawTip('无效的存档'); | ||||
|     var _replay = function () { | ||||
|         core.startGame( | ||||
|             data.hard, | ||||
|  | ||||
| @ -161,15 +161,12 @@ main.floors.MT16= | ||||
|             }, | ||||
|             { | ||||
|                 "type": "animate", | ||||
|                 "name": "amazed", | ||||
|                 "async": true | ||||
|                 "name": "amazed" | ||||
|             }, | ||||
|             { | ||||
|                 "type": "sleep", | ||||
|                 "time": 1000 | ||||
|             }, | ||||
|             { | ||||
|                 "type": "waitAsync" | ||||
|                 "time": 1000, | ||||
|                 "noSkip": true | ||||
|             }, | ||||
|             { | ||||
|                 "type": "moveHero", | ||||
|  | ||||
| @ -69,10 +69,12 @@ import { Image, Text } from './render/preset/misc'; | ||||
| import { RenderItem } from './render/item'; | ||||
| import { texture } from './render/cache'; | ||||
| import { RenderAdapter } from './render/adapter'; | ||||
| import { getMainRenderer } from './render'; | ||||
| import { Layer } from './render/preset/layer'; | ||||
| import { LayerGroupFloorBinder } from './render/preset/floor'; | ||||
| import { HeroKeyMover } from './main/action/move'; | ||||
| import { Camera } from './render/camera'; | ||||
| import * as Animation from 'mutate-animate'; | ||||
| import './render/index'; | ||||
| 
 | ||||
| // ----- 类注册
 | ||||
| Mota.register('class', 'AudioPlayer', AudioPlayer); | ||||
| @ -152,7 +154,6 @@ Mota.register('module', 'Effect', { | ||||
| }); | ||||
| Mota.register('module', 'Render', { | ||||
|     texture, | ||||
|     getMainRenderer: getMainRenderer, | ||||
|     MotaRenderer, | ||||
|     Container, | ||||
|     Sprite, | ||||
| @ -161,11 +162,13 @@ Mota.register('module', 'Render', { | ||||
|     RenderItem, | ||||
|     RenderAdapter, | ||||
|     Layer, | ||||
|     LayerGroupFloorBinder | ||||
|     LayerGroupFloorBinder, | ||||
|     Camera | ||||
| }); | ||||
| Mota.register('module', 'Action', { | ||||
|     HeroKeyMover | ||||
| }); | ||||
| Mota.register('module', 'Animation', Animation); | ||||
| 
 | ||||
| main.renderLoaded = true; | ||||
| Mota.require('var', 'hook').emit('renderLoaded'); | ||||
|  | ||||
							
								
								
									
										335
									
								
								src/core/render/camera.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										335
									
								
								src/core/render/camera.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,335 @@ | ||||
| import { Animation, Transition } from 'mutate-animate'; | ||||
| import { RenderItem } from './item'; | ||||
| import { logger } from '../common/logger'; | ||||
| import { Transform } from './transform'; | ||||
| 
 | ||||
| interface CameraTranslate { | ||||
|     readonly type: 'translate'; | ||||
|     readonly from: Camera; | ||||
|     x: number; | ||||
|     y: number; | ||||
| } | ||||
| 
 | ||||
| interface CameraRotate { | ||||
|     readonly type: 'rotate'; | ||||
|     readonly from: Camera; | ||||
|     /** 旋转角,单位弧度 */ | ||||
|     angle: number; | ||||
| } | ||||
| 
 | ||||
| interface CameraScale { | ||||
|     readonly type: 'scale'; | ||||
|     readonly from: Camera; | ||||
|     x: number; | ||||
|     y: number; | ||||
| } | ||||
| 
 | ||||
| type CameraOperation = CameraTranslate | CameraScale | CameraRotate; | ||||
| 
 | ||||
| export class Camera { | ||||
|     /** 当前绑定的渲染元素 */ | ||||
|     readonly binded: RenderItem; | ||||
|     /** 目标变换矩阵,默认与 `this.binded.transform` 同引用 */ | ||||
|     transform: Transform; | ||||
| 
 | ||||
|     /** 委托ticker的id */ | ||||
|     private delegation: number; | ||||
|     /** 所有的动画id */ | ||||
|     private animationIds: Set<number> = new Set(); | ||||
| 
 | ||||
|     /** 是否需要更新视角 */ | ||||
|     private needUpdate: boolean = false; | ||||
| 
 | ||||
|     /** 变换操作列表,因为矩阵乘法跟顺序有关,因此需要把各个操作拆分成列表进行 */ | ||||
|     protected operation: CameraOperation[] = []; | ||||
| 
 | ||||
|     /** 渲染元素到摄像机的映射 */ | ||||
|     private static cameraMap: Map<RenderItem, Camera> = new Map(); | ||||
| 
 | ||||
|     /** | ||||
|      * 获取一个渲染元素的摄像机,如果不存在则为它创建一个并返回。注意使用`new Camera`创建的摄像机不在此列 | ||||
|      * @param item 渲染元素 | ||||
|      */ | ||||
|     static for(item: RenderItem) { | ||||
|         const camera = this.cameraMap.get(item); | ||||
|         if (!camera) { | ||||
|             const ca = new Camera(item); | ||||
|             this.cameraMap.set(item, ca); | ||||
|             return ca; | ||||
|         } else { | ||||
|             return camera; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     constructor(item: RenderItem) { | ||||
|         this.binded = item; | ||||
| 
 | ||||
|         this.delegation = item.delegateTicker(() => this.tick()); | ||||
|         this.transform = item.transform; | ||||
| 
 | ||||
|         item.on('destroy', () => { | ||||
|             this.destroy(); | ||||
|         }); | ||||
| 
 | ||||
|         if (Camera.cameraMap.has(item)) { | ||||
|             logger.warn(22); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private tick = () => { | ||||
|         if (!this.needUpdate) return; | ||||
|         const trans = this.transform; | ||||
|         trans.reset(); | ||||
|         for (const o of this.operation) { | ||||
|             if (o.type === 'translate') { | ||||
|                 trans.translate(o.x, o.y); | ||||
|             } else if (o.type === 'rotate') { | ||||
|                 trans.rotate(o.angle); | ||||
|             } else { | ||||
|                 trans.scale(o.x, o.y); | ||||
|             } | ||||
|         } | ||||
|         this.binded.update(this.binded); | ||||
|         this.needUpdate = false; | ||||
|     }; | ||||
| 
 | ||||
|     /** | ||||
|      * 在下一帧进行强制更新 | ||||
|      */ | ||||
|     requestUpdate() { | ||||
|         this.needUpdate = true; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * 移除一个变换操作 | ||||
|      * @param operation 要移除的操作 | ||||
|      */ | ||||
|     removeOperation(operation: CameraOperation) { | ||||
|         const index = this.operation.indexOf(operation); | ||||
|         if (index === -1) return; | ||||
|         this.operation.splice(index, 1); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * 清空变换操作列表 | ||||
|      */ | ||||
|     clearOperation() { | ||||
|         this.operation.splice(0); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * 添加一个平移操作 | ||||
|      * @returns 添加的平移变换操作 | ||||
|      */ | ||||
|     addTranslate(): CameraTranslate { | ||||
|         const item: CameraTranslate = { | ||||
|             type: 'translate', | ||||
|             x: 0, | ||||
|             y: 0, | ||||
|             from: this | ||||
|         }; | ||||
|         this.operation.push(item); | ||||
|         return item; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * 添加一个旋转操作 | ||||
|      * @returns 添加的旋转变换操作 | ||||
|      */ | ||||
|     addRotate(): CameraRotate { | ||||
|         const item: CameraRotate = { | ||||
|             type: 'rotate', | ||||
|             angle: 0, | ||||
|             from: this | ||||
|         }; | ||||
|         this.operation.push(item); | ||||
|         return item; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * 添加一个放缩操作 | ||||
|      * @returns 添加的放缩变换操作 | ||||
|      */ | ||||
|     addScale(): CameraScale { | ||||
|         const item: CameraScale = { | ||||
|             type: 'scale', | ||||
|             x: 0, | ||||
|             y: 0, | ||||
|             from: this | ||||
|         }; | ||||
|         this.operation.push(item); | ||||
|         return item; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * 施加动画 | ||||
|      * @param time 动画时长 | ||||
|      * @param update 每帧的更新函数 | ||||
|      */ | ||||
|     applyAnimation(time: number, update: () => void) { | ||||
|         const delegation = this.binded.delegateTicker( | ||||
|             () => { | ||||
|                 update(); | ||||
|                 this.needUpdate = true; | ||||
|             }, | ||||
|             time, | ||||
|             () => { | ||||
|                 update(); | ||||
|                 this.needUpdate = true; | ||||
|                 this.animationIds.delete(delegation); | ||||
|             } | ||||
|         ); | ||||
|         this.animationIds.add(delegation); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * 为一个平移操作实施动画 | ||||
|      * @param operation 平移操作 | ||||
|      * @param animate 动画实例 | ||||
|      * @param time 动画时长 | ||||
|      */ | ||||
|     applyTranslateAnimation( | ||||
|         operation: CameraTranslate, | ||||
|         animate: Animation, | ||||
|         time: number | ||||
|     ) { | ||||
|         if (operation.from !== this) { | ||||
|             logger.warn(20); | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         const update = () => { | ||||
|             operation.x = animate.x; | ||||
|             operation.y = animate.y; | ||||
|         }; | ||||
| 
 | ||||
|         this.applyAnimation(time, update); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * 为一个旋转操作实施动画 | ||||
|      * @param operation 旋转操作 | ||||
|      * @param animate 动画实例 | ||||
|      * @param time 动画时长 | ||||
|      */ | ||||
|     applyRotateAnimation( | ||||
|         operation: CameraRotate, | ||||
|         animate: Animation, | ||||
|         time: number | ||||
|     ) { | ||||
|         if (operation.from !== this) { | ||||
|             logger.warn(20); | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         const update = () => { | ||||
|             operation.angle = animate.angle; | ||||
|         }; | ||||
| 
 | ||||
|         this.applyAnimation(time, update); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * 为一个缩放操作实施动画 | ||||
|      * @param operation 缩放操作 | ||||
|      * @param animate 动画实例 | ||||
|      * @param time 动画时长 | ||||
|      */ | ||||
|     applyScaleAnimation( | ||||
|         operation: CameraScale, | ||||
|         animate: Animation, | ||||
|         time: number | ||||
|     ) { | ||||
|         if (operation.from !== this) { | ||||
|             logger.warn(20); | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         const update = () => { | ||||
|             operation.x = animate.size; | ||||
|             operation.y = animate.size; | ||||
|         }; | ||||
| 
 | ||||
|         this.applyAnimation(time, update); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * 为一个平移操作实施渐变,使用渐变的 x,y 值,即`transition.value.x`与`transition.value.y` | ||||
|      * @param operation 平移操作 | ||||
|      * @param animate 渐变实例 | ||||
|      * @param time 渐变时长 | ||||
|      */ | ||||
|     applyTranslateTransition( | ||||
|         operation: CameraTranslate, | ||||
|         animate: Transition, | ||||
|         time: number | ||||
|     ) { | ||||
|         if (operation.from !== this) { | ||||
|             logger.warn(21); | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         const update = () => { | ||||
|             operation.x = animate.value.x; | ||||
|             operation.y = animate.value.y; | ||||
|         }; | ||||
| 
 | ||||
|         this.applyAnimation(time, update); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * 为一个旋转操作实施渐变,使用渐变的 angle 值,即`transition.value.angle` | ||||
|      * @param operation 旋转操作 | ||||
|      * @param animate 渐变实例 | ||||
|      * @param time 渐变时长 | ||||
|      */ | ||||
|     applyRotateTransition( | ||||
|         operation: CameraRotate, | ||||
|         animate: Transition, | ||||
|         time: number | ||||
|     ) { | ||||
|         if (operation.from !== this) { | ||||
|             logger.warn(21); | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         const update = () => { | ||||
|             operation.angle = animate.value.angle; | ||||
|         }; | ||||
| 
 | ||||
|         this.applyAnimation(time, update); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * 为一个缩放操作实施渐变,使用渐变的 size 值,即`transition.value.size` | ||||
|      * @param operation 缩放操作 | ||||
|      * @param animate 渐变实例 | ||||
|      * @param time 渐变时长 | ||||
|      */ | ||||
|     applyScaleTransition( | ||||
|         operation: CameraScale, | ||||
|         animate: Transition, | ||||
|         time: number | ||||
|     ) { | ||||
|         if (operation.from !== this) { | ||||
|             logger.warn(21); | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         const update = () => { | ||||
|             operation.x = animate.value.size; | ||||
|             operation.y = animate.value.size; | ||||
|         }; | ||||
| 
 | ||||
|         this.applyAnimation(time, update); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * 摧毁这个摄像机,当绑定元素被摧毁之后摄像机会一并摧毁,如果这个摄像机不使用了,一定要将它摧毁 | ||||
|      */ | ||||
|     destroy() { | ||||
|         this.binded.removeTicker(this.delegation); | ||||
|         this.animationIds.forEach(v => this.binded.removeTicker(v)); | ||||
|         Camera.cameraMap.delete(this.binded); | ||||
|     } | ||||
| } | ||||
| @ -1,6 +1,6 @@ | ||||
| import { FloorItemDetail } from '@/plugin/fx/itemDetail'; | ||||
| import { FloorDamageExtends } from './preset/damage'; | ||||
| import { LayerDoorAnimate, LayerGroupFloorBinder } from './preset/floor'; | ||||
| import { LayerDoorAnimate } from './preset/floor'; | ||||
| import { HeroRenderer } from './preset/hero'; | ||||
| import { LayerGroup, FloorLayer } from './preset/layer'; | ||||
| import { MotaRenderer } from './render'; | ||||
| @ -14,10 +14,6 @@ import { Container } from './container'; | ||||
| 
 | ||||
| let main: MotaRenderer; | ||||
| 
 | ||||
| export function getMainRenderer() { | ||||
|     return main; | ||||
| } | ||||
| 
 | ||||
| Mota.require('var', 'loading').once('loaded', () => { | ||||
|     const render = new MotaRenderer(); | ||||
|     main = render; | ||||
| @ -29,6 +25,10 @@ Mota.require('var', 'loading').once('loaded', () => { | ||||
|     mapDraw.id = 'map-draw'; | ||||
|     layer.id = 'layer-main'; | ||||
| 
 | ||||
|     mapDraw.setHD(true); | ||||
|     mapDraw.setAntiAliasing(false); | ||||
|     mapDraw.size(core._PX_, core._PY_); | ||||
| 
 | ||||
|     ['bg', 'bg2', 'event', 'fg', 'fg2'].forEach(v => { | ||||
|         layer.addLayer(v as FloorLayer); | ||||
|     }); | ||||
|  | ||||
| @ -128,6 +128,7 @@ export class FloorViewport implements ILayerGroupRenderExtends { | ||||
|      * @param y 目标图格纵坐标 | ||||
|      */ | ||||
|     setPosition(x: number, y: number) { | ||||
|         if (!this.enabled) return; | ||||
|         const { x: nx, y: ny } = this.getBoundedPosition(x, y); | ||||
|         this.group.removeTicker(this.transition, false); | ||||
|         this.nx = nx; | ||||
| @ -140,6 +141,7 @@ export class FloorViewport implements ILayerGroupRenderExtends { | ||||
|      * @param y 目标图格纵坐标 | ||||
|      */ | ||||
|     moveTo(x: number, y: number) { | ||||
|         if (!this.enabled) return; | ||||
|         const { x: nx, y: ny } = this.getBoundedPosition(x, y); | ||||
|         if (this.inTransition) { | ||||
|             const distance = Math.hypot(this.nx - nx, this.ny - ny); | ||||
| @ -176,6 +178,7 @@ export class FloorViewport implements ILayerGroupRenderExtends { | ||||
|      * @param y 目标图格纵坐标 | ||||
|      */ | ||||
|     mutateTo(x: number, y: number) { | ||||
|         if (!this.enabled) return; | ||||
|         const { x: nx, y: ny } = this.getBoundedPosition(x, y); | ||||
|         this.createTransition(nx, ny, this.transitionTime); | ||||
|     } | ||||
| @ -321,6 +324,7 @@ export class FloorViewport implements ILayerGroupRenderExtends { | ||||
|         const halfWidth = core._PX_ / 2; | ||||
|         const halfHeight = core._PY_ / 2; | ||||
|         this.delegation = this.group.delegateTicker(() => { | ||||
|             if (!this.enabled) return; | ||||
|             if (this.nx === nx && this.ny === ny) return; | ||||
|             const cell = this.group.cellSize; | ||||
|             const half = cell / 2; | ||||
|  | ||||
| @ -4,7 +4,7 @@ import { RenderItem } from './item'; | ||||
| import { Transform } from './transform'; | ||||
| 
 | ||||
| export class MotaRenderer extends Container { | ||||
|     static list: Set<MotaRenderer> = new Set(); | ||||
|     static list: Map<string, MotaRenderer> = new Map(); | ||||
| 
 | ||||
|     target: MotaCanvas2D; | ||||
| 
 | ||||
| @ -13,18 +13,17 @@ export class MotaRenderer extends Container { | ||||
|     constructor(id: string = 'render-main') { | ||||
|         super('static', false); | ||||
| 
 | ||||
|         this.id = id; | ||||
| 
 | ||||
|         this.target = new MotaCanvas2D(id); | ||||
|         this.size(core._PX_, core._PY_); | ||||
|         this.target.withGameScale(true); | ||||
|         this.target.size(core._PX_, core._PY_); | ||||
|         this.target.css(`z-index: 100`); | ||||
|         this.target.setAntiAliasing(false); | ||||
| 
 | ||||
|         this.setAnchor(0.5, 0.5); | ||||
|         this.transform.translate(240, 240); | ||||
| 
 | ||||
|         MotaRenderer.list.add(this); | ||||
|         MotaRenderer.list.set(id, this); | ||||
|     } | ||||
| 
 | ||||
|     update(item?: RenderItem) { | ||||
| @ -80,7 +79,11 @@ export class MotaRenderer extends Container { | ||||
|     } | ||||
| 
 | ||||
|     destroy() { | ||||
|         MotaRenderer.list.delete(this); | ||||
|         MotaRenderer.list.delete(this.id); | ||||
|     } | ||||
| 
 | ||||
|     static get(id: string) { | ||||
|         return this.list.get(id); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -38,7 +38,7 @@ export class Transform { | ||||
|     /** | ||||
|      * 移动,叠加关系 | ||||
|      */ | ||||
|     move(x: number, y: number) { | ||||
|     translate(x: number, y: number) { | ||||
|         mat3.translate(this.mat, this.mat, [x, y]); | ||||
|         this.x += x; | ||||
|         this.y += y; | ||||
|  | ||||
| @ -40,6 +40,10 @@ | ||||
|         "17": "Floor-damage extension needs 'floor-binder' extension as dependency.", | ||||
|         "18": "Uncaught error in posting like info for danmaku. Danmaku id: $1.", | ||||
|         "19": "Repeat light id: '$1'.", | ||||
|         "20": "Cannot apply animation to camera operation that is not belong to it.", | ||||
|         "21": "Cannot apply transition to camera operation that is not belong to it.", | ||||
|         "22": "There is already a camera for delivered render item. Consider using 'Camera.for' to avoid some exceptions.", | ||||
|         "23": "Render item with id of '$1' has already exists.", | ||||
|         "1001": "Item-detail extension needs 'floor-binder' and 'floor-damage' extension as dependency." | ||||
|     } | ||||
| } | ||||
| @ -443,6 +443,9 @@ export class HeroMover extends ObjectMoverBase { | ||||
|     /** 是否会在特殊时刻进行自动存档 */ | ||||
|     private autoSave: boolean = false; | ||||
| 
 | ||||
|     /** 本次移动开始时的移动速度 */ | ||||
|     private beforeMoveSpeed: number = 100; | ||||
| 
 | ||||
|     /** 这一步的传送门信息 */ | ||||
|     private portalData?: BluePalace.PortalTo; | ||||
| 
 | ||||
| @ -452,6 +455,7 @@ export class HeroMover extends ObjectMoverBase { | ||||
|         inLockControl: boolean = false, | ||||
|         autoSave: boolean = false | ||||
|     ): IMoveController | null { | ||||
|         if (this.moving) return null; | ||||
|         this.ignoreTerrain = ignoreTerrain; | ||||
|         this.noRoute = noRoute; | ||||
|         this.inLockControl = inLockControl; | ||||
| @ -472,6 +476,7 @@ export class HeroMover extends ObjectMoverBase { | ||||
|     } | ||||
| 
 | ||||
|     protected async onMoveStart(controller: IMoveController): Promise<void> { | ||||
|         this.beforeMoveSpeed = this.moveSpeed; | ||||
|         const adapter = HeroMover.adapter; | ||||
|         if (!adapter) return; | ||||
|         await adapter.all('readyMove'); | ||||
| @ -479,6 +484,8 @@ export class HeroMover extends ObjectMoverBase { | ||||
|     } | ||||
| 
 | ||||
|     protected async onMoveEnd(controller: IMoveController): Promise<void> { | ||||
|         this.moveSpeed = this.beforeMoveSpeed; | ||||
|         this.onSetMoveSpeed(this.moveSpeed, controller); | ||||
|         const adapter = HeroMover.adapter; | ||||
|         if (!adapter) return; | ||||
|         await adapter.all('endMove'); | ||||
|  | ||||
| @ -38,6 +38,8 @@ import type { Layer } from '@/core/render/preset/layer'; | ||||
| import type { LayerGroupFloorBinder } from '@/core/render/preset/floor'; | ||||
| import type { HeroKeyMover } from '@/core/main/action/move'; | ||||
| import type { BlockMover, HeroMover, ObjectMoverBase } from './state/move'; | ||||
| import type { Camera } from '@/core/render/camera'; | ||||
| import type * as Animation from 'mutate-animate'; | ||||
| 
 | ||||
| interface ClassInterface { | ||||
|     // 渲染进程与游戏进程通用
 | ||||
| @ -119,6 +121,7 @@ interface ModuleInterface { | ||||
|         RenderAdapter: typeof RenderAdapter; | ||||
|         Layer: typeof Layer; | ||||
|         LayerGroupFloorBinder: typeof LayerGroupFloorBinder; | ||||
|         Camera: typeof Camera; | ||||
|     }; | ||||
|     State: { | ||||
|         ItemState: typeof ItemState; | ||||
| @ -133,6 +136,7 @@ interface ModuleInterface { | ||||
|     Action: { | ||||
|         HeroKeyMover: typeof HeroKeyMover; | ||||
|     }; | ||||
|     Animation: typeof Animation; | ||||
| } | ||||
| 
 | ||||
| interface SystemInterfaceMap { | ||||
| @ -148,20 +152,10 @@ interface PluginInterface { | ||||
|     // 渲染进程定义的插件
 | ||||
|     pop_r: typeof import('../plugin/pop'); | ||||
|     use_r: typeof import('../plugin/use'); | ||||
|     // animate: typeof import('../plugin/animateController');
 | ||||
|     // utils: typeof import('../plugin/utils');
 | ||||
|     // status: typeof import('../plugin/ui/statusBar');
 | ||||
|     fly_r: typeof import('../plugin/ui/fly'); | ||||
|     chase_r: typeof import('../plugin/chase/chase'); | ||||
|     // webglUtils: typeof import('../plugin/webgl/utils');
 | ||||
|     // shadow_r: typeof import('../plugin/shadow/shadow');
 | ||||
|     // gameShadow_r: typeof import('../plugin/shadow/gameShadow');
 | ||||
|     // achievement: typeof import('../plugin/ui/achievement');
 | ||||
|     completion_r: typeof import('../plugin/completion'); | ||||
|     // path: typeof import('../plugin/fx/path');
 | ||||
|     gameCanvas_r: typeof import('../plugin/fx/gameCanvas'); | ||||
|     // noise: typeof import('../plugin/fx/noise');
 | ||||
|     smooth_r: typeof import('../plugin/fx/smoothView'); | ||||
|     frag_r: typeof import('../plugin/fx/frag'); | ||||
|     // 游戏进程定义的插件
 | ||||
|     utils_g: typeof import('../plugin/game/utils'); | ||||
| @ -174,12 +168,9 @@ interface PluginInterface { | ||||
|     chase_g: typeof import('../plugin/game/chase'); | ||||
|     skill_g: typeof import('../plugin/game/skill'); | ||||
|     towerBoss_g: typeof import('../plugin/game/towerBoss'); | ||||
|     // heroFourFrames_g: typeof import('../plugin/game/fx/heroFourFrames');
 | ||||
|     rewrite_g: typeof import('../plugin/game/fx/rewrite'); | ||||
|     itemDetail_g: typeof import('../plugin/game/fx/itemDetail'); | ||||
|     checkBlock_g: typeof import('../plugin/game/enemy/checkblock'); | ||||
|     // halo_g: typeof import('../plugin/game/fx/halo');
 | ||||
|     // study_g: typeof import('../plugin/game/study');
 | ||||
| } | ||||
| 
 | ||||
| interface PackageInterface { | ||||
|  | ||||
| @ -5,7 +5,7 @@ import type { | ||||
|     LayerFloorBinder | ||||
| } from '@/core/render/preset/floor'; | ||||
| import type { HeroRenderer } from '@/core/render/preset/hero'; | ||||
| import type { Layer } from '@/core/render/preset/layer'; | ||||
| import type { Layer, LayerGroup } from '@/core/render/preset/layer'; | ||||
| import type { TimingFn } from 'mutate-animate'; | ||||
| import { BlockMover, heroMoveCollection, MoveStep } from '@/game/state/move'; | ||||
| import type { FloorViewport } from '@/core/render/preset/viewport'; | ||||
| @ -85,6 +85,11 @@ export function init() { | ||||
|     } | ||||
| 
 | ||||
|     Mota.r(() => { | ||||
|         // ----- 引入
 | ||||
|         const Camera = Mota.require('module', 'Render').Camera; | ||||
|         const Renderer = Mota.require('module', 'Render').MotaRenderer; | ||||
|         const Animation = Mota.require('module', 'Animation'); | ||||
| 
 | ||||
|         // ----- 勇士移动相关
 | ||||
|         control.prototype.moveAction = async function (callback?: () => void) { | ||||
|             heroMover.clearMoveQueue(); | ||||
| @ -578,6 +583,79 @@ export function init() { | ||||
|             if (success) adapters.viewport?.all('mutateTo', destX, destY); | ||||
|             return success; | ||||
|         }; | ||||
| 
 | ||||
|         control.prototype.moveViewport = function ( | ||||
|             x: number, | ||||
|             y: number, | ||||
|             _moveMode: EaseMode, | ||||
|             time: number = 0, | ||||
|             callback?: () => void | ||||
|         ) { | ||||
|             const main = Renderer.get('render-main'); | ||||
|             const layer = main?.getElementById('layer-main') as LayerGroup; | ||||
|             if (!layer) return; | ||||
|             const camera = Camera.for(layer); | ||||
|             camera.clearOperation(); | ||||
|             const translate = camera.addTranslate(); | ||||
| 
 | ||||
|             const animateTime = time / Math.max(core.status.replay.speed, 1); | ||||
|             const animate = new Animation.Animation(); | ||||
|             animate | ||||
|                 .absolute() | ||||
|                 .time(1) | ||||
|                 .mode(Animation.linear()) | ||||
|                 .move(-core.bigmap.offsetX, -core.bigmap.offsetY); | ||||
|             animate.time(animateTime).move(-x * 32, -y * 32); | ||||
| 
 | ||||
|             camera.applyTranslateAnimation( | ||||
|                 translate, | ||||
|                 animate, | ||||
|                 animateTime + 50 | ||||
|             ); | ||||
|             camera.transform = layer.camera; | ||||
| 
 | ||||
|             const timeout = window.setTimeout(() => { | ||||
|                 core.bigmap.offsetX = x * 32; | ||||
|                 core.bigmap.offsetY = y * 32; | ||||
|                 callback?.(); | ||||
|             }, animateTime + 50); | ||||
| 
 | ||||
|             // time /= Math.max(core.status.replay.speed, 1);
 | ||||
|             // var per_time = 10,
 | ||||
|             //     step = 0,
 | ||||
|             //     steps = parseInt(time / per_time);
 | ||||
|             // if (steps <= 0) {
 | ||||
|             //     this.setViewport(32 * x, 32 * y);
 | ||||
|             //     if (callback) callback();
 | ||||
|             //     return;
 | ||||
|             // }
 | ||||
|             // var px = core.clamp(32 * x, 0, 32 * core.bigmap.width - core._PX_);
 | ||||
|             // var py = core.clamp(32 * y, 0, 32 * core.bigmap.height - core._PY_);
 | ||||
|             // var cx = core.bigmap.offsetX;
 | ||||
|             // var cy = core.bigmap.offsetY;
 | ||||
|             // var moveFunc = core.applyEasing(moveMode);
 | ||||
| 
 | ||||
|             // var animate = window.setInterval(function () {
 | ||||
|             //     step++;
 | ||||
|             //     core.setViewport(
 | ||||
|             //         cx + moveFunc(step / steps) * (px - cx),
 | ||||
|             //         cy + moveFunc(step / steps) * (py - cy)
 | ||||
|             //     );
 | ||||
|             //     if (step == steps) {
 | ||||
|             //         delete core.animateFrame.asyncId[animate];
 | ||||
|             //         clearInterval(animate);
 | ||||
|             //         core.setViewport(px, py);
 | ||||
|             //         if (callback) callback();
 | ||||
|             //     }
 | ||||
|             // }, per_time);
 | ||||
| 
 | ||||
|             const id = fallbackIds++; | ||||
|             core.animateFrame.lastAsyncId = id; | ||||
|             core.animateFrame.asyncId[id] = () => { | ||||
|                 callback?.(); | ||||
|                 clearTimeout(timeout); | ||||
|             }; | ||||
|         }; | ||||
|     }); | ||||
| 
 | ||||
|     loading.once('loaded', () => { | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user