1029 lines
33 KiB
TypeScript
1029 lines
33 KiB
TypeScript
|
|
import { Component, ECSComponent, Serializable, Serialize, Property } from '@esengine/ecs-framework';
|
|||
|
|
import type { IResourceComponent, ResourceReference } from '@esengine/asset-system';
|
|||
|
|
import { UVHelper } from '@esengine/asset-system';
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Resize anchor point for tilemap expansion
|
|||
|
|
* 瓦片地图扩展的锚点位置
|
|||
|
|
*/
|
|||
|
|
export type ResizeAnchor =
|
|||
|
|
| 'top-left' | 'top-center' | 'top-right'
|
|||
|
|
| 'middle-left' | 'center' | 'middle-right'
|
|||
|
|
| 'bottom-left' | 'bottom-center' | 'bottom-right';
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Tileset data interface
|
|||
|
|
* 图块集数据接口
|
|||
|
|
*/
|
|||
|
|
export interface ITilesetData {
|
|||
|
|
/** Tileset name | 图块集名称 */
|
|||
|
|
name: string;
|
|||
|
|
/** Data format version | 数据格式版本 */
|
|||
|
|
version: number;
|
|||
|
|
/** Image file path | 图片文件路径 */
|
|||
|
|
image: string;
|
|||
|
|
/** Image width in pixels | 图片宽度(像素) */
|
|||
|
|
imageWidth: number;
|
|||
|
|
/** Image height in pixels | 图片高度(像素) */
|
|||
|
|
imageHeight: number;
|
|||
|
|
/** Single tile width in pixels | 单个图块宽度(像素) */
|
|||
|
|
tileWidth: number;
|
|||
|
|
/** Single tile height in pixels | 单个图块高度(像素) */
|
|||
|
|
tileHeight: number;
|
|||
|
|
/** Total number of tiles | 图块总数 */
|
|||
|
|
tileCount: number;
|
|||
|
|
/** Number of tile columns | 图块列数 */
|
|||
|
|
columns: number;
|
|||
|
|
/** Number of tile rows | 图块行数 */
|
|||
|
|
rows: number;
|
|||
|
|
/** Margin around tileset in pixels | 图块集边距(像素) */
|
|||
|
|
margin?: number;
|
|||
|
|
/** Spacing between tiles in pixels | 图块间距(像素) */
|
|||
|
|
spacing?: number;
|
|||
|
|
/** Individual tile metadata | 单个图块元数据 */
|
|||
|
|
tiles?: Array<{
|
|||
|
|
id: number;
|
|||
|
|
type?: string;
|
|||
|
|
properties?: Record<string, unknown>;
|
|||
|
|
}>;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Layer data interface
|
|||
|
|
* 图层数据接口
|
|||
|
|
*/
|
|||
|
|
export interface ITilemapLayerData {
|
|||
|
|
/** Unique layer identifier | 图层唯一标识符 */
|
|||
|
|
id: string;
|
|||
|
|
/** Layer display name | 图层显示名称 */
|
|||
|
|
name: string;
|
|||
|
|
/** Layer visibility | 图层可见性 */
|
|||
|
|
visible: boolean;
|
|||
|
|
/** Layer opacity (0-1) | 图层不透明度(0-1) */
|
|||
|
|
opacity: number;
|
|||
|
|
/** Tile index data array (row-major order) | 图块索引数据数组(行优先顺序) */
|
|||
|
|
data: number[];
|
|||
|
|
/** Default tileset index for this layer | 此图层的默认图块集索引 */
|
|||
|
|
tilesetIndex?: number;
|
|||
|
|
/** Layer X offset in pixels | 图层X偏移(像素) */
|
|||
|
|
offsetX?: number;
|
|||
|
|
/** Layer Y offset in pixels | 图层Y偏移(像素) */
|
|||
|
|
offsetY?: number;
|
|||
|
|
/** Custom layer properties | 自定义图层属性 */
|
|||
|
|
properties?: Record<string, unknown>;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Tileset reference info
|
|||
|
|
* 图块集引用信息
|
|||
|
|
*/
|
|||
|
|
export interface ITilesetRef {
|
|||
|
|
/** Tileset image source path | 图块集图片源路径 */
|
|||
|
|
source: string;
|
|||
|
|
/** First global tile ID for this tileset | 此图块集的第一个全局图块ID */
|
|||
|
|
firstGid: number;
|
|||
|
|
/** Loaded tileset data | 已加载的图块集数据 */
|
|||
|
|
data?: ITilesetData;
|
|||
|
|
/** GPU texture ID for rendering | 用于渲染的GPU纹理ID */
|
|||
|
|
textureId?: number;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Tilemap data interface
|
|||
|
|
* 瓦片地图数据接口
|
|||
|
|
*/
|
|||
|
|
export interface ITilemapData {
|
|||
|
|
/** Tilemap name | 瓦片地图名称 */
|
|||
|
|
name: string;
|
|||
|
|
/** Data format version | 数据格式版本 */
|
|||
|
|
version: number;
|
|||
|
|
/** Map width in tiles | 地图宽度(图块数) */
|
|||
|
|
width: number;
|
|||
|
|
/** Map height in tiles | 地图高度(图块数) */
|
|||
|
|
height: number;
|
|||
|
|
/** Single tile width in pixels | 单个图块宽度(像素) */
|
|||
|
|
tileWidth: number;
|
|||
|
|
/** Single tile height in pixels | 单个图块高度(像素) */
|
|||
|
|
tileHeight: number;
|
|||
|
|
/** Array of tileset references | 图块集引用数组 */
|
|||
|
|
tilesets: ITilesetRef[];
|
|||
|
|
/** Array of layer data | 图层数据数组 */
|
|||
|
|
layers: ITilemapLayerData[];
|
|||
|
|
/** Collision data array | 碰撞数据数组 */
|
|||
|
|
collisionData?: number[];
|
|||
|
|
/** Custom tilemap properties | 自定义瓦片地图属性 */
|
|||
|
|
properties?: Record<string, unknown>;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Tilemap Component - Manages tile-based 2D map rendering
|
|||
|
|
* 瓦片地图组件 - 管理基于瓦片的2D地图渲染
|
|||
|
|
*/
|
|||
|
|
@ECSComponent('Tilemap')
|
|||
|
|
@Serializable({ version: 2, typeId: 'Tilemap' })
|
|||
|
|
export class TilemapComponent extends Component implements IResourceComponent {
|
|||
|
|
/** Tilemap asset GUID reference | 瓦片地图资源GUID引用 */
|
|||
|
|
@Serialize()
|
|||
|
|
@Property({ type: 'asset', label: 'Tilemap', fileExtension: '.tilemap.json' })
|
|||
|
|
public tilemapAssetGuid: string = '';
|
|||
|
|
|
|||
|
|
@Serialize()
|
|||
|
|
private _width: number = 10;
|
|||
|
|
|
|||
|
|
@Serialize()
|
|||
|
|
private _height: number = 10;
|
|||
|
|
|
|||
|
|
/** Map width in tiles | 地图宽度(图块数) */
|
|||
|
|
@Property({ type: 'integer', label: 'Width (Tiles)', min: 1 })
|
|||
|
|
public get width(): number {
|
|||
|
|
return this._width;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public set width(value: number) {
|
|||
|
|
if (value !== this._width && value > 0) {
|
|||
|
|
this.resize(value, this._height);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/** Map height in tiles | 地图高度(图块数) */
|
|||
|
|
@Property({ type: 'integer', label: 'Height (Tiles)', min: 1 })
|
|||
|
|
public get height(): number {
|
|||
|
|
return this._height;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public set height(value: number) {
|
|||
|
|
if (value !== this._height && value > 0) {
|
|||
|
|
this.resize(this._width, value);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/** Single tile width in pixels | 单个图块宽度(像素) */
|
|||
|
|
@Serialize()
|
|||
|
|
@Property({ type: 'integer', label: 'Tile Width', min: 1 })
|
|||
|
|
public tileWidth: number = 32;
|
|||
|
|
|
|||
|
|
/** Single tile height in pixels | 单个图块高度(像素) */
|
|||
|
|
@Serialize()
|
|||
|
|
@Property({ type: 'integer', label: 'Tile Height', min: 1 })
|
|||
|
|
public tileHeight: number = 32;
|
|||
|
|
|
|||
|
|
/** Component visibility | 组件可见性 */
|
|||
|
|
@Serialize()
|
|||
|
|
@Property({ type: 'boolean', label: 'Visible' })
|
|||
|
|
public visible: boolean = true;
|
|||
|
|
|
|||
|
|
/** Rendering sort order | 渲染排序顺序 */
|
|||
|
|
@Serialize()
|
|||
|
|
@Property({ type: 'integer', label: 'Sorting Order' })
|
|||
|
|
public sortingOrder: number = 0;
|
|||
|
|
|
|||
|
|
/** Tint color in hex format | 着色颜色(十六进制格式) */
|
|||
|
|
@Serialize()
|
|||
|
|
@Property({ type: 'color', label: 'Color' })
|
|||
|
|
public color: string = '#ffffff';
|
|||
|
|
|
|||
|
|
/** Opacity value (0-1) | 不透明度(0-1) */
|
|||
|
|
@Serialize()
|
|||
|
|
@Property({ type: 'number', label: 'Alpha', min: 0, max: 1, step: 0.01 })
|
|||
|
|
public alpha: number = 1;
|
|||
|
|
|
|||
|
|
/** Flag indicating render data needs update | 标记渲染数据需要更新 */
|
|||
|
|
public renderDirty: boolean = true;
|
|||
|
|
|
|||
|
|
// ===== 多Tileset =====
|
|||
|
|
|
|||
|
|
@Serialize()
|
|||
|
|
private _tilesets: ITilesetRef[] = [];
|
|||
|
|
|
|||
|
|
private _tilesetsData: Map<number, ITilesetData> = new Map();
|
|||
|
|
|
|||
|
|
// ===== 多图层 =====
|
|||
|
|
|
|||
|
|
@Serialize()
|
|||
|
|
private _layers: ITilemapLayerData[] = [];
|
|||
|
|
|
|||
|
|
private _layersData: Map<string, Uint32Array> = new Map();
|
|||
|
|
|
|||
|
|
@Serialize()
|
|||
|
|
private _activeLayerIndex: number = 0;
|
|||
|
|
|
|||
|
|
// ===== 碰撞数据 =====
|
|||
|
|
|
|||
|
|
@Serialize()
|
|||
|
|
private _collisionDataArray: number[] = [];
|
|||
|
|
|
|||
|
|
private _collisionData: Uint32Array = new Uint32Array(0);
|
|||
|
|
|
|||
|
|
// ===== Getters =====
|
|||
|
|
|
|||
|
|
/** All tileset references | 所有图块集引用 */
|
|||
|
|
get tilesets(): readonly ITilesetRef[] {
|
|||
|
|
return this._tilesets;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/** All layer data | 所有图层数据 */
|
|||
|
|
get layers(): readonly ITilemapLayerData[] {
|
|||
|
|
return this._layers;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/** Current active layer index | 当前活动图层索引 */
|
|||
|
|
get activeLayerIndex(): number {
|
|||
|
|
return this._activeLayerIndex;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
set activeLayerIndex(value: number) {
|
|||
|
|
if (value >= 0 && value < this._layers.length) {
|
|||
|
|
this._activeLayerIndex = value;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/** Current active layer data | 当前活动图层数据 */
|
|||
|
|
get activeLayer(): ITilemapLayerData | undefined {
|
|||
|
|
return this._layers[this._activeLayerIndex];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/** Total map width in pixels | 地图总宽度(像素) */
|
|||
|
|
get pixelWidth(): number {
|
|||
|
|
return this._width * this.tileWidth;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/** Total map height in pixels | 地图总高度(像素) */
|
|||
|
|
get pixelHeight(): number {
|
|||
|
|
return this._height * this.tileHeight;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/** Raw collision data array | 原始碰撞数据数组 */
|
|||
|
|
get collisionData(): Uint32Array {
|
|||
|
|
return this._collisionData;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ===== Initialization | 初始化 =====
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Initialize an empty tilemap with default layer
|
|||
|
|
* 初始化一个带有默认图层的空瓦片地图
|
|||
|
|
* @param width Map width in tiles | 地图宽度(图块数)
|
|||
|
|
* @param height Map height in tiles | 地图高度(图块数)
|
|||
|
|
*/
|
|||
|
|
initializeEmpty(width: number, height: number): void {
|
|||
|
|
this._width = width;
|
|||
|
|
this._height = height;
|
|||
|
|
|
|||
|
|
const defaultLayer: ITilemapLayerData = {
|
|||
|
|
id: 'default',
|
|||
|
|
name: 'Layer 0',
|
|||
|
|
visible: true,
|
|||
|
|
opacity: 1,
|
|||
|
|
data: new Array(width * height).fill(0)
|
|||
|
|
};
|
|||
|
|
this._layers = [defaultLayer];
|
|||
|
|
this._layersData.set('default', new Uint32Array(width * height));
|
|||
|
|
this._activeLayerIndex = 0;
|
|||
|
|
|
|||
|
|
this.renderDirty = true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Apply tilemap data from external source
|
|||
|
|
* 从外部数据源应用瓦片地图数据
|
|||
|
|
* @param data Tilemap data to apply | 要应用的瓦片地图数据
|
|||
|
|
*/
|
|||
|
|
applyTilemapData(data: ITilemapData): void {
|
|||
|
|
this._width = data.width;
|
|||
|
|
this._height = data.height;
|
|||
|
|
this.tileWidth = data.tileWidth;
|
|||
|
|
this.tileHeight = data.tileHeight;
|
|||
|
|
|
|||
|
|
// 加载Tilesets
|
|||
|
|
this._tilesets = data.tilesets.map((ts) => ({ ...ts }));
|
|||
|
|
this._tilesetsData.clear();
|
|||
|
|
|
|||
|
|
// 加载图层
|
|||
|
|
this._layers = data.layers.map((layer) => ({
|
|||
|
|
...layer,
|
|||
|
|
data: [...layer.data]
|
|||
|
|
}));
|
|||
|
|
this._layersData.clear();
|
|||
|
|
for (const layer of this._layers) {
|
|||
|
|
this._layersData.set(layer.id, new Uint32Array(layer.data));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 加载碰撞数据
|
|||
|
|
if (data.collisionData) {
|
|||
|
|
this._collisionData = new Uint32Array(data.collisionData);
|
|||
|
|
this._collisionDataArray = [...data.collisionData];
|
|||
|
|
} else {
|
|||
|
|
this._collisionData = new Uint32Array(0);
|
|||
|
|
this._collisionDataArray = [];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
this.renderDirty = true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ===== Tileset Methods | 图块集方法 =====
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Add a new tileset reference
|
|||
|
|
* 添加新的图块集引用
|
|||
|
|
* @param source Tileset image source path | 图块集图片源路径
|
|||
|
|
* @param firstGid Optional first global tile ID | 可选的第一个全局图块ID
|
|||
|
|
* @returns Index of the added tileset | 添加的图块集索引
|
|||
|
|
*/
|
|||
|
|
addTileset(source: string, firstGid?: number): number {
|
|||
|
|
const gid = firstGid ?? this.calculateNextFirstGid();
|
|||
|
|
this._tilesets.push({ source, firstGid: gid });
|
|||
|
|
this.renderDirty = true;
|
|||
|
|
return this._tilesets.length - 1;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Remove a tileset by index
|
|||
|
|
* 按索引移除图块集
|
|||
|
|
* @param index Tileset index to remove | 要移除的图块集索引
|
|||
|
|
*/
|
|||
|
|
removeTileset(index: number): void {
|
|||
|
|
if (index >= 0 && index < this._tilesets.length) {
|
|||
|
|
this._tilesets.splice(index, 1);
|
|||
|
|
this._tilesetsData.delete(index);
|
|||
|
|
this.renderDirty = true;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Set tileset data for a specific index
|
|||
|
|
* 设置指定索引的图块集数据
|
|||
|
|
* @param index Tileset index | 图块集索引
|
|||
|
|
* @param data Tileset data to set | 要设置的图块集数据
|
|||
|
|
*/
|
|||
|
|
setTilesetData(index: number, data: ITilesetData): void {
|
|||
|
|
if (index >= 0 && index < this._tilesets.length) {
|
|||
|
|
this._tilesets[index].data = data;
|
|||
|
|
this._tilesetsData.set(index, data);
|
|||
|
|
this.renderDirty = true;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Get tileset data by index
|
|||
|
|
* 按索引获取图块集数据
|
|||
|
|
* @param index Tileset index | 图块集索引
|
|||
|
|
* @returns Tileset data or undefined | 图块集数据或undefined
|
|||
|
|
*/
|
|||
|
|
getTilesetData(index: number): ITilesetData | undefined {
|
|||
|
|
return this._tilesetsData.get(index) || this._tilesets[index]?.data;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Find tileset for a global tile ID
|
|||
|
|
* 根据全局图块ID查找图块集
|
|||
|
|
* @param gid Global tile ID | 全局图块ID
|
|||
|
|
* @returns Tileset info with local ID, or null if not found | 包含本地ID的图块集信息,未找到返回null
|
|||
|
|
*/
|
|||
|
|
getTilesetForGid(gid: number): { tileset: ITilesetRef; localId: number; index: number } | null {
|
|||
|
|
if (gid <= 0) return null;
|
|||
|
|
|
|||
|
|
for (let i = this._tilesets.length - 1; i >= 0; i--) {
|
|||
|
|
const tileset = this._tilesets[i];
|
|||
|
|
if (gid >= tileset.firstGid) {
|
|||
|
|
return {
|
|||
|
|
tileset,
|
|||
|
|
localId: gid - tileset.firstGid + 1,
|
|||
|
|
index: i
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private calculateNextFirstGid(): number {
|
|||
|
|
if (this._tilesets.length === 0) return 1;
|
|||
|
|
|
|||
|
|
let maxGid = 1;
|
|||
|
|
for (const tileset of this._tilesets) {
|
|||
|
|
const tileCount = tileset.data?.tileCount || 256;
|
|||
|
|
const nextGid = tileset.firstGid + tileCount;
|
|||
|
|
if (nextGid > maxGid) maxGid = nextGid;
|
|||
|
|
}
|
|||
|
|
return maxGid;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ===== Layer Methods | 图层方法 =====
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Add a new layer to the tilemap
|
|||
|
|
* 向瓦片地图添加新图层
|
|||
|
|
* @param name Optional layer name | 可选的图层名称
|
|||
|
|
* @param index Optional insertion index | 可选的插入索引
|
|||
|
|
* @returns The created layer data | 创建的图层数据
|
|||
|
|
*/
|
|||
|
|
addLayer(name?: string, index?: number): ITilemapLayerData {
|
|||
|
|
const id = `layer_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`;
|
|||
|
|
const layerName = name || `Layer ${this._layers.length}`;
|
|||
|
|
const data = new Array(this._width * this._height).fill(0);
|
|||
|
|
|
|||
|
|
const layer: ITilemapLayerData = {
|
|||
|
|
id,
|
|||
|
|
name: layerName,
|
|||
|
|
visible: true,
|
|||
|
|
opacity: 1,
|
|||
|
|
data
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
if (index !== undefined && index >= 0 && index <= this._layers.length) {
|
|||
|
|
this._layers.splice(index, 0, layer);
|
|||
|
|
} else {
|
|||
|
|
this._layers.push(layer);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
this._layersData.set(id, new Uint32Array(data));
|
|||
|
|
this.renderDirty = true;
|
|||
|
|
return layer;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Remove a layer by index (cannot remove last layer)
|
|||
|
|
* 按索引移除图层(不能移除最后一个图层)
|
|||
|
|
* @param index Layer index to remove | 要移除的图层索引
|
|||
|
|
*/
|
|||
|
|
removeLayer(index: number): void {
|
|||
|
|
if (index >= 0 && index < this._layers.length && this._layers.length > 1) {
|
|||
|
|
const layer = this._layers[index];
|
|||
|
|
this._layers.splice(index, 1);
|
|||
|
|
this._layersData.delete(layer.id);
|
|||
|
|
|
|||
|
|
if (this._activeLayerIndex >= this._layers.length) {
|
|||
|
|
this._activeLayerIndex = this._layers.length - 1;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
this.renderDirty = true;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Move a layer from one position to another
|
|||
|
|
* 将图层从一个位置移动到另一个位置
|
|||
|
|
* @param fromIndex Source index | 源索引
|
|||
|
|
* @param toIndex Target index | 目标索引
|
|||
|
|
*/
|
|||
|
|
moveLayer(fromIndex: number, toIndex: number): void {
|
|||
|
|
if (
|
|||
|
|
fromIndex >= 0 &&
|
|||
|
|
fromIndex < this._layers.length &&
|
|||
|
|
toIndex >= 0 &&
|
|||
|
|
toIndex < this._layers.length &&
|
|||
|
|
fromIndex !== toIndex
|
|||
|
|
) {
|
|||
|
|
const [layer] = this._layers.splice(fromIndex, 1);
|
|||
|
|
this._layers.splice(toIndex, 0, layer);
|
|||
|
|
this.renderDirty = true;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Get layer data by index
|
|||
|
|
* 按索引获取图层数据
|
|||
|
|
*/
|
|||
|
|
getLayer(index: number): ITilemapLayerData | undefined {
|
|||
|
|
return this._layers[index];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Get layer data by ID
|
|||
|
|
* 按ID获取图层数据
|
|||
|
|
*/
|
|||
|
|
getLayerById(id: string): ITilemapLayerData | undefined {
|
|||
|
|
return this._layers.find((l) => l.id === id);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Set layer visibility
|
|||
|
|
* 设置图层可见性
|
|||
|
|
*/
|
|||
|
|
setLayerVisible(index: number, visible: boolean): void {
|
|||
|
|
if (index >= 0 && index < this._layers.length) {
|
|||
|
|
this._layers[index].visible = visible;
|
|||
|
|
this.renderDirty = true;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Set layer opacity
|
|||
|
|
* 设置图层不透明度
|
|||
|
|
*/
|
|||
|
|
setLayerOpacity(index: number, opacity: number): void {
|
|||
|
|
if (index >= 0 && index < this._layers.length) {
|
|||
|
|
this._layers[index].opacity = Math.max(0, Math.min(1, opacity));
|
|||
|
|
this.renderDirty = true;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Rename a layer
|
|||
|
|
* 重命名图层
|
|||
|
|
*/
|
|||
|
|
renameLayer(index: number, name: string): void {
|
|||
|
|
if (index >= 0 && index < this._layers.length) {
|
|||
|
|
this._layers[index].name = name;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ===== Tile Operations | 瓦片操作 =====
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Get tile index at position
|
|||
|
|
* 获取指定位置的图块索引
|
|||
|
|
* @param layerIndex Layer index | 图层索引
|
|||
|
|
* @param col Column (X) | 列(X)
|
|||
|
|
* @param row Row (Y) | 行(Y)
|
|||
|
|
* @returns Tile index (0 = empty) | 图块索引(0表示空)
|
|||
|
|
*/
|
|||
|
|
getTile(layerIndex: number, col: number, row: number): number {
|
|||
|
|
if (col < 0 || col >= this._width || row < 0 || row >= this._height) {
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
const layer = this._layers[layerIndex];
|
|||
|
|
if (!layer) return 0;
|
|||
|
|
|
|||
|
|
const layerData = this._layersData.get(layer.id);
|
|||
|
|
if (layerData) {
|
|||
|
|
return layerData[row * this._width + col];
|
|||
|
|
}
|
|||
|
|
return layer.data[row * this._width + col] || 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Set tile index at position
|
|||
|
|
* 设置指定位置的图块索引
|
|||
|
|
* @param layerIndex Layer index | 图层索引
|
|||
|
|
* @param col Column (X) | 列(X)
|
|||
|
|
* @param row Row (Y) | 行(Y)
|
|||
|
|
* @param tileIndex Tile index to set (0 = clear) | 要设置的图块索引(0表示清除)
|
|||
|
|
*/
|
|||
|
|
setTile(layerIndex: number, col: number, row: number, tileIndex: number): void {
|
|||
|
|
if (col < 0 || col >= this._width || row < 0 || row >= this._height) {
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
const layer = this._layers[layerIndex];
|
|||
|
|
if (!layer) return;
|
|||
|
|
|
|||
|
|
const index = row * this._width + col;
|
|||
|
|
layer.data[index] = tileIndex;
|
|||
|
|
|
|||
|
|
let layerData = this._layersData.get(layer.id);
|
|||
|
|
if (!layerData) {
|
|||
|
|
layerData = new Uint32Array(layer.data);
|
|||
|
|
this._layersData.set(layer.id, layerData);
|
|||
|
|
}
|
|||
|
|
layerData[index] = tileIndex;
|
|||
|
|
|
|||
|
|
this.renderDirty = true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Get raw tile data array for a layer
|
|||
|
|
* 获取图层的原始图块数据数组
|
|||
|
|
* @param layerIndex Layer index | 图层索引
|
|||
|
|
* @returns Uint32Array of tile indices | 图块索引的Uint32Array
|
|||
|
|
*/
|
|||
|
|
getLayerData(layerIndex: number): Uint32Array | undefined {
|
|||
|
|
const layer = this._layers[layerIndex];
|
|||
|
|
if (!layer) return undefined;
|
|||
|
|
return this._layersData.get(layer.id);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Get merged tile data from all visible layers
|
|||
|
|
* 获取所有可见图层合并后的图块数据
|
|||
|
|
* @returns Merged tile data array | 合并的图块数据数组
|
|||
|
|
*/
|
|||
|
|
getMergedTileData(): Uint32Array {
|
|||
|
|
const merged = new Uint32Array(this._width * this._height);
|
|||
|
|
|
|||
|
|
for (const layer of this._layers) {
|
|||
|
|
if (!layer.visible) continue;
|
|||
|
|
|
|||
|
|
const layerData = this._layersData.get(layer.id);
|
|||
|
|
if (!layerData) continue;
|
|||
|
|
|
|||
|
|
for (let i = 0; i < merged.length; i++) {
|
|||
|
|
if (layerData[i] > 0) {
|
|||
|
|
merged[i] = layerData[i];
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return merged;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ===== Collision | 碰撞 =====
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Check if tile has collision
|
|||
|
|
* 检查图块是否有碰撞
|
|||
|
|
* @param col Column (X) | 列(X)
|
|||
|
|
* @param row Row (Y) | 行(Y)
|
|||
|
|
* @returns True if has collision | 如果有碰撞返回true
|
|||
|
|
*/
|
|||
|
|
hasCollision(col: number, row: number): boolean {
|
|||
|
|
if (col < 0 || col >= this._width || row < 0 || row >= this._height) {
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
if (this._collisionData.length === 0) {
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
return this._collisionData[row * this._width + col] > 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Get collision type at tile position
|
|||
|
|
* 获取图块位置的碰撞类型
|
|||
|
|
*/
|
|||
|
|
getCollisionType(col: number, row: number): number {
|
|||
|
|
if (col < 0 || col >= this._width || row < 0 || row >= this._height) {
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
if (this._collisionData.length === 0) {
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
return this._collisionData[row * this._width + col];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Set collision type at tile position
|
|||
|
|
* 设置图块位置的碰撞类型
|
|||
|
|
* @param col Column (X) | 列(X)
|
|||
|
|
* @param row Row (Y) | 行(Y)
|
|||
|
|
* @param collisionType Collision type (0 = none) | 碰撞类型(0表示无)
|
|||
|
|
*/
|
|||
|
|
setCollision(col: number, row: number, collisionType: number): void {
|
|||
|
|
if (col < 0 || col >= this._width || row < 0 || row >= this._height) {
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
if (this._collisionData.length === 0) {
|
|||
|
|
this._collisionData = new Uint32Array(this._width * this._height);
|
|||
|
|
this._collisionDataArray = new Array(this._width * this._height).fill(0);
|
|||
|
|
}
|
|||
|
|
const index = row * this._width + col;
|
|||
|
|
this._collisionData[index] = collisionType;
|
|||
|
|
this._collisionDataArray[index] = collisionType;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Check collision at world coordinates
|
|||
|
|
* 检查世界坐标处的碰撞
|
|||
|
|
*/
|
|||
|
|
hasCollisionAt(worldX: number, worldY: number): boolean {
|
|||
|
|
const [col, row] = this.worldToTile(worldX, worldY);
|
|||
|
|
return this.hasCollision(col, row);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Get all collision tiles within bounds
|
|||
|
|
* 获取边界内的所有碰撞图块
|
|||
|
|
* @returns Array of [col, row, type] | [列, 行, 类型]数组
|
|||
|
|
*/
|
|||
|
|
getCollisionTilesInBounds(
|
|||
|
|
left: number,
|
|||
|
|
bottom: number,
|
|||
|
|
right: number,
|
|||
|
|
top: number
|
|||
|
|
): Array<[number, number, number]> {
|
|||
|
|
const result: Array<[number, number, number]> = [];
|
|||
|
|
|
|||
|
|
if (this._collisionData.length === 0) {
|
|||
|
|
return result;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const startCol = Math.max(0, Math.floor(left / this.tileWidth));
|
|||
|
|
const endCol = Math.min(this._width, Math.ceil(right / this.tileWidth));
|
|||
|
|
const startRow = Math.max(0, Math.floor(bottom / this.tileHeight));
|
|||
|
|
const endRow = Math.min(this._height, Math.ceil(top / this.tileHeight));
|
|||
|
|
|
|||
|
|
for (let row = startRow; row < endRow; row++) {
|
|||
|
|
for (let col = startCol; col < endCol; col++) {
|
|||
|
|
const type = this._collisionData[row * this._width + col];
|
|||
|
|
if (type > 0) {
|
|||
|
|
result.push([col, row, type]);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return result;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Generate collision rectangles for physics
|
|||
|
|
* 为物理系统生成碰撞矩形
|
|||
|
|
* @returns Array of collision rectangles | 碰撞矩形数组
|
|||
|
|
*/
|
|||
|
|
generateCollisionRects(): Array<{
|
|||
|
|
x: number;
|
|||
|
|
y: number;
|
|||
|
|
width: number;
|
|||
|
|
height: number;
|
|||
|
|
type: number;
|
|||
|
|
}> {
|
|||
|
|
const rects: Array<{
|
|||
|
|
x: number;
|
|||
|
|
y: number;
|
|||
|
|
width: number;
|
|||
|
|
height: number;
|
|||
|
|
type: number;
|
|||
|
|
}> = [];
|
|||
|
|
|
|||
|
|
if (this._collisionData.length === 0) {
|
|||
|
|
return rects;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
for (let row = 0; row < this._height; row++) {
|
|||
|
|
for (let col = 0; col < this._width; col++) {
|
|||
|
|
const type = this._collisionData[row * this._width + col];
|
|||
|
|
if (type > 0) {
|
|||
|
|
rects.push({
|
|||
|
|
x: col * this.tileWidth,
|
|||
|
|
y: row * this.tileHeight,
|
|||
|
|
width: this.tileWidth,
|
|||
|
|
height: this.tileHeight,
|
|||
|
|
type
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return rects;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ===== Coordinate Conversion | 坐标转换 =====
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Convert world coordinates to tile coordinates
|
|||
|
|
* 将世界坐标转换为图块坐标
|
|||
|
|
* @returns [col, row] | [列, 行]
|
|||
|
|
*/
|
|||
|
|
worldToTile(worldX: number, worldY: number): [number, number] {
|
|||
|
|
const col = Math.floor(worldX / this.tileWidth);
|
|||
|
|
const row = Math.floor(worldY / this.tileHeight);
|
|||
|
|
return [col, row];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Convert tile coordinates to world coordinates (center of tile)
|
|||
|
|
* 将图块坐标转换为世界坐标(图块中心)
|
|||
|
|
* @returns [worldX, worldY] | [世界X, 世界Y]
|
|||
|
|
*/
|
|||
|
|
tileToWorld(col: number, row: number): [number, number] {
|
|||
|
|
const worldX = col * this.tileWidth + this.tileWidth / 2;
|
|||
|
|
const worldY = row * this.tileHeight + this.tileHeight / 2;
|
|||
|
|
return [worldX, worldY];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ===== UV Calculation | UV计算 =====
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Get UV coordinates for a tile
|
|||
|
|
* 获取 tile 的 UV 坐标
|
|||
|
|
*
|
|||
|
|
* 使用 UVHelper 计算 OpenGL 纹理坐标。
|
|||
|
|
* Uses UVHelper to calculate OpenGL texture coordinates.
|
|||
|
|
*
|
|||
|
|
* @see UVHelper.calculateTileUV for coordinate system documentation
|
|||
|
|
* @param tilesetIndex Tileset index | Tileset 索引
|
|||
|
|
* @param localTileId Local tile ID (1-based) | 本地 tile ID(从1开始)
|
|||
|
|
* @returns [u0, v0, u1, v1] OpenGL UV coordinates, or null if invalid
|
|||
|
|
*/
|
|||
|
|
getTileUV(tilesetIndex: number, localTileId: number): [number, number, number, number] | null {
|
|||
|
|
if (localTileId <= 0) return null;
|
|||
|
|
|
|||
|
|
const tilesetData = this.getTilesetData(tilesetIndex);
|
|||
|
|
if (!tilesetData) {
|
|||
|
|
console.warn('[TilemapComponent] getTileUV: No tileset data for index', tilesetIndex);
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Use UVHelper for coordinate calculation
|
|||
|
|
// 使用 UVHelper 计算坐标
|
|||
|
|
return UVHelper.calculateTileUV(localTileId - 1, {
|
|||
|
|
columns: tilesetData.columns,
|
|||
|
|
tileWidth: tilesetData.tileWidth,
|
|||
|
|
tileHeight: tilesetData.tileHeight,
|
|||
|
|
imageWidth: tilesetData.imageWidth,
|
|||
|
|
imageHeight: tilesetData.imageHeight,
|
|||
|
|
margin: tilesetData.margin,
|
|||
|
|
spacing: tilesetData.spacing
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ===== Resize | 大小调整 =====
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Calculate offset based on anchor point
|
|||
|
|
* 根据锚点计算偏移量
|
|||
|
|
*/
|
|||
|
|
private calculateAnchorOffset(
|
|||
|
|
oldSize: number,
|
|||
|
|
newSize: number,
|
|||
|
|
anchor: 'start' | 'center' | 'end'
|
|||
|
|
): number {
|
|||
|
|
const delta = newSize - oldSize;
|
|||
|
|
switch (anchor) {
|
|||
|
|
case 'start': return 0;
|
|||
|
|
case 'center': return Math.floor(delta / 2);
|
|||
|
|
case 'end': return delta;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Resize the tilemap, preserving existing data at the specified anchor position
|
|||
|
|
* 调整瓦片地图大小,在指定锚点位置保留现有数据
|
|||
|
|
* @param newWidth New width in tiles | 新宽度(图块数)
|
|||
|
|
* @param newHeight New height in tiles | 新高度(图块数)
|
|||
|
|
* @param anchor Anchor point for preserving data (default: 'bottom-left' for Y-up coordinate system) | 保留数据的锚点(默认:'bottom-left',适用于Y轴向上的坐标系)
|
|||
|
|
*/
|
|||
|
|
resize(newWidth: number, newHeight: number, anchor: ResizeAnchor = 'bottom-left'): void {
|
|||
|
|
if (newWidth === this._width && newHeight === this._height) {
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Parse anchor to get X and Y alignment
|
|||
|
|
// 解析锚点获取X和Y方向的对齐方式
|
|||
|
|
let xAnchor: 'start' | 'center' | 'end' = 'start';
|
|||
|
|
let yAnchor: 'start' | 'center' | 'end' = 'end'; // 'end' means bottom in Y-up system
|
|||
|
|
|
|||
|
|
if (anchor.includes('left')) xAnchor = 'start';
|
|||
|
|
else if (anchor.includes('right')) xAnchor = 'end';
|
|||
|
|
else xAnchor = 'center';
|
|||
|
|
|
|||
|
|
if (anchor.includes('bottom')) yAnchor = 'end';
|
|||
|
|
else if (anchor.includes('top')) yAnchor = 'start';
|
|||
|
|
else yAnchor = 'center';
|
|||
|
|
|
|||
|
|
// Calculate offsets for placing old data in new array
|
|||
|
|
// 计算将旧数据放入新数组的偏移量
|
|||
|
|
const offsetX = this.calculateAnchorOffset(this._width, newWidth, xAnchor);
|
|||
|
|
const offsetY = this.calculateAnchorOffset(this._height, newHeight, yAnchor);
|
|||
|
|
|
|||
|
|
// 调整所有图层
|
|||
|
|
for (const layer of this._layers) {
|
|||
|
|
const oldLayerData = this._layersData.get(layer.id);
|
|||
|
|
const newLayerData = new Uint32Array(newWidth * newHeight);
|
|||
|
|
const newDataArray = new Array(newWidth * newHeight).fill(0);
|
|||
|
|
|
|||
|
|
if (oldLayerData) {
|
|||
|
|
for (let y = 0; y < this._height; y++) {
|
|||
|
|
for (let x = 0; x < this._width; x++) {
|
|||
|
|
const newX = x + offsetX;
|
|||
|
|
const newY = y + offsetY;
|
|||
|
|
|
|||
|
|
// Check bounds
|
|||
|
|
if (newX >= 0 && newX < newWidth && newY >= 0 && newY < newHeight) {
|
|||
|
|
const value = oldLayerData[y * this._width + x];
|
|||
|
|
newLayerData[newY * newWidth + newX] = value;
|
|||
|
|
newDataArray[newY * newWidth + newX] = value;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
this._layersData.set(layer.id, newLayerData);
|
|||
|
|
layer.data = newDataArray;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 调整碰撞数据
|
|||
|
|
if (this._collisionData.length > 0) {
|
|||
|
|
const newCollisionData = new Uint32Array(newWidth * newHeight);
|
|||
|
|
const newCollisionArray = new Array(newWidth * newHeight).fill(0);
|
|||
|
|
|
|||
|
|
for (let y = 0; y < this._height; y++) {
|
|||
|
|
for (let x = 0; x < this._width; x++) {
|
|||
|
|
const newX = x + offsetX;
|
|||
|
|
const newY = y + offsetY;
|
|||
|
|
|
|||
|
|
if (newX >= 0 && newX < newWidth && newY >= 0 && newY < newHeight) {
|
|||
|
|
const value = this._collisionData[y * this._width + x];
|
|||
|
|
newCollisionData[newY * newWidth + newX] = value;
|
|||
|
|
newCollisionArray[newY * newWidth + newX] = value;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
this._collisionData = newCollisionData;
|
|||
|
|
this._collisionDataArray = newCollisionArray;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
this._width = newWidth;
|
|||
|
|
this._height = newHeight;
|
|||
|
|
this.renderDirty = true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ===== Serialization | 序列化 =====
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Called after deserialization to restore runtime data
|
|||
|
|
* 反序列化后调用以恢复运行时数据
|
|||
|
|
*/
|
|||
|
|
override onDeserialized(): void {
|
|||
|
|
// 恢复图层运行时数据
|
|||
|
|
for (const layer of this._layers) {
|
|||
|
|
if (layer.data && layer.data.length > 0) {
|
|||
|
|
this._layersData.set(layer.id, new Uint32Array(layer.data));
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 恢复Tileset缓存数据
|
|||
|
|
for (let i = 0; i < this._tilesets.length; i++) {
|
|||
|
|
const tileset = this._tilesets[i];
|
|||
|
|
if (tileset.data) {
|
|||
|
|
this._tilesetsData.set(i, tileset.data);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 恢复碰撞数据
|
|||
|
|
if (this._collisionDataArray.length > 0) {
|
|||
|
|
this._collisionData = new Uint32Array(this._collisionDataArray);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
this.renderDirty = true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Cleanup when component is destroyed
|
|||
|
|
* 组件销毁时清理资源
|
|||
|
|
*/
|
|||
|
|
onDestroy(): void {
|
|||
|
|
this._tilesets = [];
|
|||
|
|
this._tilesetsData.clear();
|
|||
|
|
this._layers = [];
|
|||
|
|
this._layersData.clear();
|
|||
|
|
this._collisionData = new Uint32Array(0);
|
|||
|
|
this._collisionDataArray = [];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Export tilemap to data format
|
|||
|
|
* 导出瓦片地图为数据格式
|
|||
|
|
* @returns Tilemap data object | 瓦片地图数据对象
|
|||
|
|
*/
|
|||
|
|
exportToData(): ITilemapData {
|
|||
|
|
return {
|
|||
|
|
name: 'Tilemap',
|
|||
|
|
version: 2,
|
|||
|
|
width: this._width,
|
|||
|
|
height: this._height,
|
|||
|
|
tileWidth: this.tileWidth,
|
|||
|
|
tileHeight: this.tileHeight,
|
|||
|
|
tilesets: this._tilesets.map((ts) => ({ ...ts })),
|
|||
|
|
layers: this._layers.map((layer) => ({
|
|||
|
|
...layer,
|
|||
|
|
data: [...layer.data]
|
|||
|
|
})),
|
|||
|
|
collisionData: this._collisionData.length > 0 ? Array.from(this._collisionData) : undefined
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ===== IResourceComponent 实现 =====
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 获取此组件需要的所有资源引用
|
|||
|
|
* Get all resource references needed by this component
|
|||
|
|
*/
|
|||
|
|
getResourceReferences(): ResourceReference[] {
|
|||
|
|
const refs: ResourceReference[] = [];
|
|||
|
|
|
|||
|
|
// 收集所有 tileset 纹理引用
|
|||
|
|
// Collect all tileset texture references
|
|||
|
|
for (const tileset of this._tilesets) {
|
|||
|
|
if (tileset.source) {
|
|||
|
|
refs.push({
|
|||
|
|
path: tileset.source,
|
|||
|
|
type: 'texture',
|
|||
|
|
runtimeId: tileset.textureId
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return refs;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 设置已加载资源的运行时 ID
|
|||
|
|
* Set runtime IDs for loaded resources
|
|||
|
|
*/
|
|||
|
|
setResourceIds(pathToId: Map<string, number>): void {
|
|||
|
|
// 为每个 tileset 设置纹理 ID
|
|||
|
|
// Set texture ID for each tileset
|
|||
|
|
for (const tileset of this._tilesets) {
|
|||
|
|
if (tileset.source) {
|
|||
|
|
const textureId = pathToId.get(tileset.source);
|
|||
|
|
if (textureId !== undefined) {
|
|||
|
|
tileset.textureId = textureId;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 标记渲染数据为脏,需要重新构建
|
|||
|
|
// Mark render data as dirty, needs rebuild
|
|||
|
|
this.renderDirty = true;
|
|||
|
|
}
|
|||
|
|
}
|