refactor: 地图渲染器改为与 LayerState 绑定

This commit is contained in:
AncTe committed 2025-11-18 21:12:43 +08:00
1 parent 6ae0d6ca5f
commit ebaa35ea5b
17 files changed
+564 -387

No files matched your search

@@ -1,18 +1,43 @@
import { logger } from '@motajs/common';
import { IMapLayer, MapLayer } from '../map';
import { ILayerState } from './types';
import {
Hookable,
HookController,
IHookController,
logger
} from '@motajs/common';
import {
IMapLayer,
IMapLayerHookController,
IMapLayerHooks,
MapLayer
} from '../map';
import { ILayerState, ILayerStateHooks } from './types';
export class LayerState implements ILayerState {
readonly layerList: WeakSet<IMapLayer> = new WeakSet();
export class LayerState
extends Hookable<ILayerStateHooks>
implements ILayerState
{
readonly layerList: Set<IMapLayer> = new Set();
/** 图层到图层别名映射 */
readonly layerAliasMap: WeakMap<IMapLayer, string> = new WeakMap();
/** 图层别名到图层的映射 */
readonly aliasLayerMap: Map<symbol, IMapLayer> = new Map();
/** 背景图块 */
private backgroundTile: number = 0;
/** 图层钩子映射 */
private layerHookMap: Map<IMapLayer, IMapLayerHookController> = new Map();
addLayer(width: number, height: number): IMapLayer {
const array = new Uint32Array(width * height);
const layer = new MapLayer(array, width, height);
this.layerList.add(layer);
this.forEachHook((hook, controller) => {
hook.onUpdateLayer?.(controller, this.layerList);
});
const controller = layer.addHook(new StateMapLayerHook(this));
this.layerHookMap.set(layer, controller);
controller.load();
return layer;
}
@@ -24,6 +49,17 @@ export class LayerState implements ILayerState {
this.aliasLayerMap.delete(symbol);
this.layerAliasMap.delete(layer);
}
this.forEachHook((hook, controller) => {
hook.onUpdateLayer?.(controller, this.layerList);
});
const controller = this.layerHookMap.get(layer);
if (!controller) return;
controller.unload();
this.layerHookMap.delete(layer);
}
hasLayer(layer: IMapLayer): boolean {
return this.layerList.has(layer);
}
setLayerAlias(layer: IMapLayer, alias: string): void {
@@ -57,4 +93,58 @@ export class LayerState implements ILayerState {
layer.resize2(width, height);
}
}
setBackground(tile: number): void {
this.backgroundTile = tile;
this.forEachHook((hook, controller) => {
hook.onChangeBackground?.(controller, tile);
});
}
getBackground(): number {
return this.backgroundTile;
}
protected createController(
hook: Partial<ILayerStateHooks>
): IHookController<ILayerStateHooks> {
return new HookController(this, hook);
}
}
class StateMapLayerHook implements Partial<IMapLayerHooks> {
constructor(readonly state: LayerState) {}
onUpdateArea(
controller: IMapLayerHookController,
x: number,
y: number,
width: number,
height: number
): void {
this.state.forEachHook((hook, c) => {
hook.onUpdateLayerArea?.(c, controller.layer, x, y, width, height);
});
}
onUpdateBlock(
controller: IMapLayerHookController,
block: number,
x: number,
y: number
): void {
this.state.forEachHook((hook, c) => {
hook.onUpdateLayerBlock?.(c, controller.layer, block, x, y);
});
}
onResize(
controller: IMapLayerHookController,
width: number,
height: number
): void {
this.state.forEachHook((hook, c) => {
hook.onResizeLayer?.(c, controller.layer, width, height);
});
}
}
+87 -2
View File
@@ -1,8 +1,76 @@
import { IHookable, IHookBase, IHookController } from '@motajs/common';
import { IMapLayer } from '../map';
export interface ILayerState {
export interface ILayerStateHooks extends IHookBase {
/**
* 当设置背景图块时执行,如果设置的背景图块与原先一样,则不会执行
* @param controller 钩子控制器
* @param tile 背景图块
*/
onChangeBackground(controller: IHookController<this>, tile: number): void;
/**
* 当地图列表发生变化时执行
* @param controller 钩子控制器
* @param layerList 地图图层列表
*/
onUpdateLayer(
controller: IHookController<this>,
layerList: Set<IMapLayer>
): void;
/**
* 当地图状态对象的某个图层发生区域更新时执行
* @param controller 钩子控制器
* @param layer 触发更新的地图图层对象
* @param x 更新区域左上角横坐标
* @param y 更新区域左上角纵坐标
* @param width 更新区域宽度
* @param height 更新区域高度
*/
onUpdateLayerArea(
controller: IHookController<this>,
layer: IMapLayer,
x: number,
y: number,
width: number,
height: number
): void;
/**
* 当地图状态对象的某个图层设置图块时执行,如果设置的图块与原先一样则不会触发
* @param controller 钩子控制器
* @param layer 触发更新的地图图层对象
* @param block 设置为的图块
* @param x 图块横坐标
* @param y 图块纵坐标
*/
onUpdateLayerBlock(
controller: IHookController<this>,
layer: IMapLayer,
block: number,
x: number,
y: number
): void;
/**
* 当地图状态对象的某个图层大小发生变化时执行
* @param controller 钩子控制器
* @param layer 触发更新的地图图层对象
* @param width 地图的新宽度
* @param height 地图的新高度
*/
onResizeLayer(
controller: IHookController<this>,
layer: IMapLayer,
width: number,
height: number
): void;
}
export interface ILayerState extends IHookable<ILayerStateHooks> {
/** 地图列表 */
readonly layerList: WeakSet<IMapLayer>;
readonly layerList: Set<IMapLayer>;
/**
* 添加图层
@@ -17,6 +85,12 @@ export interface ILayerState {
*/
removeLayer(layer: IMapLayer): void;
/**
* 当前地图状态对象是否包含指定图层对象
* @param layer 图层对象
*/
hasLayer(layer: IMapLayer): boolean;
/**
* 设置图层别名
* @param layer 图层对象
@@ -49,6 +123,17 @@ export interface ILayerState {
height: number,
keepBlock?: boolean
): void;
/**
* 设置背景图块
* @param tile 背景图块数字
*/
setBackground(tile: number): void;
/**
* 获取背景图块数字,如果没有设置过,则返回 0
*/
getBackground(): number;
}
export interface ICoreState {
+36 -65
View File
@@ -2,25 +2,19 @@ import { isNil } from 'lodash-es';
import {
IMapLayer,
IMapLayerData,
IMapLayerExtends,
IMapLayerExtendsController
IMapLayerHookController,
IMapLayerHooks
} from './types';
import { logger } from '@motajs/common';
import { Hookable, HookController, logger } from '@motajs/common';
interface IExtendsData {
readonly ex: IMapLayerExtends;
readonly controller: IMapLayerExtendsController;
}
export class MapLayer implements IMapLayer {
export class MapLayer
extends Hookable<IMapLayerHooks, IMapLayerHookController>
implements IMapLayer
{
width: number;
height: number;
empty: boolean = true;
/** 已经加载完毕的图层拓展 */
private loadedExtends: Set<IExtendsData> = new Set();
/** 添加的图层拓展 */
private addedExtends: Map<string, IExtendsData> = new Map();
zIndex: number = 0;
/** 地图图块数组 */
private mapArray: Uint32Array;
@@ -28,6 +22,7 @@ export class MapLayer implements IMapLayer {
private mapData: IMapLayerData;
constructor(array: Uint32Array, width: number, height: number) {
super();
this.width = width;
this.height = height;
const area = width * height;
@@ -42,9 +37,6 @@ export class MapLayer implements IMapLayer {
resize(width: number, height: number): void {
if (this.width === width && this.height === height) {
this.loadedExtends.forEach(v => {
v.ex.onResize?.(v.controller, width, height);
});
return;
}
this.mapData.expired = true;
@@ -78,17 +70,14 @@ export class MapLayer implements IMapLayer {
expired: false,
array: this.mapArray
};
this.loadedExtends.forEach(v => {
v.ex.onResize?.(v.controller, width, height);
this.forEachHook((hook, controller) => {
hook.onResize?.(controller, width, height);
});
}
resize2(width: number, height: number): void {
if (this.width === width && this.height === height) {
this.mapArray.fill(0);
this.loadedExtends.forEach(v => {
v.ex.onResize?.(v.controller, width, height);
});
return;
}
this.mapData.expired = true;
@@ -99,17 +88,18 @@ export class MapLayer implements IMapLayer {
expired: false,
array: this.mapArray
};
this.loadedExtends.forEach(v => {
v.ex.onResize?.(v.controller, width, height);
});
this.empty = true;
this.forEachHook((hook, controller) => {
hook.onResize?.(controller, width, height);
});
}
setBlock(block: number, x: number, y: number): void {
const index = y * this.width + x;
if (block === this.mapArray[index]) return;
this.mapArray[index] = block;
this.loadedExtends.forEach(v => {
v.ex.onUpdateBlock?.(v.controller, block, x, y);
this.forEachHook((hook, controller) => {
hook.onUpdateBlock?.(controller, block, x, y);
});
if (block !== 0) {
this.empty = false;
@@ -131,8 +121,8 @@ export class MapLayer implements IMapLayer {
const height = Math.ceil(array.length / width);
if (width === this.width && height === this.height) {
this.mapArray.set(array);
this.loadedExtends.forEach(v => {
v.ex.onUpdateArea?.(v.controller, x, y, width, height);
this.forEachHook((hook, controller) => {
hook.onUpdateArea?.(controller, x, y, width, height);
});
return;
}
@@ -159,8 +149,8 @@ export class MapLayer implements IMapLayer {
}
this.mapArray.set(array.subarray(start, start + nw), offset);
}
this.loadedExtends.forEach(v => {
v.ex.onUpdateArea?.(v.controller, x, y, width, height);
this.forEachHook((hook, controller) => {
hook.onUpdateArea?.(controller, x, y, width, height);
});
this.empty &&= empty;
}
@@ -213,51 +203,32 @@ export class MapLayer implements IMapLayer {
return this.mapData;
}
loadExtends(ex: IMapLayerExtends): boolean {
if (!this.addedExtends.has(ex.id)) return false;
ex.awake?.();
const data = this.addedExtends.get(ex.id)!;
this.loadedExtends.add(data);
return true;
protected createController(
hook: Partial<IMapLayerHooks>
): IMapLayerHookController {
return new MapLayerHookController(this, hook);
}
addExtends(ex: IMapLayerExtends): IMapLayerExtendsController {
const controller = new MapLayerExtendsController(this, ex);
this.addedExtends.set(ex.id, {
ex,
controller
});
return controller;
}
removeExtends(ex: IMapLayerExtends | string): void {
const id = typeof ex === 'string' ? ex : ex.id;
const data = this.addedExtends.get(id);
if (!data) return;
data.ex.destroy?.();
this.addedExtends.delete(id);
this.loadedExtends.delete(data);
setZIndex(zIndex: number): void {
this.zIndex = zIndex;
}
}
class MapLayerExtendsController implements IMapLayerExtendsController {
loaded: boolean = false;
class MapLayerHookController
extends HookController<IMapLayerHooks>
implements IMapLayerHookController
{
hookable: MapLayer;
constructor(
readonly layer: MapLayer,
readonly ex: IMapLayerExtends
) {}
load(): void {
this.loaded = this.layer.loadExtends(this.ex);
hook: Partial<IMapLayerHooks>
) {
super(layer, hook);
this.hookable = layer;
}
getMapData(): Readonly<IMapLayerData> {
return this.layer.getMapRef();
}
unload(): void {
this.layer.removeExtends(this.ex);
this.loaded = false;
}
}
+22 -43
View File
@@ -1,3 +1,5 @@
import { IHookable, IHookBase, IHookController } from '@motajs/common';
export interface IMapLayerData {
/** 当前引用是否过期,当地图图层内部的地图数组引用更新时,此项会变为 `true` */
expired: boolean;
@@ -5,26 +7,15 @@ export interface IMapLayerData {
array: Uint32Array;
}
export interface IMapLayerHooks {
export interface IMapLayerHooks extends IHookBase {
/**
* 当钩子准备完毕时执行,会自动分析依赖,并把依赖实例作为参数传入,遵循依赖列表的顺序
* @param dependencies 依赖列表
*/
awake(): void;
/**
* 当拓展被移除之前执行,可以用来清理相关内容
*/
destroy(): void;
/**
* 当地图大小发生变化时执行
* 当地图大小发生变化时执行,如果调用了地图的 `resize` 方法,但是地图大小没变,则不会触发
* @param controller 拓展控制器
* @param width 地图宽度
* @param height 地图高度
*/
onResize(
controller: IMapLayerExtendsController,
controller: IMapLayerHookController,
width: number,
height: number
): void;
@@ -38,7 +29,7 @@ export interface IMapLayerHooks {
* @param height 更新区域高度
*/
onUpdateArea(
controller: IMapLayerExtendsController,
controller: IMapLayerHookController,
x: number,
y: number,
width: number,
@@ -53,47 +44,37 @@ export interface IMapLayerHooks {
* @param y 更新点纵坐标
*/
onUpdateBlock(
controller: IMapLayerExtendsController,
controller: IMapLayerHookController,
block: number,
x: number,
y: number
): void;
}
export interface IMapLayerExtends extends Partial<IMapLayerHooks> {
/** 这个拓展对象的标识符 */
readonly id: string;
}
export interface IMapLayerExtendsController {
/** 当前图层拓展是否已经被加载 */
readonly loaded: boolean;
export interface IMapLayerHookController
extends IHookController<IMapLayerHooks> {
/** 拓展所属的图层对象 */
readonly layer: IMapLayer;
/**
* 加载此图层拓展,如果拓展依赖了其他拓展并且已经添加,将会自动加载其他拓展
*/
load(): void;
/**
* 获取地图数据,是对内部存储的直接引用
*/
getMapData(): Readonly<IMapLayerData>;
/**
* 结束此拓展的生命周期,释放相关资源
*/
unload(): void;
}
export interface IMapLayer {
export interface IMapLayer
extends IHookable<IMapLayerHooks, IMapLayerHookController> {
/** 地图宽度 */
readonly width: number;
/** 地图高度 */
readonly height: number;
/** 地图是否全部空白,此值具有保守性,即如果其为 `true`,则地图一定空白,但是如果其为 `false`,那么地图也有可能空白 */
/**
* 地图是否全部空白,此值具有充分性,但不具有必要性,
* 即如果其为 `true`,则地图一定空白,但是如果其为 `false`,那么地图也有可能空白
*/
readonly empty: boolean;
/** 图层纵深 */
readonly zIndex: number;
/**
* 调整地图尺寸,维持原有图块。如果尺寸变大,那么会补零,如果尺寸变小,那么会将当前数组裁剪
@@ -153,15 +134,13 @@ export interface IMapLayer {
): Uint32Array;
/**
* 添加图层拓展,使用一系列钩子与图层本身通讯。不同图层拓展没有顺序关系。
* @param ex 图层拓展对象
* @returns 图层拓展控制对象,可以通过它来控制拓展的生命周期,也可以用于获取图层内的一些数据
* 获取整个地图的地图数组,是对内部数组的直接引用
*/
addExtends(ex: IMapLayerExtends): IMapLayerExtendsController;
getMapRef(): IMapLayerData;
/**
* 移除指定的图层拓展对象
* @param ex 要移除的图层拓展对象,也可以填拓展对象的标识符
* 设置地图纵深,会影响渲染的遮挡顺序
* @param zIndex 纵深
*/
removeExtends(ex: IMapLayerExtends | string): void;
setZIndex(zIndex: number): void;
}