From 200eb886dda475d7330682b94d2d89eb1d2781d0 Mon Sep 17 00:00:00 2001 From: chicrobot Date: Thu, 8 Jan 2026 02:56:48 +0800 Subject: [PATCH] =?UTF-8?q?fix(map)=20=E4=BF=AE=E5=A4=8D=E7=93=A6=E7=89=87?= =?UTF-8?q?=E6=98=BE=E7=A4=BA=E5=81=8F=E7=A7=BB=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- scenes/MapEditTestScene.js | 6 + scenes/TmpScene.js | 342 +++++++++++++++++++++++++++++++++++-- 2 files changed, 333 insertions(+), 15 deletions(-) diff --git a/scenes/MapEditTestScene.js b/scenes/MapEditTestScene.js index 5f646ac..95905ba 100644 --- a/scenes/MapEditTestScene.js +++ b/scenes/MapEditTestScene.js @@ -476,6 +476,9 @@ export default class MapEditTestScene extends Phaser.Scene { // 先设置裁剪区域,使用图块在大图中的精确位置作为起始点 tile.setCrop(frameX, frameY, cropWidth, cropHeight); + + // 调整瓦片位置,使其相对于容器(0,0)位置显示,而不是原图像位置 + tile.setPosition(-frameX*2, -frameY*2); // 然后设置缩放,放大显示 tile.setScale(2); @@ -739,6 +742,9 @@ export default class MapEditTestScene extends Phaser.Scene { // 先设置裁剪区域,使用图块在大图中的精确位置 tile.setCrop(cropX, cropY, cropWidth, cropHeight); + // 调整瓦片位置,使其相对于目标位置(0,0)显示,而不是原图像位置 + tile.setPosition(drawX - cropX*2, drawY - cropY*2); + // 然后设置缩放,确保瓦片填满地图网格 const scaleFactor = mapTileSize / cropWidth; tile.setScale(scaleFactor); diff --git a/scenes/TmpScene.js b/scenes/TmpScene.js index ecb9d9c..43c30b1 100644 --- a/scenes/TmpScene.js +++ b/scenes/TmpScene.js @@ -1,13 +1,36 @@ export default class TmpScene extends Phaser.Scene { constructor() { super({ key: 'TmpScene' }); + this.selectedTile = null; // 当前选中的瓦片 + this.mapLayer = null; // 地图层 + + this.mapWidth = 50; // 地图宽度(瓦片数量) + this.mapHeight = 50; // 地图高度(瓦片数量) + + this.tileSize = 16; // 地图瓦片大小 + this.tileColumns = 18; // 瓦片图列数 + this.tileRows = 13; // 瓦片图行数 } preload() { // 加载资源 + // 加载瓦片资源 + this.loadTileAssets(); + // 显示加载进度 + this.createLoadingUI(); + // 加载完成后移除加载UI + this.load.on('complete', () => { + this.loadingText.destroy(); + this.progressBar.destroy(); + this.progressFill.destroy(); + }); } create() { + // 设置输入处理 + console.log('设置输入处理'); + this.setupInput(); + // 创建场景 // 物理世界和相机边界 const worldSize = this.mapWidth * this.tileSize; @@ -23,8 +46,210 @@ export default class TmpScene extends Phaser.Scene { // 创建瓦片选择区域 console.log('创建瓦片选择区域'); this.createTileSelectionArea(); + + // 检查资源是否已加载 + if (this.assetsLoaded) { + console.log('资源已加载,调用updateTileSelection'); + this.updateTileSelection(); + } else { + console.log('资源尚未加载,等待加载完成'); + } } + loadTileAssets() { + console.log('开始加载瓦片资源'); + + // 只加载单张瓦片图,替代多张瓦片资源 + const tilemapKey = 'mapedit-tilemap'; + const tilemapPath = 'assets/PNG/Tiles/Tilemap/tilemap_packed.png'; + console.log(`加载瓦片图: ${tilemapKey} -> ${tilemapPath}`); + this.load.image(tilemapKey, tilemapPath); + + // 加载进度回调 + this.load.on('progress', (progress) => { + console.log(`资源加载进度: ${Math.round(progress * 100)}%`); + }); + + // 文件加载完成回调 + this.load.on('filecomplete', (key, type, data) => { + console.log(`文件加载完成: ${key} (${type})`); + }); + + // 加载完成回调 + this.load.on('complete', () => { + console.log('所有资源加载完成!'); + this.assetsLoaded = true; + this.loadingText.setText('资源加载完成!'); + + // 设置瓦片图的过滤模式为NEAREST + if (this.textures.exists(tilemapKey)) { + const texture = this.textures.get(tilemapKey); + texture.setFilter(Phaser.Textures.FilterMode.NEAREST); + console.log(`设置纹理 ${tilemapKey} 的过滤模式为NEAREST`); + } + + // 初始化瓦片集合 - 只使用单张瓦片图键名 + this.tileCount = this.tileColumns * this.tileRows; // 根据Tilesheet.txt,总瓦片数为18x13=234 + this.tilemapKey = tilemapKey; + // 存储瓦片的位置信息,用于后续切割 + this.tileImageSize = 16; // 瓦片大小16x16px + this.tileSpacing = 0; // 瓦片间距0px + this.tileColumns = 18; // 18列 + this.tileRows = 13; // 13行 + + console.log(`瓦片图初始化完成,共 ${this.tileCount} 个瓦片`); + // 只有在tileSelectionContainer初始化后才调用updateTileSelection + if (this.tileSelectionContainer) { + console.log('tileSelectionContainer已初始化,调用updateTileSelection'); + this.updateTileSelection(); + } else { + console.log('tileSelectionContainer尚未初始化,等待create方法调用'); + } + }); + } + + createLoadingUI() { + // 加载文本 + this.loadingText = this.add.text(10, 10, '加载瓦片资源...', { + fontSize: '16px', + fill: '#ffffff', + fontFamily: 'Arial' + }); + this.loadingText.setScrollFactor(0, 0); + this.loadingText.setDepth(100); + + // 加载进度条 + this.progressBar = this.add.rectangle(10, 40, 300, 20, 0x555555); + this.progressBar.setOrigin(0, 0); + this.progressBar.setScrollFactor(0, 0); + this.progressBar.setDepth(100); + + this.progressFill = this.add.rectangle(10, 40, 0, 20, 0x00ff00); + this.progressFill.setOrigin(0, 0); + this.progressFill.setScrollFactor(0, 0); + this.progressFill.setDepth(100); + + // 更新进度 + this.load.on('progress', (progress) => { + this.progressFill.width = progress * 300; + this.loadingText.setText(`加载瓦片资源... ${Math.round(progress * 100)}%`); + }); + } + + updateTileSelection() { + console.log('开始更新瓦片选择显示'); + console.log(`当前瓦片数量: ${this.tileCount}`); + + // 清空当前瓦片选择 + console.log('清空当前瓦片选择'); + this.tileSelectionContainer.removeAll(true); + + try { + // 检查纹理是否存在 + if (this.textures.exists(this.tilemapKey)) { + console.log(`纹理 ${this.tilemapKey} 存在`); + + // 获取瓦片图的实际尺寸 + const texture = this.textures.get(this.tilemapKey); + const frame = texture.get(); + const actualWidth = frame.width; + const actualHeight = frame.height; + console.log(`瓦片图实际尺寸: ${actualWidth}x${actualHeight}`); + + // 直接添加完整瓦片图,不裁剪,不缩放 + const fullTilemap = this.add.image(0, 0, this.tilemapKey); + fullTilemap.setOrigin(0, 0); + fullTilemap.setScale(1); + this.tileSelectionContainer.add(fullTilemap); + + + + console.log(`瓦片配置:大小${this.tileImageSize}x${this.tileImageSize},间距${this.tileSpacing},${this.tileColumns}x${this.tileRows}排列`); + + // 为每个瓦片位置添加透明的交互式矩形,用于悬停高亮和点击选择 + for (let i = 0; i < this.tileCount; i++) { + const col = i % this.tileColumns; + const row = Math.floor(i / this.tileColumns); + + // 精确计算瓦片在实际图片中的位置(无间距) + const tileX = col * this.tileImageSize; + const tileY = row * this.tileImageSize; + + console.log(`瓦片 ${i} 索引 -> 行列: (${col}, ${row}) -> 位置: (${tileX}, ${tileY}), 大小: ${this.tileImageSize}x${this.tileImageSize}`); + + // 创建透明的交互式矩形,精确对齐瓦片 + // 使用add.zone创建透明区域,专门用于交互 + const tileZone = this.add.zone(tileX, tileY, this.tileImageSize, this.tileImageSize); + tileZone.setOrigin(0, 0); + tileZone.setInteractive({ useHandCursor: true }); + this.tileSelectionContainer.add(tileZone); + + // 瓦片点击事件 + tileZone.on('pointerdown', () => { + console.log(`点击了瓦片 ${i},行列: (${col}, ${row})`); + this.selectTile(i); + }); + + // 悬停高亮效果 + tileZone.on('pointerover', () => { + // 创建临时高亮矩形 + if (!this.highlightRect) { + this.highlightRect = this.add.rectangle(tileX, tileY, this.tileImageSize, this.tileImageSize, 0xffffff, 0.3); + this.highlightRect.setOrigin(0, 0); + this.tileSelectionContainer.add(this.highlightRect); + } else { + this.highlightRect.setPosition(tileX, tileY); + } + }); + + tileZone.on('pointerout', () => { + // 移除高亮效果 + if (this.highlightRect) { + this.highlightRect.destroy(); + this.highlightRect = null; + } + }); + } + + console.log('瓦片选择区域已更新,直接显示完整瓦片图'); + } else { + console.error(`纹理 ${this.tilemapKey} 不存在!`); + } + } catch (error) { + console.error('更新瓦片选择显示失败:', error); + } + } + + setupInput() { + // 鼠标输入 + this.input.on('pointerdown', (pointer) => { + // this.handleMouseDown(pointer); + // 开始绘制 + this.isDrawing = true; + this.lastDrawnTile = null; + }); + this.input.on('pointerup', (pointer) => { + // this.handleMouseUp(pointer); + // 停止绘制 + this.isDrawing = false; + this.lastDrawnTile = null; + }); + this.input.on('pointermove', (pointer) => { + // this.handleMouseMove(pointer); + // 拖动绘制:如果鼠标按下且不是在拖拽地图,则绘制瓦片 + if (this.isDrawing && !this.isDragging) { + // this.placeTile(pointer); + } + }); + + // 键盘输入 + // 空格键返回启动场景 + this.input.keyboard.on('keydown-SPACE', () => { + this.scene.start('BootScene'); + }); + } + + createMapArea() { // 创建地图容器 this.mapContainer = this.add.container(0, 0); @@ -72,44 +297,131 @@ export default class TmpScene extends Phaser.Scene { // 瓦片选择区域位置 const tilePanelX = 10; const tilePanelY = 10; - const chunkSize = 16; - const tilePictureSizeX = 288; - const tilePictureSizeY = 208; - - // 瓦片选择区域背景 - const tilePanel = this.add.rectangle(tilePanelX, tilePanelY, tilePictureSizeX, tilePictureSizeY+chunkSize, 0x111111, 0.8); - tilePanel.setOrigin(0, 0); - tilePanel.setScrollFactor(0, 0); - tilePanel.setDepth(10); // 瓦片选择区域标题 this.add.text(tilePanelX, tilePanelY, '瓦片选择', { - fontSize: '16px', + fontSize: this.tileSize+'px', fill: '#ffffff', fontFamily: 'Arial', fontWeight: 'bold' }).setScrollFactor(0, 0).setDepth(11); + + // 瓦片选择区域背景 + const tilePanel = this.add.rectangle(tilePanelX, tilePanelY+this.tileSize, this.tileColumns*this.tileSize, this.tileRows*this.tileSize, 0x111111, 0.8); + tilePanel.setOrigin(0, 0); + tilePanel.setScrollFactor(0, 0); + tilePanel.setDepth(10); + // 瓦片选择容器 - 注意:这是一个Container对象,可以使用removeAll方法 - this.tileSelectionContainer = this.add.container(tilePanelX, tilePanelY+tilePictureSizeY+chunkSize); + this.tileSelectionContainer = this.add.container(tilePanelX, tilePanelY+this.tileSize); this.tileSelectionContainer.setScrollFactor(0, 0); this.tileSelectionContainer.setDepth(11); // 当前选中瓦片显示 - 改为使用Container容器,而不是Rectangle,这样可以使用removeAll方法 - this.selectedTileContainer = this.add.container(tilePanelX, tilePanelY+tilePictureSizeY+chunkSize*2); + this.selectedTileContainer = this.add.container(tilePanelX, tilePanelY+(this.tileRows+1)*this.tileSize+16); this.selectedTileContainer.setScrollFactor(0, 0); this.selectedTileContainer.setDepth(11); // 添加选中瓦片背景 - const selectedTileBg = this.add.rectangle(0, 0, 16, 16, 0x333333); + const selectedTileBg = this.add.rectangle(0, 0, 32, 32, 0x333333); selectedTileBg.setOrigin(0, 0); selectedTileBg.setDepth(10); this.selectedTileContainer.add(selectedTileBg); - this.add.text(tilePanelX, tilePanelY+tilePictureSizeY+chunkSize, '当前选中瓦片:', { - fontSize: '16px', + this.add.text(tilePanelX, tilePanelY+(this.tileRows+1)*this.tileSize, '当前选中瓦片:', { + fontSize: this.tileSize+'px', fill: '#ffffff', fontFamily: 'Arial' }).setScrollFactor(0, 0).setDepth(11); } + + /** + * 选择瓦片 + * @param {number} tileIndex - 瓦片索引 + */ + selectTile(tileIndex) { + // 防御性编程:检查参数类型 + if (typeof tileIndex !== 'number') { + console.error('selectTile: tileIndex 必须是数字类型'); + return; + } + + this.selectedTile = tileIndex; + console.log(`选择瓦片索引: ${tileIndex}`); + + // 更新选中瓦片显示 + // 注意:使用Container对象的removeAll方法,而不是Rectangle对象 + try { + // 防御性编程:检查容器是否存在 + if (this.selectedTileContainer && typeof this.selectedTileContainer.removeAll === 'function') { + this.selectedTileContainer.removeAll(true); + } else { + console.error('selectTile: selectedTileContainer 不存在或没有 removeAll 方法'); + return; + } + } catch (error) { + console.error('selectTile: 清空选中瓦片容器失败:', error); + return; + } + + if (this.selectedTile !== null && this.selectedTile !== -1) { + try { + // 防御性编程:检查瓦片索引是否在有效范围内 + if (this.selectedTile >= 0 && this.selectedTile < this.tileCount) { + // 防御性编程:检查纹理是否存在 + if (this.textures.exists(this.tilemapKey)) { + // 计算瓦片在大图中的位置 + const col = this.selectedTile % this.tileColumns; + const row = Math.floor(this.selectedTile / this.tileColumns); + + // 计算图块在大图中的精确左上角像素位置 + const frameX = col * this.tileImageSize; + const frameY = row * this.tileImageSize; + + // 创建一个新的image,专门用于显示选中的瓦片 + const tile = this.add.image(0, 0, this.tilemapKey); + tile.setOrigin(0, 0); + + // 先设置裁剪区域,使用图块在大图中的精确位置作为起始点 + tile.setCrop(frameX, frameY, this.tileImageSize, this.tileImageSize); + + // 调整瓦片位置,使其相对于容器(0,0)位置显示,而不是原图像位置 + tile.setPosition(-frameX*2, -frameY*2); + + // 然后设置缩放,放大显示 + tile.setScale(2); + + this.selectedTileContainer.add(tile); + + // 添加选中边框,匹配放大后的瓦片大小 + const border = this.add.rectangle(0, 0, this.tileImageSize*2, this.tileImageSize*2, 0x00ff00, 0); + border.setOrigin(0, 0); + border.setStrokeStyle(2, 0x00ff00, 1); + this.selectedTileContainer.add(border); + } else { + console.error(`selectTile: 纹理 ${this.tilemapKey} 不存在`); + } + } else { + console.error(`selectTile: 瓦片索引 ${this.selectedTile} 超出范围 (0-${this.tileCount-1})`); + } + } catch (error) { + console.error('selectTile: 显示选中瓦片失败:', error); + } + } else if (this.selectedTile === -1) { + // 显示删除模式 + try { + const deleteText = this.add.text(0, 20, '删除模式', { + fontSize: '14px', + fill: '#ff0000', + fontFamily: 'Arial', + fontWeight: 'bold' + }); + deleteText.setOrigin(0, 0); + this.selectedTileContainer.add(deleteText); + } catch (error) { + console.error('selectTile: 显示删除模式失败:', error); + } + } + } } \ No newline at end of file