JS Canvas瓦片地图编辑器多维数组存储瓦片ID被覆盖问题
Canvas瓦片地图编辑器数组数据丢失问题修复
问题现象
开发基于Canvas的瓦片地图编辑器时,用于存储瓦片ID的二维数组无法持久保存数据:每次触发瓦片保存逻辑,之前存储的所有瓦片ID都会被覆盖,仅保留最新一次操作的瓦片ID,无法导出完整的地图数据用于后续碰撞检测。
问题根因
原有saveIdexedTiledMap函数存在逻辑错误:每次函数执行时,都会通过双层循环遍历所有地图格子,把整个数组的所有位置重置为0,再给当前点击的单个格子赋值瓦片ID。相当于每次保存都先清空全部历史数据,自然无法保留之前绘制的瓦片记录。
除此之外原有逻辑还存在三个数据不一致隐患:
- 右键删除瓦片时没有同步更新数组对应位置的值
- 点击清空画布按钮时没有重置数组,会出现画布空白但数组还留存旧数据的问题
- 计算格子行列时没有做整数取整,可能出现浮点数数组索引导致赋值异常
修复步骤
- 数组初始化仅执行一次:不要在每次保存时重置全量数组,改为在编辑器初始化、画布加载完成时,一次性创建和地图尺寸匹配、初始值全为0的二维数组。
- 保存逻辑仅更新当前操作格子:触发绘制/删除操作时,只修改鼠标当前位置对应格子的数组值,不再遍历重置整个数组。
- 全操作同步数据:删除单个瓦片、清空全画布时同步更新数组数据,保证画布显示和存储的地图数据完全一致。
- 索引做整数校验:计算格子行列时增加取整逻辑,避免浮点数索引导致的数组赋值错误。
修复后核心代码
1. 全局变量与初始化逻辑
// 语义化重命名地图存储数组,避免无意义的array命名 let tileMapData = []; // 提前计算地图行列常量,避免重复计算 const TILE_SIZE = 32; const mapCols = canvas.width / TILE_SIZE; const mapRows = canvas.height / TILE_SIZE; // 初始化空地图数组,仅在加载、清空、改尺寸时调用 function initTileMapArray() { tileMapData = []; for (let row = 0; row < mapRows; row++) { // 用fill快速创建全0的行数组 tileMapData[row] = new Array(mapCols).fill(0); } } // 在原有showSpreadSheetImg函数末尾,网格绘制完成后调用一次初始化 // 追加在canvasMapGrids.draw()之后即可 initTileMapArray();
2. 修复后的瓦片数据更新函数
/** * 更新地图瓦片数据 * @param {string} operateType 操作类型:draw=绘制瓦片 delete=删除瓦片 */ function updateTileMapData(operateType = 'draw') { // 计算当前鼠标位置对应的格子行列,取整保证索引为整数 const gridRow = Math.floor(mouseCanvasGrids.y / TILE_SIZE); const gridCol = Math.floor(mouseCanvasGrids.x / TILE_SIZE); // 仅更新当前操作的格子,不重置全量数据 if (operateType === 'draw' && selectedTile.length > 0) { tileMapData[gridRow][gridCol] = selectedTile[0].index; } else if (operateType === 'delete') { tileMapData[gridRow][gridCol] = 0; } console.log(tileMapData); }
3. 调整原有逻辑的调用位置
- 左键绘制瓦片逻辑末尾,把原来的
saveIdexedTiledMap()替换为updateTileMapData('draw') - 右键删除瓦片的
deleteSelectedTile函数末尾,追加updateTileMapData('delete') - 清空画布按钮的点击事件中,清空画布重绘网格后,追加
initTileMapArray()重置全量数据为0
// 清空画布逻辑修改示例 clearCanvas.addEventListener("click", () => { ctx.clearRect(0, 0, canvas.width, canvas.height); canvasMapGrids.draw(); initTileMapArray(); // 同步重置地图数组 });
后续使用建议
- 导出地图JSON时直接调用
JSON.stringify(tileMapData)即可得到标准格式的地图数据,可直接用于游戏中的瓦片渲染、碰撞检测逻辑。 - 如果后续支持自定义地图尺寸、瓦片尺寸,只需要在配置提交后重新计算
mapCols、mapRows的值,再调用initTileMapArray()即可适配新尺寸。 - 可以给数组增加本地存储逻辑,页面刷新时从localStorage读取已保存的
tileMapData重绘画布,避免刷新丢失编辑内容。
内容的提问来源于stack exchange,提问作者DafrDev
相关产品推荐
相关产品推荐

