refactor: 地图分区与尺寸优化

This commit is contained in:
AncTe committed 2026-05-06 19:57:11 +08:00
1 parent a01caba0c8
commit 4c48b63e7e
7 files changed
+441 -85

No files matched your search

+22 -8
View File
@@ -18,6 +18,8 @@ export class LayerState
implements ILayerState
{
readonly layerList: Set<IMapLayer> = new Set();
/** 具体 MapLayer 实例列表,供内部 resize 使用 */
private readonly mapLayerList: Set<MapLayer> = new Set();
/** 图层到图层别名映射 */
readonly layerAliasMap: WeakMap<IMapLayer, string> = new WeakMap();
/** 图层别名到图层的映射 */
@@ -35,10 +37,18 @@ export class LayerState
/** 楼层级脏标记 */
private dirty: boolean = false;
addLayer(width: number, height: number): IMapLayer {
const array = new Uint32Array(width * height);
const layer = new MapLayer(array, width, height);
constructor(
public width: number,
public height: number
) {
super();
}
addLayer(): IMapLayer {
const array = new Uint32Array(this.width * this.height);
const layer = new MapLayer(array, this.width, this.height);
this.layerList.add(layer);
this.mapLayerList.add(layer);
this.forEachHook(hook => {
hook.onUpdateLayer?.(this.layerList);
});
@@ -50,6 +60,7 @@ export class LayerState
removeLayer(layer: IMapLayer): void {
this.layerList.delete(layer);
this.mapLayerList.delete(layer as MapLayer);
const alias = this.layerAliasMap.get(layer);
if (alias) {
const symbol = Symbol.for(alias);
@@ -89,15 +100,18 @@ export class LayerState
}
resizeLayer(
layer: IMapLayer,
width: number,
height: number,
keepBlock: boolean = false
): void {
if (keepBlock) {
layer.resize(width, height);
} else {
layer.resize2(width, height);
this.width = width;
this.height = height;
for (const layer of this.mapLayerList) {
if (keepBlock) {
layer.resize(width, height);
} else {
layer.resize2(width, height);
}
}
}
+104 -15
View File
@@ -6,21 +6,65 @@ import {
IMapLayer,
IMapLayerSave,
IMapStore,
IMapStoreSave
IMapStoreSave,
MapArea
} from './types';
import { LayerState } from './layerState';
import { uniq } from 'lodash-es';
export class MapStore implements IMapStore {
/** 楼层 id 到状态对象的映射 */
private readonly mapData: Map<string, LayerState> = new Map();
/** 所有楼层 id 的只读集合视图 */
readonly maps: Set<string> = new Set();
/** 所有楼层 id 的有序数组 */
readonly maps: string[] = [];
/** 差分压缩参考基准,首次 compareWith 后设置,之后不再更新 */
private refData: Map<string, Map<number, Uint32Array>> | null = null;
//#region 楼层访问
/** 分区列表 */
private areaList: Set<MapArea> = new Set();
/** 上一次调用 notifyEnterFloor 传入的楼层 id */
private lastFloorId: string | null = null;
/** 自动分区激活器开关 */
private autoActivitorEnabled: boolean = false;
//#region 楼层管理
createLayerState(id: string, width: number, height: number): ILayerState {
if (this.mapData.has(id)) {
logger.warn(121, id);
} else {
this.maps.push(id);
}
const state = new LayerState(width, height);
// 若 refData 已存在,新楼层直接视为全脏
if (this.refData !== null) {
state.setDirty(true);
}
this.mapData.set(id, state);
return state;
}
setMapList(maps: string[]): void {
this.maps.length = 0;
this.maps.push(...uniq(maps));
}
useManualOrder(sort: (arr: string[]) => string[]): void {
const copy = this.maps.slice();
const sorted = sort(copy);
const oldSet = new Set(this.maps);
const newSet = new Set(sorted);
if (oldSet.size !== newSet.size || !newSet.isSubsetOf(oldSet)) {
logger.warn(125);
return;
}
this.maps.length = 0;
this.maps.push(...uniq(sorted));
}
getLayerState(id: string): ILayerState | null {
return this.mapData.get(id) ?? null;
@@ -34,20 +78,65 @@ export class MapStore implements IMapStore {
//#endregion
//#region 楼层管理
//#region 分区管理
createLayerState(id: string): ILayerState {
if (this.mapData.has(id)) {
logger.warn(121, id);
setArea(areas: Set<MapArea>): void {
this.areaList = areas;
}
activeArea(id: string): void {
const idx = this.maps.indexOf(id);
if (idx === -1) return;
const area = this.findAreaByIndex(idx);
if (!area) return;
this.setAreaActive(area, true);
}
deactiveArea(id: string): void {
const idx = this.maps.indexOf(id);
if (idx === -1) return;
const area = this.findAreaByIndex(idx);
if (!area) return;
this.setAreaActive(area, false);
}
useAutoActivitor(enable: boolean): void {
this.autoActivitorEnabled = enable;
}
notifyEnterFloor(id: string): void {
if (!this.autoActivitorEnabled) return;
const idx = this.maps.indexOf(id);
if (idx === -1) return;
const area = this.findAreaByIndex(idx);
if (!area) return;
if (this.lastFloorId !== null) {
this.deactiveArea(this.lastFloorId);
}
const state = new LayerState();
// 若 refData 已存在,新楼层直接视为全脏
if (this.refData !== null) {
state.setDirty(true);
this.activeArea(id);
this.lastFloorId = id;
}
private findAreaByIndex(idx: number): MapArea | null {
for (const area of this.areaList) {
for (const interval of area) {
if (idx >= interval.start && idx <= interval.end) {
return area;
}
}
}
return null;
}
private setAreaActive(area: MapArea, active: boolean): void {
for (const interval of area) {
for (let i = interval.start; i <= interval.end; i++) {
const floorId = this.maps[i];
if (floorId !== undefined) {
this.setMapActiveStatus(floorId, active);
}
}
}
this.mapData.set(id, state);
this.maps.add(id);
return state;
}
//#endregion
+67 -31
View File
@@ -75,20 +75,6 @@ export interface IMapLayer extends IHookable<
/** 图层纵深 */
readonly zIndex: number;
/**
* 调整地图尺寸,维持原有图块。如果尺寸变大,那么会补零,如果尺寸变小,那么会将当前数组裁剪
* @param width 地图宽度
* @param height 地图高度
*/
resize(width: number, height: number): void;
/**
* 调整地图尺寸,但是将地图全部重置为零,不保留原地图数据
* @param width 地图宽度
* @param height 地图高度
*/
resize2(width: number, height: number): void;
/**
* 设置某一点的图块
* @param block 图块数字
@@ -225,13 +211,15 @@ export interface ILayerState extends IHookable<ILayerStateHooks> {
readonly layerList: Set<IMapLayer>;
/** 此楼层是否处于激活状态 */
readonly active: boolean;
/** 此楼层的地图宽度 */
readonly width: number;
/** 此楼层的地图高度 */
readonly height: number;
/**
* 添加图层
* @param width 地图宽度
* @param height 地图高度
* 添加图层,使用楼层预设的宽高
*/
addLayer(width: number, height: number): IMapLayer;
addLayer(): IMapLayer;
/**
* 移除指定图层
@@ -265,18 +253,12 @@ export interface ILayerState extends IHookable<ILayerStateHooks> {
getLayerAlias(layer: IMapLayer): string | undefined;
/**
* 重新设置图层的大小
* @param layer 图层对象
* @param width 新的图层宽度
* @param height 新的图层高度
* 重新设置所有图层的大小,同时更新楼层预设宽高
* @param width 新的地图宽度
* @param height 新的地图高度
* @param keepBlock 是否保留原有图块,默认不保留
*/
resizeLayer(
layer: IMapLayer,
width: number,
height: number,
keepBlock?: boolean
): void;
resizeLayer(width: number, height: number, keepBlock?: boolean): void;
/**
* 设置背景图块
@@ -336,9 +318,18 @@ export interface IMapStoreSave {
readonly floors: ReadonlyMap<string, ILayerStateSave>;
}
/** 单段闭区间 [start, end],start 和 end 均为 maps 下标 */
export interface IMapAreaInterval {
readonly start: number;
readonly end: number;
}
/** 一个区域由一个或多个独立区间组成 */
export type MapArea = IMapAreaInterval[];
export interface IMapStore extends ISaveableContent<IMapStoreSave> {
/** 所有楼层的 id 集合 */
readonly maps: ReadonlySet<string>;
/** 所有楼层的 id 有序数组 */
readonly maps: ReadonlyArray<string>;
/**
* 获取指定 id 的楼层状态,不存在则返回 null
@@ -355,8 +346,10 @@ export interface IMapStore extends ISaveableContent<IMapStoreSave> {
/**
* 创建并注册一个空白楼层,若 id 已存在则警告并覆盖,返回楼层状态对象
* @param id 楼层 id
* @param width 地图宽度
* @param height 地图高度
*/
createLayerState(id: string): ILayerState;
createLayerState(id: string, width: number, height: number): ILayerState;
/**
* 获取指定 id 的楼层是否激活,不存在的 id 返回 false
@@ -391,4 +384,47 @@ export interface IMapStore extends ISaveableContent<IMapStoreSave> {
* @param ref 外层 key = 楼层 id,内层 key = zIndex,value = 图层完整图块数据
*/
compareWith(ref: Map<string, Map<number, Uint32Array>>): void;
/**
* 设定楼层有序列表。设定后有序列表将用于分区索引计算
* @param maps 楼层 id 数组
*/
setMapList(maps: string[]): void;
/**
* 使用自定义排序函数重排 maps。排序函数接收当前列表的副本,返回新顺序。
* 若返回的数组元素集合与原列表不一致,则警告并放弃本次排序
* @param sort 排序函数
*/
useManualOrder(sort: (arr: string[]) => string[]): void;
/**
* 设定分区列表。每个分区由一个或多个区间组成
* @param areas 分区集合
*/
setArea(areas: Set<MapArea>): void;
/**
* 激活指定楼层所属分区的所有楼层
* @param id 楼层 id
*/
activeArea(id: string): void;
/**
* 去激活指定楼层所属分区的所有楼层
* @param id 楼层 id
*/
deactiveArea(id: string): void;
/**
* 开启或关闭自动分区激活器
* @param enable 是否开启
*/
useAutoActivitor(enable: boolean): void;
/**
* 通知当前进入的楼层。开启自动激活器时,将自动去激活上一个分区并激活新分区
* @param id 楼层 id
*/
notifyEnterFloor(id: string): void;
}