From 637647dc17f1a6cf4a527b0603834ca047535cc3 Mon Sep 17 00:00:00 2001 From: unanmed <1319491857@qq.com> Date: Tue, 28 Oct 2025 23:58:37 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=9B=BE=E9=9B=86=E8=84=8F=E6=A0=87?= =?UTF-8?q?=E8=AE=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../client-base/src/material/asset.ts | 30 +++ .../client-base/src/material/autotile.ts | 12 +- .../client-base/src/material/index.ts | 6 + .../client-base/src/material/manager.ts | 59 ++++- .../client-base/src/material/types.ts | 205 +++++++++++------- 5 files changed, 220 insertions(+), 92 deletions(-) create mode 100644 packages-user/client-base/src/material/asset.ts diff --git a/packages-user/client-base/src/material/asset.ts b/packages-user/client-base/src/material/asset.ts new file mode 100644 index 0000000..f1242be --- /dev/null +++ b/packages-user/client-base/src/material/asset.ts @@ -0,0 +1,30 @@ +import { ITextureComposedData } from '@motajs/render-assets'; +import { IMaterialAsset } from './types'; + +export class MaterialAsset implements IMaterialAsset { + /** 标记列表 */ + private readonly marks: Map = new Map(); + /** 脏标记,所有值小于此标记的都视为需要更新 */ + private dirtyFlag: number = 0; + + constructor(readonly data: ITextureComposedData) {} + + dirty(): void { + this.dirtyFlag++; + } + + mark(): symbol { + const symbol = Symbol(); + this.marks.set(symbol, this.dirtyFlag); + return symbol; + } + + unmark(mark: symbol): void { + this.marks.delete(mark); + } + + dirtySince(mark: symbol): boolean { + const value = this.marks.get(mark) ?? -1; + return value < this.dirtyFlag; + } +} diff --git a/packages-user/client-base/src/material/autotile.ts b/packages-user/client-base/src/material/autotile.ts index 509f1bb..0de4e3b 100644 --- a/packages-user/client-base/src/material/autotile.ts +++ b/packages-user/client-base/src/material/autotile.ts @@ -171,6 +171,10 @@ export class AutotileProcessor implements IAutotileProcessor { return this.fromStaticRenderable(tile.static(), connection); } + /** + * 根据静态可渲染对象获取自动元件的帧列表 + * @param renderable 静态可渲染对象 + */ private getStaticRectList( renderable: ITextureRenderable ): AutotileFrameList { @@ -194,11 +198,17 @@ export class AutotileProcessor implements IAutotileProcessor { } } + /** + * 对自动元件连接执行偏移操作,偏移至自动元件在图像源中的所在矩形范围 + * @param ox 横向偏移量 + * @param oy 纵向偏移量 + * @param connection 自动元件连接信息 + */ private getConnectedRect( ox: number, oy: number, connection: ConnectedAutotile - ): ConnectedAutotile | null { + ): ConnectedAutotile { const { lt, rt, rb, lb } = connection; return { diff --git a/packages-user/client-base/src/material/index.ts b/packages-user/client-base/src/material/index.ts index 39ede5d..0e8c3b5 100644 --- a/packages-user/client-base/src/material/index.ts +++ b/packages-user/client-base/src/material/index.ts @@ -7,4 +7,10 @@ export function createMaterial() { }); } +export * from './autotile'; +export * from './builder'; +export * from './fallback'; +export * from './ins'; export * from './manager'; +export * from './types'; +export * from './utils'; diff --git a/packages-user/client-base/src/material/manager.ts b/packages-user/client-base/src/material/manager.ts index be4c7d1..30fc2f1 100644 --- a/packages-user/client-base/src/material/manager.ts +++ b/packages-user/client-base/src/material/manager.ts @@ -19,12 +19,14 @@ import { IMaterialAssetData, BlockCls, IBigImageData, - IAssetBuilder + IAssetBuilder, + IMaterialAsset } from './types'; import { logger } from '@motajs/common'; import { getClsByString } from './utils'; import { isNil } from 'lodash-es'; import { AssetBuilder } from './builder'; +import { MaterialAsset } from './asset'; export class MaterialManager implements IMaterialManager { readonly tileStore: ITextureStore = new TextureStore(); @@ -34,7 +36,8 @@ export class MaterialManager implements IMaterialManager { readonly bigImageStore: ITextureStore = new TextureStore(); /** 图集信息存储 */ - readonly assetDataStore: Map = new Map(); + readonly assetDataStore: Map = new Map(); + /** 大怪物数据 */ readonly bigImageData: Map = new Map(); /** tileset 中 `Math.floor(id / 10000) + 1` 映射到 tileset 对应索引的映射,用于处理图块超出 10000 的 tileset */ @@ -60,6 +63,9 @@ export class MaterialManager implements IMaterialManager { /** 是否已经构建过素材 */ private built: boolean = false; + /** 标记列表 */ + private readonly markList: symbol[] = []; + constructor() { this.assetBuilder.pipe(this.assetStore); } @@ -264,6 +270,23 @@ export class MaterialManager implements IMaterialManager { return newTexture; } + /** + * 检查图集状态,如果已存在图集则标记为脏,否则新增图集 + * @param data 图集数据 + */ + private checkAssetDirty(data: ITextureComposedData) { + const asset = this.assetDataStore.get(data.index); + if (asset) { + // 如果不是新图集,需要标记为脏 + asset.dirty(); + } else { + // 如果有新图集,需要添加 + const alias = `asset-${data.index}`; + this.assetStore.alias(data.index, alias); + this.assetDataStore.set(data.index, new MaterialAsset(data)); + } + } + cacheTileset(identifier: number): ITexture | null { const newTexture = this.getTilesetOwnTexture(identifier); if (!newTexture) return null; @@ -273,6 +296,7 @@ export class MaterialManager implements IMaterialManager { this.numIdMap.set(identifier, `X${identifier}`); const data = this.assetBuilder.addTexture(newTexture); newTexture.toAsset(data); + this.checkAssetDirty(data); return newTexture; } @@ -295,10 +319,11 @@ export class MaterialManager implements IMaterialManager { const data = this.assetBuilder.addTextureList(toAdd); const res = [...data]; - res.forEach(v => { - v.assetMap.keys().forEach(tex => { - if (set.has(tex)) tex.toAsset(v); + res.forEach(data => { + data.assetMap.keys().forEach(tex => { + if (set.has(tex)) tex.toAsset(data); }); + this.checkAssetDirty(data); }); return toAdd; @@ -313,13 +338,13 @@ export class MaterialManager implements IMaterialManager { const data = this.assetBuilder.addTextureList(this.tileStore.values()); const arr = [...data]; const res: IMaterialAssetData[] = []; - arr.forEach((v, i) => { - const alias = `asset-${i}`; - this.assetStore.alias(i, alias); - this.assetDataStore.set(i, v); + arr.forEach(v => { + const alias = `asset-${v.index}`; + this.assetStore.alias(v.index, alias); + this.assetDataStore.set(v.index, new MaterialAsset(v)); const data: IMaterialAssetData = { data: v, - identifier: i, + identifier: v.index, alias, store: this.assetStore }; @@ -331,11 +356,11 @@ export class MaterialManager implements IMaterialManager { return res; } - getAsset(identifier: number): ITextureComposedData | null { + getAsset(identifier: number): IMaterialAsset | null { return this.assetDataStore.get(identifier) ?? null; } - getAssetByAlias(alias: string): ITextureComposedData | null { + getAssetByAlias(alias: string): IMaterialAsset | null { const id = this.assetStore.identifierOf(alias); if (isNil(id)) return null; return this.assetDataStore.get(id) ?? null; @@ -406,4 +431,14 @@ export class MaterialManager implements IMaterialManager { if (isNil(identifier)) return null; return this.bigImageData.get(identifier) ?? null; } + + getIfBigImage(identifier: number): ITexture | null { + const bigImage = this.bigImageData.get(identifier) ?? null; + if (bigImage) return bigImage; + if (identifier < 10000) { + return this.tileStore.getTexture(identifier); + } else { + return this.cacheTileset(identifier); + } + } } diff --git a/packages-user/client-base/src/material/types.ts b/packages-user/client-base/src/material/types.ts index 0eceb1a..7a69d20 100644 --- a/packages-user/client-base/src/material/types.ts +++ b/packages-user/client-base/src/material/types.ts @@ -90,6 +90,37 @@ export interface IBigImageData { readonly store: ITextureStore; } +export interface IAssetDirtyMarker { + /** + * 标记为脏,即进行了一次更新 + */ + dirty(): void; +} + +export interface IAssetDirtyTracker { + /** + * 对图集状态进行标记 + */ + mark(): symbol; + + /** + * 取消指定标记符号 + * @param mark 标记符号 + */ + unmark(mark: symbol): void; + + /** + * 从指定标记符号开始,图集是否发生了变动 + * @param mark 标记符号 + */ + dirtySince(mark: symbol): boolean; +} + +export interface IMaterialAsset extends IAssetDirtyTracker, IAssetDirtyMarker { + /** 图集的贴图数据 */ + readonly data: ITextureComposedData; +} + export interface IAutotileProcessor { /** 该自动元件处理器使用的素材管理器 */ readonly manager: IMaterialManager; @@ -162,7 +193,98 @@ export interface IAutotileProcessor { ): Generator | null; } -export interface IMaterialManager { +export interface IMaterialGetter { + /** + * 根据图块数字获取图块,可以获取额外素材,会自动将未缓存的额外素材缓存 + * @param identifier 图块的图块数字 + */ + getTile(identifier: number): ITexture | null; + + /** + * 根据图块标识符获取图块类型 + * @param identifier 图块标识符,即图块数字 + */ + getBlockCls(identifier: number): BlockCls; + + /** + * 判断一个图块是否包含 `bigImage` 贴图,即是否是大怪物 + * @param identifier 图块标识符,即图块数字 + */ + isBigImage(identifier: number): boolean; + + /** + * 根据图块标识符获取一个图块的 `bigImage` 贴图 + * @param identifier 图块标识符,即图块数字 + */ + getBigImage(identifier: number): ITexture | null; + + /** + * 根据图块标识符,首先判断是否是 `bigImage` 贴图,如果是,则返回 `bigImage` 贴图, + * 否则返回普通贴图。如果图块不存在,则返回 `null` + * @param identifier 图块标识符,即图块数字 + */ + getIfBigImage(identifier: number): ITexture | null; + + /** + * 根据标识符获取图集信息 + * @param identifier 图集的标识符 + */ + getAsset(identifier: number): IMaterialAsset | null; + + /** + * 根据额外素材索引获取额外素材 + * @param identifier 额外素材的索引 + */ + getTileset(identifier: number): ITexture | null; + + /** + * 根据图片的索引获取图片 + * @param identifier 图片的索引 + */ + getImage(identifier: number): ITexture | null; +} + +export interface IMaterialAliasGetter { + /** + * 根据图块 id 获取图块,可以获取额外素材,会自动将未缓存的额外素材缓存 + * @param alias 图块 id + */ + getTileByAlias(alias: string): ITexture | null; + + /** + * 根据额外素材名称获取额外素材 + * @param alias 额外素材名称 + */ + getTilesetByAlias(alias: string): ITexture | null; + + /** + * 根据图片名称获取图片 + * @param alias 图片名称 + */ + getImageByAlias(alias: string): ITexture | null; + + /** + * 根据别名获取图集信息 + * @param alias 图集的别名 + */ + getAssetByAlias(alias: string): IMaterialAsset | null; + + /** + * 根据图块别名获取图块类型 + * @param alias 图块别名,即图块的 id + */ + getBlockClsByAlias(alias: string): BlockCls; + + /** + * 根据图块别名获取一个图块的 `bigImage` 贴图 + * @param alias 图块别名,即图块的 id + */ + getBigImageByAlias(alias: string): ITexture | null; +} + +export interface IMaterialManager + extends IMaterialGetter, + IMaterialAliasGetter { /** 贴图存储,把 terrains 等内容单独分开存储 */ readonly tileStore: ITextureStore; /** tilesets 贴图存储,每个 tileset 是一个贴图对象 */ @@ -174,6 +296,9 @@ export interface IMaterialManager { /** bigImage 存储,存储大怪物数据 */ readonly bigImageStore: ITextureStore; + /** 图集信息存储 */ + readonly assetDataStore: Iterable<[number, IMaterialAsset]>; + /** 图块类型映射 */ readonly clsMap: Map; @@ -231,42 +356,6 @@ export interface IMaterialManager { identifier: IIndexedIdentifier ): IMaterialData; - /** - * 根据图块数字获取图块,可以获取额外素材,会自动将未缓存的额外素材缓存 - * @param identifier 图块的图块数字 - */ - getTile(identifier: number): ITexture | null; - - /** - * 根据额外素材索引获取额外素材 - * @param identifier 额外素材的索引 - */ - getTileset(identifier: number): ITexture | null; - - /** - * 根据图片的索引获取图片 - * @param identifier 图片的索引 - */ - getImage(identifier: number): ITexture | null; - - /** - * 根据图块 id 获取图块,可以获取额外素材,会自动将未缓存的额外素材缓存 - * @param alias 图块 id - */ - getTileByAlias(alias: string): ITexture | null; - - /** - * 根据额外素材名称获取额外素材 - * @param alias 额外素材名称 - */ - getTilesetByAlias(alias: string): ITexture | null; - - /** - * 根据图片名称获取图片 - * @param alias 图片名称 - */ - getImageByAlias(alias: string): ITexture | null; - /** * 缓存某个 tileset * @param identifier tileset 的标识符,即图块数字 @@ -286,18 +375,6 @@ export interface IMaterialManager { */ buildAssets(): Iterable; - /** - * 根据标识符获取图集信息 - * @param identifier 图集的标识符 - */ - getAsset(identifier: number): ITextureComposedData | null; - - /** - * 根据别名获取图集信息 - * @param alias 图集的别名 - */ - getAssetByAlias(alias: string): ITextureComposedData | null; - /** * 根据图块标识符在图集中获取对应的可渲染对象 * @param identifier 图块标识符,即图块数字 @@ -310,18 +387,6 @@ export interface IMaterialManager { */ getRenderableByAlias(alias: string): ITextureRenderable | null; - /** - * 根据图块标识符获取图块类型 - * @param identifier 图块标识符,即图块数字 - */ - getBlockCls(identifier: number): BlockCls; - - /** - * 根据图块别名获取图块类型 - * @param alias 图块别名,即图块的 id - */ - getBlockClsByAlias(alias: string): BlockCls; - /** * 根据图块别名获取图块标识符,即图块数字 * @param alias 图块别名,即图块的 id @@ -340,24 +405,6 @@ export interface IMaterialManager { * @param image `bigImage` 对应的贴图对象 */ setBigImage(identifier: number, image: ITexture): IBigImageData; - - /** - * 判断一个图块是否包含 `bigImage` 贴图,即是否是大怪物 - * @param identifier 图块标识符,即图块数字 - */ - isBigImage(identifier: number): boolean; - - /** - * 根据图块标识符获取一个图块的 `bigImage` 贴图 - * @param identifier 图块标识符,即图块数字 - */ - getBigImage(identifier: number): ITexture | null; - - /** - * 根据图块别名获取一个图块的 `bigImage` 贴图 - * @param alias 图块别名,即图块的 id - */ - getBigImageByAlias(alias: string): ITexture | null; } export interface IAssetBuilder {