mirror of
https://github.com/unanmed/HumanBreak.git
synced 2026-11-03 01:54:58 +08:00
feat: 开关门动画
This commit is contained in:
1 parent
3372337fdf
commit
dce4cc5b7d
16 files changed
+305
-104
No files matched your search
@@ -17,5 +17,6 @@ export async function createMainExtension() {
|
||||
const layer = state.layer.getLayerByAlias('event');
|
||||
if (layer) {
|
||||
mainMapExtension.addHero(state.hero, layer);
|
||||
mainMapExtension.addDoor(layer);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
import {
|
||||
IMapLayer,
|
||||
IMapLayerHookController,
|
||||
IMapLayerHooks
|
||||
} from '@user/data-state';
|
||||
import { IMapDoorRenderer } from './types';
|
||||
import { IMapRenderer } from '../types';
|
||||
import { sleep } from 'mutate-animate';
|
||||
import { DOOR_ANIMATE_INTERVAL } from '../../shared';
|
||||
|
||||
export class MapDoorRenderer implements IMapDoorRenderer {
|
||||
/** 钩子控制器 */
|
||||
readonly controller: IMapLayerHookController;
|
||||
|
||||
/** 动画间隔 */
|
||||
private interval: number = DOOR_ANIMATE_INTERVAL;
|
||||
|
||||
constructor(
|
||||
readonly renderer: IMapRenderer,
|
||||
readonly layer: IMapLayer
|
||||
) {
|
||||
this.controller = layer.addHook(new MapDoorHook(this));
|
||||
this.controller.load();
|
||||
}
|
||||
|
||||
setAnimateInterval(interval: number): void {
|
||||
this.interval = interval;
|
||||
}
|
||||
|
||||
async openDoor(x: number, y: number): Promise<void> {
|
||||
const status = this.renderer.getBlockStatus(this.layer, x, y);
|
||||
if (!status) return;
|
||||
const array = this.layer.getMapRef().array;
|
||||
const index = y * this.layer.width + x;
|
||||
const num = array[index];
|
||||
const data = this.renderer.manager.getIfBigImage(num);
|
||||
if (!data) return;
|
||||
const frames = data.frames;
|
||||
for (let i = 0; i < frames; i++) {
|
||||
status.useSpecifiedFrame(i);
|
||||
await sleep(this.interval);
|
||||
}
|
||||
}
|
||||
|
||||
async closeDoor(num: number, x: number, y: number): Promise<void> {
|
||||
const data = this.renderer.manager.getIfBigImage(num);
|
||||
if (!data) return;
|
||||
const moving = this.renderer.addMovingBlock(this.layer, num, x, y);
|
||||
const frames = data.frames;
|
||||
|
||||
for (let i = frames - 1; i >= 0; i--) {
|
||||
moving.useSpecifiedFrame(i);
|
||||
await sleep(this.interval);
|
||||
}
|
||||
moving.destroy();
|
||||
}
|
||||
|
||||
destroy(): void {
|
||||
this.controller.unload();
|
||||
}
|
||||
}
|
||||
|
||||
class MapDoorHook implements Partial<IMapLayerHooks> {
|
||||
constructor(readonly renderer: MapDoorRenderer) {}
|
||||
|
||||
onOpenDoor(x: number, y: number): Promise<void> {
|
||||
return this.renderer.openDoor(x, y);
|
||||
}
|
||||
|
||||
onCloseDoor(num: number, x: number, y: number): Promise<void> {
|
||||
return this.renderer.closeDoor(num, x, y);
|
||||
}
|
||||
}
|
||||
@@ -156,7 +156,8 @@ export class MapHeroRenderer implements IMapHeroRenderer {
|
||||
offset: dirImage.width / 4,
|
||||
texture: dirImage,
|
||||
cls: BlockCls.Unknown,
|
||||
frames: 4
|
||||
frames: 4,
|
||||
defaultFrame: 0
|
||||
};
|
||||
this.textureMap.set(v, data);
|
||||
});
|
||||
|
||||
@@ -1,22 +1,30 @@
|
||||
import { IHeroState, IMapLayer } from '@user/data-state';
|
||||
import { IMapExtensionManager, IMapHeroRenderer } from './types';
|
||||
import {
|
||||
IMapDoorRenderer,
|
||||
IMapExtensionManager,
|
||||
IMapHeroRenderer
|
||||
} from './types';
|
||||
import { IMapRenderer } from '../types';
|
||||
import { MapHeroRenderer } from './hero';
|
||||
import { logger } from '@motajs/common';
|
||||
import { MapDoorRenderer } from './door';
|
||||
|
||||
export class MapExtensionManager implements IMapExtensionManager {
|
||||
/** 勇士状态至勇士渲染器的映射 */
|
||||
readonly heroMap: WeakMap<IHeroState, IMapHeroRenderer> = new WeakMap();
|
||||
readonly heroMap: Map<IHeroState, IMapHeroRenderer> = new Map();
|
||||
/** 地图图层到门渲染器的映射 */
|
||||
readonly doorMap: Map<IMapLayer, IMapDoorRenderer> = new Map();
|
||||
|
||||
constructor(readonly renderer: IMapRenderer) {}
|
||||
|
||||
addHero(state: IHeroState, layer: IMapLayer): void {
|
||||
addHero(state: IHeroState, layer: IMapLayer): IMapHeroRenderer | null {
|
||||
if (this.heroMap.has(state)) {
|
||||
logger.error(45);
|
||||
return;
|
||||
logger.error(45, 'hero renderer');
|
||||
return null;
|
||||
}
|
||||
const heroRenderer = new MapHeroRenderer(this.renderer, layer, state);
|
||||
this.heroMap.set(state, heroRenderer);
|
||||
return heroRenderer;
|
||||
}
|
||||
|
||||
removeHero(state: IHeroState): void {
|
||||
@@ -25,4 +33,28 @@ export class MapExtensionManager implements IMapExtensionManager {
|
||||
renderer.destroy();
|
||||
this.heroMap.delete(state);
|
||||
}
|
||||
|
||||
addDoor(layer: IMapLayer): IMapDoorRenderer | null {
|
||||
if (this.doorMap.has(layer)) {
|
||||
logger.error(45, 'door renderer');
|
||||
return null;
|
||||
}
|
||||
const doorRenderer = new MapDoorRenderer(this.renderer, layer);
|
||||
this.doorMap.set(layer, doorRenderer);
|
||||
return doorRenderer;
|
||||
}
|
||||
|
||||
removeDoor(layer: IMapLayer): void {
|
||||
const renderer = this.doorMap.get(layer);
|
||||
if (!renderer) return;
|
||||
renderer.destroy();
|
||||
this.doorMap.delete(layer);
|
||||
}
|
||||
|
||||
destroy(): void {
|
||||
this.heroMap.forEach(v => void v.destroy());
|
||||
this.doorMap.forEach(v => void v.destroy());
|
||||
this.heroMap.clear();
|
||||
this.doorMap.clear();
|
||||
}
|
||||
}
|
||||
@@ -12,13 +12,28 @@ export interface IMapExtensionManager {
|
||||
* @param state 勇士状态
|
||||
* @param layer 勇士所在图层
|
||||
*/
|
||||
addHero(state: IHeroState, layer: IMapLayer): void;
|
||||
addHero(state: IHeroState, layer: IMapLayer): IMapHeroRenderer | null;
|
||||
|
||||
/**
|
||||
* 移除勇士渲染拓展
|
||||
* @param state 勇士状态
|
||||
*/
|
||||
removeHero(state: IHeroState): void;
|
||||
|
||||
/**
|
||||
* 添加开门动画拓展
|
||||
*/
|
||||
addDoor(layer: IMapLayer): IMapDoorRenderer | null;
|
||||
|
||||
/**
|
||||
* 移除开门动画拓展
|
||||
*/
|
||||
removeDoor(layer: IMapLayer): void;
|
||||
|
||||
/**
|
||||
* 摧毁这个拓展管理对象,释放相关资源
|
||||
*/
|
||||
destroy(): void;
|
||||
}
|
||||
|
||||
export interface IMapHeroRenderer {
|
||||
@@ -119,3 +134,31 @@ export interface IMapHeroRenderer {
|
||||
*/
|
||||
destroy(): void;
|
||||
}
|
||||
|
||||
export interface IMapDoorRenderer {
|
||||
/**
|
||||
* 开启指定位置的门,播放开门动画
|
||||
* @param x 门横坐标
|
||||
* @param y 门纵坐标
|
||||
*/
|
||||
openDoor(x: number, y: number): Promise<void>;
|
||||
|
||||
/**
|
||||
* 在指定位置执行关门动画
|
||||
* @param num 门图块数字
|
||||
* @param x 门横坐标
|
||||
* @param y 门纵坐标
|
||||
*/
|
||||
closeDoor(num: number, x: number, y: number): Promise<void>;
|
||||
|
||||
/**
|
||||
* 设置开关门动画两帧之间的间隔
|
||||
* @param interval 开门动画间隔
|
||||
*/
|
||||
setAnimateInterval(interval: number): void;
|
||||
|
||||
/**
|
||||
* 摧毁这个门动画拓展,释放相关资源
|
||||
*/
|
||||
destroy(): void;
|
||||
}
|
||||
@@ -1348,7 +1348,6 @@ export class MapRenderer
|
||||
}
|
||||
|
||||
render(): HTMLCanvasElement {
|
||||
// todo: 改为 FBO,最后把 FBO 画到画布上
|
||||
const gl = this.gl;
|
||||
const data = this.contextData;
|
||||
if (!this.assetData) {
|
||||
@@ -1711,6 +1710,10 @@ export class MapRenderer
|
||||
this.needUpdateTransform = true;
|
||||
}
|
||||
|
||||
requestUpdate(): void {
|
||||
this.updateRequired = true;
|
||||
}
|
||||
|
||||
needUpdate(): boolean {
|
||||
return (
|
||||
this.updateRequired ||
|
||||
|
||||
@@ -41,6 +41,11 @@ export interface IMapDataGetter {
|
||||
* @param moving 移动图块对象
|
||||
*/
|
||||
hasMoving(moving: IMovingBlock): boolean;
|
||||
|
||||
/**
|
||||
* 申请更新渲染
|
||||
*/
|
||||
requestUpdate(): void;
|
||||
}
|
||||
|
||||
interface BlockMapPos {
|
||||
@@ -106,7 +111,13 @@ export class MapVertexGenerator
|
||||
|
||||
/** 空顶点数组,因为空顶点很常用,所以直接定义一个全局常量 */
|
||||
private static readonly EMPTY_VETREX: Float32Array = new Float32Array(
|
||||
INSTANCED_COUNT
|
||||
// prettier-ignore
|
||||
[
|
||||
0, 0, 0, 0, // 顶点坐标
|
||||
0, 0, 0, 0, // 纹理坐标
|
||||
0, 1, 0, 0, // a_tileData,不透明度需要设为 1
|
||||
-1, 0, 0, 0, // a_texData,当前帧数需要设为 -1
|
||||
]
|
||||
);
|
||||
|
||||
readonly block: IBlockSplitter<MapVertexBlock>;
|
||||
@@ -1044,7 +1055,7 @@ class MapVertexBlock implements IMapVertexBlock {
|
||||
* @param blockHeight 分块高度
|
||||
*/
|
||||
constructor(
|
||||
readonly renderer: IMapRenderer,
|
||||
readonly renderer: IMapRenderer & IMapDataGetter,
|
||||
originArray: IMapVertexData,
|
||||
startIndex: number,
|
||||
count: number,
|
||||
@@ -1074,6 +1085,7 @@ class MapVertexBlock implements IMapVertexBlock {
|
||||
*/
|
||||
markRenderDirty() {
|
||||
this.renderDirty = true;
|
||||
this.renderer.requestUpdate();
|
||||
}
|
||||
|
||||
markDirty(
|
||||
@@ -1101,6 +1113,7 @@ class MapVertexBlock implements IMapVertexBlock {
|
||||
data.dirtyBottom = Math.max(db, data.dirtyBottom);
|
||||
}
|
||||
this.dirty = true;
|
||||
this.renderer.requestUpdate();
|
||||
}
|
||||
|
||||
getDirtyArea(layer: IMapLayer): Readonly<ILayerDirtyData> | null {
|
||||
|
||||
@@ -33,6 +33,8 @@ export const DYNAMIC_RESERVE = 16;
|
||||
* 调整此值可以调整频率,值越大,越不容易因为数量小于预留数量而减小预留。
|
||||
*/
|
||||
export const MOVING_TOLERANCE = 60;
|
||||
/** 开关门动画的动画时长 */
|
||||
export const DOOR_ANIMATE_INTERVAL = 50;
|
||||
|
||||
//#region 状态栏
|
||||
|
||||
|
||||
Reference in new issue
Block a user