diff --git a/main.js b/main.js index 7392a40..66ec5ea 100644 --- a/main.js +++ b/main.js @@ -21,11 +21,11 @@ import ItemListTestScene from './scenes/ItemListTestScene.js'; // 导入资源包测试场景 import ResourceTestScene from './scenes/ResourceTestScene.js'; // 导入地图编辑测试场景 -import MapEditTestScene from './scenes/MapEditTestScene.js'; +import MapEditSinglePicTestScene from './scenes/MapEditSinglePicTestScene.js'; // 导入角色动画测试场景 import CharacterAnimationTestScene from './scenes/CharacterAnimationTestScene.js'; // 导入临时场景 -import TmpScene from './scenes/TmpScene.js'; +import MapEditTestScene from './scenes/MapEditTestScene.js'; // 创建游戏的最终配置对象 const config = { @@ -46,9 +46,9 @@ const config = { InventoryTestScene, ItemListTestScene, ResourceTestScene, - MapEditTestScene, + MapEditSinglePicTestScene, CharacterAnimationTestScene, - TmpScene + MapEditTestScene ] }; diff --git a/scenes/BootScene.js b/scenes/BootScene.js index 553a916..83ae92d 100644 --- a/scenes/BootScene.js +++ b/scenes/BootScene.js @@ -19,9 +19,9 @@ export default class BootScene extends Phaser.Scene { // 测试场景列表 const testScenes = [ - { key: 'TmpScene', name: '测试场景13:临时场景' }, - { key: 'CharacterAnimationTestScene', name: '测试场景12:角色动画测试' }, - { key: 'MapEditTestScene', name: '测试场景11:地图编辑测试' }, + { key: 'CharacterAnimationTestScene', name: '测试场景13:角色动画测试' }, + { key: 'MapEditTestScene', name: '测试场景12:地图编辑测试' }, + { key: 'MapEditSinglePicTestScene', name: '测试场景11:地图编辑单瓦片图资源测试' }, { key: 'ResourceTestScene', name: '测试场景10:资源包测试' }, { key: 'ItemListTestScene', name: '测试场景9:物品列表菜单测试' }, { key: 'InventoryTestScene', name: '测试场景8:背包系统测试' }, diff --git a/scenes/MapEditSinglePicTestScene.js b/scenes/MapEditSinglePicTestScene.js new file mode 100644 index 0000000..89c9dc4 --- /dev/null +++ b/scenes/MapEditSinglePicTestScene.js @@ -0,0 +1,922 @@ +// 地图编辑测试场景 +// 防御性编程原则: +// 1. 始终检查对象和方法是否存在 +// 2. 检查参数类型和范围 +// 3. 使用 try-catch 块捕获可能的异常 +// 4. 为所有容器和对象使用明确的命名 +// 5. 避免使用相同的键名,防止冲突 +// 6. 始终检查纹理是否存在 +// 7. 在使用方法前检查方法是否存在 +// 8. 添加详细的日志和错误信息 +// 9. 为复杂逻辑添加注释 +// 10. 使用类型检查和断言 + +export default class MapEditSinglePicTestScene extends Phaser.Scene { + constructor() { + super({ key: 'MapEditSinglePicTestScene' }); + this.tileSize = 16; // 地图瓦片大小 + this.selectedTile = null; // 当前选中的瓦片 + this.mapLayer = null; // 地图层 + this.mapWidth = 50; // 地图宽度(瓦片数量) + this.mapHeight = 50; // 地图高度(瓦片数量) + this.isDragging = false; // 是否正在拖拽地图 + this.lastDragPosition = { x: 0, y: 0 }; // 上次拖拽位置 + this.zoomLevel = 1; // 缩放级别 + // 瓦片图相关属性 + this.tilemapKey = ''; // 单张瓦片图的键名 + this.tileCount = 234; // 总瓦片数 + this.tileImageSize = 16; // 瓦片在图片中的实际大小 + this.tileSpacing = 0; // 瓦片间距 + this.tileColumns = 18; // 瓦片图列数 + this.tileRows = 13; // 瓦片图行数 + } + + preload() { + // 加载瓦片资源 + this.loadTileAssets(); + + // 显示加载进度 + this.createLoadingUI(); + } + + create() { + console.log('开始创建地图编辑场景'); + // 物理世界和相机边界 + const worldSize = this.mapWidth * this.tileSize; + this.physics.world.setBounds(0, 0, worldSize, worldSize); + this.cameras.main.setBounds(0, 0, worldSize, worldSize); + + // 设置背景色 + this.cameras.main.setBackgroundColor('#222222'); + + // 修复:移除可能有问题的defaultFilterMode设置,使用资源加载完成后设置过滤模式 + + // 创建地图编辑区域 + console.log('创建地图编辑区域'); + this.createMapArea(); + + // 创建瓦片选择区域 + console.log('创建瓦片选择区域'); + this.createTileSelectionArea(); + + // 检查资源是否已加载 + if (this.assetsLoaded) { + console.log('资源已加载,调用updateTileSelection'); + this.updateTileSelection(); + } else { + console.log('资源尚未加载,等待加载完成'); + } + + // 创建控制面板 + console.log('创建控制面板'); + this.createControlPanel(); + + // 设置输入处理 + console.log('设置输入处理'); + this.setupInput(); + + // 显示提示信息 + console.log('显示提示信息'); + this.showInstructions(); + + 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 = 234; // 根据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方法调用'); + } + }); + } + + /** + * 创建加载UI + */ + 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)}%`); + }); + } + + /** + * 创建地图编辑区域 + */ + createMapArea() { + // 创建地图容器 + this.mapContainer = this.add.container(0, 0); + this.mapContainer.setDepth(1); + + // 创建背景网格 + this.createGrid(); + + // 创建地图层(用于放置瓦片) + this.mapLayer = this.add.container(0, 0); + this.mapContainer.add(this.mapLayer); + + // 初始化空地图 + this.mapData = []; + for (let y = 0; y < this.mapHeight; y++) { + this.mapData[y] = []; + for (let x = 0; x < this.mapWidth; x++) { + this.mapData[y][x] = -1; // -1表示空瓦片 + } + } + } + + /** + * 创建背景网格 + */ + createGrid() { + // 创建网格线 + const gridGraphics = this.add.graphics(); + gridGraphics.lineStyle(1, 0x444444, 0.5); + + // 绘制垂直线 + for (let x = 0; x <= this.mapWidth; x++) { + gridGraphics.moveTo(x * this.tileSize, 0); + gridGraphics.lineTo(x * this.tileSize, this.mapHeight * this.tileSize); + } + + // 绘制水平线 + for (let y = 0; y <= this.mapHeight; y++) { + gridGraphics.moveTo(0, y * this.tileSize); + gridGraphics.lineTo(this.mapWidth * this.tileSize, y * this.tileSize); + } + + this.mapContainer.add(gridGraphics); + } + + /** + * 创建瓦片选择区域 + */ + createTileSelectionArea() { + // 瓦片选择区域背景 - 使用固定大小,足够容纳完整瓦片图 + const tilePanel = this.add.rectangle(10, 70, 300, 250, 0x111111, 0.8); + tilePanel.setOrigin(0, 0); + tilePanel.setScrollFactor(0, 0); + tilePanel.setDepth(10); + + // 瓦片选择区域标题 + this.add.text(20, 80, '瓦片选择', { + fontSize: '16px', + fill: '#ffffff', + fontFamily: 'Arial', + fontWeight: 'bold' + }).setScrollFactor(0, 0).setDepth(11); + + // 瓦片选择容器 - 注意:这是一个Container对象,可以使用removeAll方法 + this.tileSelectionContainer = this.add.container(20, 110); + this.tileSelectionContainer.setScrollFactor(0, 0); + this.tileSelectionContainer.setDepth(11); + + // 移除网格,不再需要 + // this.tileSelectionGrid = this.add.grid(0, 0, fullWidth, fullHeight, tileWidth, tileHeight, 0x333333, 0.5, 0x444444, 0.5); + // this.tileSelectionContainer.add(this.tileSelectionGrid); + + // 当前选中瓦片显示 - 改为使用Container容器,而不是Rectangle,这样可以使用removeAll方法 + this.selectedTileContainer = this.add.container(20, 340); + this.selectedTileContainer.setScrollFactor(0, 0); + this.selectedTileContainer.setDepth(11); + + // 添加选中瓦片背景 + const selectedTileBg = this.add.rectangle(0, 0, 16, 16, 0x333333); + selectedTileBg.setOrigin(0, 0); + selectedTileBg.setDepth(10); + this.selectedTileContainer.add(selectedTileBg); + + this.add.text(20, 320, '当前选中瓦片:', { + fontSize: '14px', + fill: '#ffffff', + fontFamily: 'Arial' + }).setScrollFactor(0, 0).setDepth(11); + } + + /** + * 创建控制面板 + */ + createControlPanel() { + // 控制面板背景 + const controlPanel = this.add.rectangle(this.sys.game.config.width - 210, 10, 200, 150, 0x111111, 0.8); + controlPanel.setOrigin(0, 0); + controlPanel.setScrollFactor(0, 0); + controlPanel.setDepth(10); + + // 控制面板标题 + this.add.text(this.sys.game.config.width - 200, 20, '控制面板', { + fontSize: '16px', + fill: '#ffffff', + fontFamily: 'Arial', + fontWeight: 'bold' + }).setScrollFactor(0, 0).setDepth(11); + + // 创建按钮 + this.createButton(this.sys.game.config.width - 200, 50, 180, 30, '清空地图', () => { + this.clearMap(); + }); + + this.createButton(this.sys.game.config.width - 200, 90, 180, 30, '保存地图', () => { + this.saveMap(); + }); + + this.createButton(this.sys.game.config.width - 200, 130, 180, 30, '加载地图', () => { + this.loadMap(); + }); + } + + /** + * 创建按钮 + */ + createButton(x, y, width, height, text, callback) { + // 按钮背景 + const button = this.add.rectangle(x, y, width, height, 0x333333); + button.setOrigin(0, 0); + button.setScrollFactor(0, 0); + button.setDepth(10); + button.setInteractive({ useHandCursor: true }); + + // 按钮文本 + const buttonText = this.add.text(x + 10, y + 5, text, { + fontSize: '14px', + fill: '#ffffff', + fontFamily: 'Arial' + }); + buttonText.setScrollFactor(0, 0); + buttonText.setDepth(11); + + // 按钮事件 + button.on('pointerdown', callback); + + button.on('pointerover', () => { + button.setFillStyle(0x555555); + }); + + button.on('pointerout', () => { + button.setFillStyle(0x333333); + }); + } + + /** + * 更新瓦片选择显示 + */ + 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); + + // 根据用户提供的精确信息:图片288*208,瓦片16*16,18*13排列 + const tileWidth = 16; // 瓦片大小16x16px + const tileHeight = 16; + const spacing = 0; // 瓦片之间没有间距(288/18=16,208/13=16) + const columns = 18; // 18列 + const rows = 13; // 13行 + + console.log(`瓦片配置:大小${tileWidth}x${tileHeight},间距${spacing},${columns}x${rows}排列`); + + // 为每个瓦片位置添加透明的交互式矩形,用于悬停高亮和点击选择 + for (let i = 0; i < this.tileCount; i++) { + const col = i % columns; + const row = Math.floor(i / columns); + + // 精确计算瓦片在实际图片中的位置(无间距) + const tileX = col * tileWidth; + const tileY = row * tileHeight; + + console.log(`瓦片 ${i} 索引 -> 行列: (${col}, ${row}) -> 位置: (${tileX}, ${tileY}), 大小: ${tileWidth}x${tileHeight}`); + + // 创建透明的交互式矩形,精确对齐瓦片 + // 使用add.zone创建透明区域,专门用于交互 + const tileZone = this.add.zone(tileX, tileY, tileWidth, tileHeight); + 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, tileWidth, tileHeight, 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); + } + } + + /** + * 选择瓦片 + * @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)) { + // 根据用户提供的精确信息:瓦片16*16,无间距,18*13排列 + const tileWidth = 16; + const tileHeight = 16; + const spacing = 0; + const columns = 18; + + // 计算瓦片在大图中的位置 + const col = this.selectedTile % columns; + const row = Math.floor(this.selectedTile / columns); + + // 计算裁剪区域 - 正确使用图块对应的左上角像素位置 + const cropWidth = tileWidth; + const cropHeight = tileHeight; + + // 计算图块在大图中的精确左上角像素位置 + const frameX = col * tileWidth; + const frameY = row * tileHeight; + + // 创建一个新的image,专门用于显示选中的瓦片 + const tile = this.add.image(0, 0, this.tilemapKey); + tile.setOrigin(0, 0); + + // 先设置裁剪区域,使用图块在大图中的精确位置作为起始点 + tile.setCrop(frameX, frameY, cropWidth, cropHeight); + + // 调整瓦片位置,使其相对于容器(0,0)位置显示,而不是原图像位置 + tile.setPosition(-frameX*2, -frameY*2); + + // 然后设置缩放,放大显示 + tile.setScale(2); + + this.selectedTileContainer.add(tile); + + // 添加选中边框,匹配放大后的瓦片大小 + const border = this.add.rectangle(0, 0, 64, 64, 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); + } + } + } + + /** + * 设置输入处理 + */ + setupInput() { + // 初始化拖动绘制相关变量 + this.isDrawing = false; + this.lastDrawnTile = null; + + // 鼠标输入 + 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.on('wheel', (pointer, gameObjects, deltaX, deltaY, deltaZ) => { + this.handleWheel(deltaY); + }); + + // 键盘输入 + this.setupKeyboardInput(); + } + + /** + * 设置键盘输入 + */ + setupKeyboardInput() { + // 空格键返回启动场景 + this.input.keyboard.on('keydown-SPACE', () => { + this.scene.start('BootScene'); + }); + + // 数字键1-9选择瓦片 + for (let i = 0; i < 9; i++) { + this.input.keyboard.on(`keydown-${i + 1}`, () => { + if (i < this.tileSet.length) { + this.selectTile(i); + } + }); + } + + // D键删除瓦片 + this.input.keyboard.on('keydown-D', () => { + this.selectedTile = -1; // -1表示删除模式 + }); + + // C键清空地图 + this.input.keyboard.on('keydown-C', () => { + this.clearMap(); + }); + + // S键保存地图 + this.input.keyboard.on('keydown-S', () => { + this.saveMap(); + }); + + // L键加载地图 + this.input.keyboard.on('keydown-L', () => { + this.loadMap(); + }); + } + + /** + * 处理鼠标按下事件 + */ + handleMouseDown(pointer) { + // 检查是否点击了瓦片选择区域 + const tileSelectionRect = new Phaser.Geom.Rectangle(20, 110, 200, 360); + if (Phaser.Geom.Rectangle.Contains(tileSelectionRect, pointer.x, pointer.y)) { + return; // 点击了瓦片选择区域,不处理地图交互 + } + + // 检查是否点击了控制面板 + const controlPanelRect = new Phaser.Geom.Rectangle(this.sys.game.config.width - 210, 10, 200, 150); + if (Phaser.Geom.Rectangle.Contains(controlPanelRect, pointer.x, pointer.y)) { + return; // 点击了控制面板,不处理地图交互 + } + + // 检查是否点击了当前选中瓦片显示区域 + const selectedTileRect = new Phaser.Geom.Rectangle(20, 490, 64, 64); + if (Phaser.Geom.Rectangle.Contains(selectedTileRect, pointer.x, pointer.y)) { + return; // 点击了选中瓦片显示区域,不处理地图交互 + } + + // 如果点击了地图区域 + if (this.selectedTile !== null) { + // 放置或删除瓦片 + this.placeTile(pointer); + } else { + // 开始拖拽地图 + this.isDragging = true; + this.lastDragPosition.x = pointer.x; + this.lastDragPosition.y = pointer.y; + } + } + + /** + * 处理鼠标释放事件 + */ + handleMouseUp(pointer) { + this.isDragging = false; + } + + /** + * 处理鼠标移动事件 + */ + handleMouseMove(pointer) { + if (this.isDragging) { + // 计算拖拽距离 + const deltaX = pointer.x - this.lastDragPosition.x; + const deltaY = pointer.y - this.lastDragPosition.y; + + // 移动相机 + this.cameras.main.scrollX -= deltaX / this.zoomLevel; + this.cameras.main.scrollY -= deltaY / this.zoomLevel; + + // 更新上次拖拽位置 + this.lastDragPosition.x = pointer.x; + this.lastDragPosition.y = pointer.y; + } + } + + /** + * 处理滚轮事件 + */ + handleWheel(deltaY) { + // 放大 + if (deltaY < 0 && this.zoomLevel < 2) { + this.zoomLevel += 0.1; + } + // 缩小 + else if (deltaY > 0 && this.zoomLevel > 0.5) { + this.zoomLevel -= 0.1; + } + + // 更新相机缩放 + this.cameras.main.setZoom(this.zoomLevel); + } + + /** + * 放置瓦片 + */ + placeTile(pointer) { + // 将屏幕坐标转换为世界坐标 - 确保与鼠标位置完全匹配 + const worldPoint = this.cameras.main.getWorldPoint(pointer.x, pointer.y); + + // 计算瓦片坐标 - 使用地图的瓦片大小32px + const mapTileSize = 32; + + // 确保鼠标点击位置与绘制位置完全一致 + // 使用Math.floor确保点击瓦片的哪个位置都绘制到对应瓦片 + const tileX = Math.floor(worldPoint.x / mapTileSize); + const tileY = Math.floor(worldPoint.y / mapTileSize); + + // 检查瓦片坐标是否在地图范围内 + if (tileX < 0 || tileX >= this.mapWidth || tileY < 0 || tileY >= this.mapHeight) { + return; + } + + // 检查是否已经绘制过这个位置,避免重复绘制 + if (this.lastDrawnTile && this.lastDrawnTile.x === tileX && this.lastDrawnTile.y === tileY) { + return; + } + + // 删除瓦片模式 + if (this.selectedTile === -1) { + this.removeTile(tileX, tileY); + } else { + // 放置瓦片 - 使用单张瓦片图 + + // 检查该位置是否已有瓦片 + const existingTile = this.getTileAt(tileX, tileY); + if (existingTile) { + // 删除现有瓦片 + existingTile.destroy(); + } + + // 计算精确的绘制位置 - 确保与鼠标点击的瓦片完全对应 + const drawX = tileX * mapTileSize; + const drawY = tileY * mapTileSize; + + console.log(`鼠标位置: (${pointer.x}, ${pointer.y}) -> 世界坐标: (${worldPoint.x}, ${worldPoint.y}) -> 瓦片坐标: (${tileX}, ${tileY}) -> 绘制位置: (${drawX}, ${drawY})`); + + // 创建新瓦片 - 精确放置在计算出的位置 + try { + // 根据用户提供的精确信息:瓦片16*16,无间距,18*13排列 + const tileWidth = 16; + const tileHeight = 16; + const columns = 18; + + // 计算瓦片在大图中的位置 + const col = this.selectedTile % columns; + const row = Math.floor(this.selectedTile / columns); + + // 计算裁剪区域 - 正确使用图块对应的左上角像素位置 + const cropX = col * tileWidth; // 图块在大图中的x坐标 + const cropY = row * tileHeight; // 图块在大图中的y坐标 + const cropWidth = tileWidth; + const cropHeight = tileHeight; + + console.log(`选中瓦片索引: ${this.selectedTile} -> 行列: (${col}, ${row}) -> 裁剪区域: (${cropX}, ${cropY}, ${cropWidth}, ${cropHeight})`); + + // 创建image并设置初始位置 + const tile = this.add.image(drawX, drawY, this.tilemapKey); + tile.setOrigin(0, 0); + + // 先设置裁剪区域,使用图块在大图中的精确位置 + tile.setCrop(cropX, cropY, cropWidth, cropHeight); + + // 调整瓦片位置,使其相对于目标位置(0,0)显示,而不是原图像位置 + tile.setPosition(drawX - cropX*2, drawY - cropY*2); + + // 然后设置缩放,确保瓦片填满地图网格 + const scaleFactor = mapTileSize / cropWidth; + tile.setScale(scaleFactor); + + console.log(`绘制位置: (${drawX}, ${drawY}), 缩放因子: ${scaleFactor}`); + + tile.setDepth(2); + this.mapLayer.add(tile); + + // 更新地图数据 + this.mapData[tileY][tileX] = this.selectedTile; + } catch (error) { + console.error('放置瓦片失败:', error); + } + } + + // 记录最后绘制的瓦片位置 + this.lastDrawnTile = { x: tileX, y: tileY }; + } + + /** + * 删除瓦片 + */ + removeTile(tileX, tileY) { + // 检查该位置是否已有瓦片 + const existingTile = this.getTileAt(tileX, tileY); + if (existingTile) { + // 删除现有瓦片 + existingTile.destroy(); + + // 更新地图数据 + this.mapData[tileY][tileX] = -1; + } + } + + /** + * 获取指定位置的瓦片 + */ + getTileAt(tileX, tileY) { + const children = this.mapLayer.getAll(); + for (const child of children) { + const childTileX = Math.floor(child.x / this.tileSize); + const childTileY = Math.floor(child.y / this.tileSize); + if (childTileX === tileX && childTileY === tileY) { + return child; + } + } + return null; + } + + /** + * 清空地图 + */ + clearMap() { + // 删除所有瓦片 + this.mapLayer.removeAll(true); + + // 重置地图数据 + for (let y = 0; y < this.mapHeight; y++) { + for (let x = 0; x < this.mapWidth; x++) { + this.mapData[y][x] = -1; + } + } + + console.log('地图已清空'); + } + + /** + * 保存地图 + */ + saveMap() { + // 将地图数据转换为JSON + const mapJson = JSON.stringify(this.mapData); + + // 创建下载链接 + const blob = new Blob([mapJson], { type: 'application/json' }); + const url = URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = url; + a.download = 'map.json'; + document.body.appendChild(a); + a.click(); + document.body.removeChild(a); + URL.revokeObjectURL(url); + + console.log('地图已保存'); + } + + /** + * 加载地图 + */ + loadMap() { + // 创建文件选择器 + const input = document.createElement('input'); + input.type = 'file'; + input.accept = '.json'; + input.onchange = (e) => { + const file = e.target.files[0]; + if (file) { + const reader = new FileReader(); + reader.onload = (event) => { + try { + const mapData = JSON.parse(event.target.result); + this.loadMapData(mapData); + } catch (error) { + console.error('加载地图失败:', error); + } + }; + reader.readAsText(file); + } + }; + input.click(); + } + + /** + * 加载地图数据 + */ + loadMapData(mapData) { + // 清空当前地图 + this.clearMap(); + + // 更新地图数据 + this.mapData = mapData; + + // 重新绘制地图 + for (let y = 0; y < this.mapHeight; y++) { + for (let x = 0; x < this.mapWidth; x++) { + const tileIndex = this.mapData[y][x]; + if (tileIndex !== -1 && tileIndex < this.tileSet.length) { + const tileKey = this.tileSet[tileIndex]; + try { + const tile = this.add.image(x * this.tileSize, y * this.tileSize, tileKey); + tile.setOrigin(0, 0); + tile.setDepth(2); + this.mapLayer.add(tile); + } catch (error) { + console.error(`加载地图瓦片(${x},${y})失败:`, error); + } + } + } + } + + console.log('地图已加载'); + } + + /** + * 显示提示信息 + */ + showInstructions() { + const instructions = [ + '地图编辑测试场景', + '点击瓦片选择区域选择瓦片', + '在地图上点击放置瓦片', + '选择瓦片后按D键进入删除模式', + '鼠标拖拽移动地图', + '鼠标滚轮缩放地图', + '按C键清空地图', + '按S键保存地图', + '按L键加载地图', + '按空格键返回启动场景' + ]; + + for (let i = 0; i < instructions.length; i++) { + this.add.text(this.sys.game.config.width - 300, 180 + i * 20, instructions[i], { + fontSize: '12px', + fill: '#ffffff', + fontFamily: 'Arial' + }).setScrollFactor(0, 0).setDepth(11); + } + } + + update() { + // 每帧更新 + } +} \ No newline at end of file diff --git a/scenes/MapEditTestScene.js b/scenes/MapEditTestScene.js index 95905ba..b248f33 100644 --- a/scenes/MapEditTestScene.js +++ b/scenes/MapEditTestScene.js @@ -1,64 +1,63 @@ -// 地图编辑测试场景 -// 防御性编程原则: -// 1. 始终检查对象和方法是否存在 -// 2. 检查参数类型和范围 -// 3. 使用 try-catch 块捕获可能的异常 -// 4. 为所有容器和对象使用明确的命名 -// 5. 避免使用相同的键名,防止冲突 -// 6. 始终检查纹理是否存在 -// 7. 在使用方法前检查方法是否存在 -// 8. 添加详细的日志和错误信息 -// 9. 为复杂逻辑添加注释 -// 10. 使用类型检查和断言 - export default class MapEditTestScene extends Phaser.Scene { constructor() { super({ key: 'MapEditTestScene' }); - this.tileSize = 16; // 地图瓦片大小 this.selectedTile = null; // 当前选中的瓦片 this.mapLayer = null; // 地图层 + this.mapWidth = 50; // 地图宽度(瓦片数量) this.mapHeight = 50; // 地图高度(瓦片数量) - this.isDragging = false; // 是否正在拖拽地图 - this.lastDragPosition = { x: 0, y: 0 }; // 上次拖拽位置 - this.zoomLevel = 1; // 缩放级别 - // 瓦片图相关属性 - this.tilemapKey = ''; // 单张瓦片图的键名 - this.tileCount = 234; // 总瓦片数 - this.tileImageSize = 16; // 瓦片在图片中的实际大小 - this.tileSpacing = 0; // 瓦片间距 - this.tileColumns = 18; // 瓦片图列数 - this.tileRows = 13; // 瓦片图行数 + + this.tileSize = 16; // 地图瓦片大小 + this.tileSet = []; // 存储所有瓦片资源的键名 + this.tileCount = 234; // 总瓦片数(从tile_0000到tile_0233) + + // 地形类型配置 + this.terrainTypes = { + 0: { name: '平原', color: 0x4CAF50, passable: true, speed: 1.0 }, + 1: { name: '森林', color: 0x2E7D32, passable: true, speed: 0.7 }, + 2: { name: '山地', color: 0x795548, passable: false, speed: 0.0 }, + 3: { name: '水域', color: 0x2196F3, passable: false, speed: 0.0 }, + 4: { name: '道路', color: 0x9E9E9E, passable: true, speed: 1.5 } + }; + this.selectedTerrain = 0; // 当前选中的地形类型 + this.isTerrainMode = false; // 是否处于地形编辑模式 } - + preload() { + // 加载资源 // 加载瓦片资源 this.loadTileAssets(); - // 显示加载进度 this.createLoadingUI(); + // 加载完成后移除加载UI + this.load.on('complete', () => { + this.loadingText.destroy(); + this.progressBar.destroy(); + this.progressFill.destroy(); + }); } - + create() { - console.log('开始创建地图编辑场景'); + // 设置输入处理 + console.log('设置输入处理'); + this.setupInput(); + + // 创建场景 // 物理世界和相机边界 const worldSize = this.mapWidth * this.tileSize; this.physics.world.setBounds(0, 0, worldSize, worldSize); this.cameras.main.setBounds(0, 0, worldSize, worldSize); - // 设置背景色 this.cameras.main.setBackgroundColor('#222222'); - - // 修复:移除可能有问题的defaultFilterMode设置,使用资源加载完成后设置过滤模式 - + // 创建地图编辑区域 console.log('创建地图编辑区域'); this.createMapArea(); - + // 创建瓦片选择区域 console.log('创建瓦片选择区域'); this.createTileSelectionArea(); - + // 检查资源是否已加载 if (this.assetsLoaded) { console.log('资源已加载,调用updateTileSelection'); @@ -67,32 +66,24 @@ export default class MapEditTestScene extends Phaser.Scene { console.log('资源尚未加载,等待加载完成'); } - // 创建控制面板 - console.log('创建控制面板'); - this.createControlPanel(); - - // 设置输入处理 - console.log('设置输入处理'); - this.setupInput(); - - // 显示提示信息 - console.log('显示提示信息'); - this.showInstructions(); - - 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); + // 加载Tiles目录下的多个瓦片图(从tile_0000到tile_0233) + this.tileSet = []; + const tileBasePath = 'assets/PNG/Tiles/Tiles/'; + + for (let i = 0; i < this.tileCount; i++) { + const tileIndex = String(i).padStart(4, '0'); + const tileKey = `tile_${tileIndex}`; + const tilePath = `${tileBasePath}${tileKey}.png`; + + console.log(`加载瓦片: ${tileKey} -> ${tilePath}`); + this.load.image(tileKey, tilePath); + this.tileSet.push(tileKey); + } // 加载进度回调 this.load.on('progress', (progress) => { @@ -110,22 +101,14 @@ export default class MapEditTestScene extends Phaser.Scene { 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`); + // 设置所有瓦片图的过滤模式为NEAREST + for (const tileKey of this.tileSet) { + if (this.textures.exists(tileKey)) { + const texture = this.textures.get(tileKey); + texture.setFilter(Phaser.Textures.FilterMode.NEAREST); + } } - // 初始化瓦片集合 - 只使用单张瓦片图键名 - this.tileCount = 234; // 根据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) { @@ -136,10 +119,7 @@ export default class MapEditTestScene extends Phaser.Scene { } }); } - - /** - * 创建加载UI - */ + createLoadingUI() { // 加载文本 this.loadingText = this.add.text(10, 10, '加载瓦片资源...', { @@ -167,10 +147,173 @@ export default class MapEditTestScene extends Phaser.Scene { this.loadingText.setText(`加载瓦片资源... ${Math.round(progress * 100)}%`); }); } + + updateTileSelection() { + console.log('开始更新瓦片选择显示'); + console.log(`当前瓦片数量: ${this.tileCount}`); + + // 清空当前瓦片选择 + console.log('清空当前瓦片选择'); + this.tileSelectionContainer.removeAll(true); + + try { + // 定义瓦片排列方式(18列13行,与原来的单张瓦片图保持一致) + const tileColumns = 18; + const tileRows = 13; + + // 为每个瓦片添加图像和交互区域 + for (let i = 0; i < this.tileCount; i++) { + const tileKey = this.tileSet[i]; + + // 检查纹理是否存在 + if (this.textures.exists(tileKey)) { + const col = i % tileColumns; + const row = Math.floor(i / tileColumns); + + // 计算瓦片位置 + const tileX = col * this.tileSize; + const tileY = row * this.tileSize; + + console.log(`瓦片 ${i} (${tileKey}) -> 行列: (${col}, ${row}) -> 位置: (${tileX}, ${tileY})`); + + // 添加瓦片图像 + const tileImage = this.add.image(tileX, tileY, tileKey); + tileImage.setOrigin(0, 0); + tileImage.setScale(1); + this.tileSelectionContainer.add(tileImage); + + // 创建透明的交互式矩形,用于悬停高亮和点击选择 + const tileZone = this.add.zone(tileX, tileY, this.tileSize, this.tileSize); + tileZone.setOrigin(0, 0); + tileZone.setInteractive({ useHandCursor: true }); + this.tileSelectionContainer.add(tileZone); + + // 瓦片点击事件 + tileZone.on('pointerdown', () => { + console.log(`点击了瓦片 ${i} (${tileKey}),行列: (${col}, ${row})`); + this.selectTile(i); + }); + + // 悬停高亮效果 + tileZone.on('pointerover', () => { + // 创建临时高亮矩形 + if (!this.highlightRect) { + this.highlightRect = this.add.rectangle(tileX, tileY, this.tileSize, this.tileSize, 0xffffff, 0.3); + this.highlightRect.setOrigin(0, 0); + this.highlightRect.setDepth(12); + this.tileSelectionContainer.add(this.highlightRect); + } else { + this.highlightRect.setPosition(tileX, tileY); + } + }); + + tileZone.on('pointerout', () => { + // 移除高亮效果 + if (this.highlightRect) { + this.highlightRect.destroy(); + this.highlightRect = null; + } + }); + } else { + console.error(`纹理 ${tileKey} 不存在!`); + } + } + + console.log('瓦片选择区域已更新,显示所有独立瓦片'); + } catch (error) { + console.error('更新瓦片选择显示失败:', error); + } + } + + setupInput() { + // 初始化拖动绘制相关变量 + this.isDrawing = false; + this.lastDrawnTile = null; + this.isDragging = false; + this.lastDragPosition = { x: 0, y: 0 }; + + // 鼠标输入 + 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'); + }); + + // D键进入删除模式 + this.input.keyboard.on('keydown-D', () => { + this.selectedTile = -1; + }); + } /** - * 创建地图编辑区域 + * 处理鼠标按下事件 */ + handleMouseDown(pointer) { + // 检查是否点击了瓦片选择区域 + const tileSelectionRect = new Phaser.Geom.Rectangle(10, 26, 288, 208); + if (Phaser.Geom.Rectangle.Contains(tileSelectionRect, pointer.x, pointer.y)) { + return; // 点击了瓦片选择区域,不处理地图交互 + } + + // 如果点击了地图区域 + if (this.selectedTile !== null) { + // 放置或删除瓦片 + this.placeTile(pointer); + } else { + // 开始拖拽地图 + this.isDragging = true; + this.lastDragPosition.x = pointer.x; + this.lastDragPosition.y = pointer.y; + } + } + + /** + * 处理鼠标释放事件 + */ + handleMouseUp(pointer) { + this.isDragging = false; + } + + /** + * 处理鼠标移动事件 + */ + handleMouseMove(pointer) { + if (this.isDragging) { + // 计算拖拽距离 + const deltaX = pointer.x - this.lastDragPosition.x; + const deltaY = pointer.y - this.lastDragPosition.y; + + // 移动相机 + this.cameras.main.scrollX -= deltaX; + this.cameras.main.scrollY -= deltaY; + + // 更新上次拖拽位置 + this.lastDragPosition.x = pointer.x; + this.lastDragPosition.y = pointer.y; + } + } + + createMapArea() { // 创建地图容器 this.mapContainer = this.add.container(0, 0); @@ -183,19 +326,17 @@ export default class MapEditTestScene extends Phaser.Scene { this.mapLayer = this.add.container(0, 0); this.mapContainer.add(this.mapLayer); - // 初始化空地图 + // 初始化空地图,数据结构改为包含瓦片索引和地形ID的对象 this.mapData = []; for (let y = 0; y < this.mapHeight; y++) { this.mapData[y] = []; for (let x = 0; x < this.mapWidth; x++) { - this.mapData[y][x] = -1; // -1表示空瓦片 + this.mapData[y][x] = { tileIndex: -1, terrainId: 0 }; // tileIndex: -1表示空瓦片,terrainId: 0表示默认地形 } } } - - /** - * 创建背景网格 - */ + + //创建背景网格 createGrid() { // 创建网格线 const gridGraphics = this.add.graphics(); @@ -215,208 +356,174 @@ export default class MapEditTestScene extends Phaser.Scene { this.mapContainer.add(gridGraphics); } - - /** - * 创建瓦片选择区域 - */ + createTileSelectionArea() { - // 瓦片选择区域背景 - 使用固定大小,足够容纳完整瓦片图 - const tilePanel = this.add.rectangle(10, 70, 300, 250, 0x111111, 0.8); + // 瓦片选择区域位置 + const tilePanelX = 10; + const tilePanelY = 10; + + // 定义瓦片排列方式(18列13行,与原来的单张瓦片图保持一致) + const tileColumns = 18; + const tileRows = 13; + + // 瓦片选择区域标题 + this.add.text(tilePanelX, tilePanelY, '瓦片选择', { + fontSize: this.tileSize+'px', + fill: '#ffffff', + fontFamily: 'Arial', + fontWeight: 'bold' + }).setScrollFactor(0, 0).setDepth(11); + + // 瓦片选择区域背景 + const tilePanel = this.add.rectangle(tilePanelX, tilePanelY+this.tileSize, tileColumns*this.tileSize, tileRows*this.tileSize, 0x111111, 0.8); tilePanel.setOrigin(0, 0); tilePanel.setScrollFactor(0, 0); tilePanel.setDepth(10); - // 瓦片选择区域标题 - this.add.text(20, 80, '瓦片选择', { - fontSize: '16px', - fill: '#ffffff', - fontFamily: 'Arial', - fontWeight: 'bold' - }).setScrollFactor(0, 0).setDepth(11); // 瓦片选择容器 - 注意:这是一个Container对象,可以使用removeAll方法 - this.tileSelectionContainer = this.add.container(20, 110); + this.tileSelectionContainer = this.add.container(tilePanelX, tilePanelY+this.tileSize); this.tileSelectionContainer.setScrollFactor(0, 0); this.tileSelectionContainer.setDepth(11); - // 移除网格,不再需要 - // this.tileSelectionGrid = this.add.grid(0, 0, fullWidth, fullHeight, tileWidth, tileHeight, 0x333333, 0.5, 0x444444, 0.5); - // this.tileSelectionContainer.add(this.tileSelectionGrid); - // 当前选中瓦片显示 - 改为使用Container容器,而不是Rectangle,这样可以使用removeAll方法 - this.selectedTileContainer = this.add.container(20, 340); + this.selectedTileContainer = this.add.container(tilePanelX, tilePanelY+(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(20, 320, '当前选中瓦片:', { - fontSize: '14px', + this.add.text(tilePanelX, tilePanelY+(tileRows+1)*this.tileSize, '当前选中瓦片:', { + fontSize: this.tileSize+'px', fill: '#ffffff', fontFamily: 'Arial' }).setScrollFactor(0, 0).setDepth(11); - } - - /** - * 创建控制面板 - */ - createControlPanel() { - // 控制面板背景 - const controlPanel = this.add.rectangle(this.sys.game.config.width - 210, 10, 200, 150, 0x111111, 0.8); - controlPanel.setOrigin(0, 0); - controlPanel.setScrollFactor(0, 0); - controlPanel.setDepth(10); - // 控制面板标题 - this.add.text(this.sys.game.config.width - 200, 20, '控制面板', { - fontSize: '16px', + // 地形选择区域 + const terrainPanelY = tilePanelY+(tileRows+1)*this.tileSize+64; + + // 地形选择标题 + this.add.text(tilePanelX, terrainPanelY, '地形类型', { + fontSize: this.tileSize+'px', fill: '#ffffff', fontFamily: 'Arial', fontWeight: 'bold' }).setScrollFactor(0, 0).setDepth(11); - // 创建按钮 - this.createButton(this.sys.game.config.width - 200, 50, 180, 30, '清空地图', () => { - this.clearMap(); - }); + // 地形选择容器 + this.terrainSelectionContainer = this.add.container(tilePanelX, terrainPanelY+this.tileSize); + this.terrainSelectionContainer.setScrollFactor(0, 0); + this.terrainSelectionContainer.setDepth(11); - this.createButton(this.sys.game.config.width - 200, 90, 180, 30, '保存地图', () => { - this.saveMap(); - }); + // 添加地形选择按钮 + let terrainX = 0; + let terrainY = 0; + const terrainButtonSize = 24; + const terrainSpacing = 8; - this.createButton(this.sys.game.config.width - 200, 130, 180, 30, '加载地图', () => { - this.loadMap(); - }); - } - - /** - * 创建按钮 - */ - createButton(x, y, width, height, text, callback) { - // 按钮背景 - const button = this.add.rectangle(x, y, width, height, 0x333333); - button.setOrigin(0, 0); - button.setScrollFactor(0, 0); - button.setDepth(10); - button.setInteractive({ useHandCursor: true }); - - // 按钮文本 - const buttonText = this.add.text(x + 10, y + 5, text, { - fontSize: '14px', - fill: '#ffffff', - fontFamily: 'Arial' - }); - buttonText.setScrollFactor(0, 0); - buttonText.setDepth(11); - - // 按钮事件 - button.on('pointerdown', callback); - - button.on('pointerover', () => { - button.setFillStyle(0x555555); - }); - - button.on('pointerout', () => { - button.setFillStyle(0x333333); - }); - } - - /** - * 更新瓦片选择显示 - */ - 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); - - // 根据用户提供的精确信息:图片288*208,瓦片16*16,18*13排列 - const tileWidth = 16; // 瓦片大小16x16px - const tileHeight = 16; - const spacing = 0; // 瓦片之间没有间距(288/18=16,208/13=16) - const columns = 18; // 18列 - const rows = 13; // 13行 - - console.log(`瓦片配置:大小${tileWidth}x${tileHeight},间距${spacing},${columns}x${rows}排列`); - - // 为每个瓦片位置添加透明的交互式矩形,用于悬停高亮和点击选择 - for (let i = 0; i < this.tileCount; i++) { - const col = i % columns; - const row = Math.floor(i / columns); - - // 精确计算瓦片在实际图片中的位置(无间距) - const tileX = col * tileWidth; - const tileY = row * tileHeight; - - console.log(`瓦片 ${i} 索引 -> 行列: (${col}, ${row}) -> 位置: (${tileX}, ${tileY}), 大小: ${tileWidth}x${tileHeight}`); - - // 创建透明的交互式矩形,精确对齐瓦片 - // 使用add.zone创建透明区域,专门用于交互 - const tileZone = this.add.zone(tileX, tileY, tileWidth, tileHeight); - 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, tileWidth, tileHeight, 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; - } - }); + for (const [id, terrain] of Object.entries(this.terrainTypes)) { + // 地形按钮背景 + const terrainButton = this.add.rectangle(terrainX, terrainY, terrainButtonSize, terrainButtonSize, terrain.color, 0.8); + terrainButton.setOrigin(0, 0); + terrainButton.setInteractive({ useHandCursor: true }); + terrainButton.setData('terrainId', parseInt(id)); + this.terrainSelectionContainer.add(terrainButton); + + // 地形按钮文本 + this.terrainSelectionContainer.add(this.add.text( + terrainX + 4, + terrainY + 2, + id, + { + fontSize: '12px', + fill: '#ffffff', + fontFamily: 'Arial', + fontWeight: 'bold' } - - console.log('瓦片选择区域已更新,直接显示完整瓦片图'); - } else { - console.error(`纹理 ${this.tilemapKey} 不存在!`); + ).setOrigin(0, 0)); + + // 地形按钮点击事件 + terrainButton.on('pointerdown', () => { + this.selectTerrain(terrainButton.getData('terrainId')); + }); + + // 更新位置 + terrainX += terrainButtonSize + terrainSpacing; + if (terrainX >= 200) { + terrainX = 0; + terrainY += terrainButtonSize + terrainSpacing; } - } catch (error) { - console.error('更新瓦片选择显示失败:', error); } + + // 当前选中地形显示 + this.selectedTerrainText = this.add.text( + tilePanelX, + terrainPanelY+this.tileSize+terrainY+20, + `当前地形: ${this.terrainTypes[this.selectedTerrain].name}`, + { + fontSize: '12px', + fill: '#ffffff', + fontFamily: 'Arial' + } + ).setScrollFactor(0, 0).setDepth(11); + + // 地形模式切换按钮 + const terrainModeY = terrainPanelY+this.tileSize+terrainY+40; + this.terrainModeButton = this.add.rectangle(tilePanelX, terrainModeY, 120, 24, 0x333333, 0.8); + this.terrainModeButton.setOrigin(0, 0); + this.terrainModeButton.setInteractive({ useHandCursor: true }); + this.terrainModeButton.setDepth(11); + + // 地形模式按钮文本 + this.terrainModeText = this.add.text( + tilePanelX + 10, + terrainModeY + 4, + '切换到地形模式', + { + fontSize: '12px', + fill: '#ffffff', + fontFamily: 'Arial' + } + ).setScrollFactor(0, 0).setDepth(12); + + // 地形模式切换事件 + this.terrainModeButton.on('pointerdown', () => { + this.toggleTerrainMode(); + }); } + /** + * 选择地形 + */ + selectTerrain(terrainId) { + this.selectedTerrain = terrainId; + this.selectedTerrainText.setText(`当前地形: ${this.terrainTypes[this.selectedTerrain].name}`); + console.log(`选择了地形: ${this.terrainTypes[this.selectedTerrain].name}`); + } + + /** + * 切换地形模式 + */ + toggleTerrainMode() { + this.isTerrainMode = !this.isTerrainMode; + + if (this.isTerrainMode) { + this.terrainModeButton.setFillStyle(0x00ff00, 0.8); + this.terrainModeText.setText('地形模式: 开启'); + this.terrainModeText.setFill('#00ff00'); + } else { + this.terrainModeButton.setFillStyle(0x333333, 0.8); + this.terrainModeText.setText('切换到地形模式'); + this.terrainModeText.setFill('#ffffff'); + } + + console.log(`地形模式已${this.isTerrainMode ? '开启' : '关闭'}`); + } + /** * 选择瓦片 * @param {number} tileIndex - 瓦片索引 @@ -450,48 +557,53 @@ export default class MapEditTestScene extends Phaser.Scene { try { // 防御性编程:检查瓦片索引是否在有效范围内 if (this.selectedTile >= 0 && this.selectedTile < this.tileCount) { + const tileKey = this.tileSet[this.selectedTile]; + // 防御性编程:检查纹理是否存在 - if (this.textures.exists(this.tilemapKey)) { - // 根据用户提供的精确信息:瓦片16*16,无间距,18*13排列 - const tileWidth = 16; - const tileHeight = 16; - const spacing = 0; - const columns = 18; - - // 计算瓦片在大图中的位置 - const col = this.selectedTile % columns; - const row = Math.floor(this.selectedTile / columns); - - // 计算裁剪区域 - 正确使用图块对应的左上角像素位置 - const cropWidth = tileWidth; - const cropHeight = tileHeight; - - // 计算图块在大图中的精确左上角像素位置 - const frameX = col * tileWidth; - const frameY = row * tileHeight; - + if (this.textures.exists(tileKey)) { // 创建一个新的image,专门用于显示选中的瓦片 - const tile = this.add.image(0, 0, this.tilemapKey); + const tile = this.add.image(0, 0, tileKey); tile.setOrigin(0, 0); - // 先设置裁剪区域,使用图块在大图中的精确位置作为起始点 - tile.setCrop(frameX, frameY, cropWidth, cropHeight); - - // 调整瓦片位置,使其相对于容器(0,0)位置显示,而不是原图像位置 - tile.setPosition(-frameX*2, -frameY*2); - - // 然后设置缩放,放大显示 + // 放大显示 tile.setScale(2); this.selectedTileContainer.add(tile); // 添加选中边框,匹配放大后的瓦片大小 - const border = this.add.rectangle(0, 0, 64, 64, 0x00ff00, 0); + const border = this.add.rectangle(0, 0, this.tileSize*2, this.tileSize*2, 0x00ff00, 0); border.setOrigin(0, 0); border.setStrokeStyle(2, 0x00ff00, 1); this.selectedTileContainer.add(border); + + // 显示瓦片索引和行列信息 + const columns = 18; // 瓦片排列列数 + const row = Math.floor(this.selectedTile / columns); + const col = this.selectedTile % columns; + + // 瓦片索引文本 + this.selectedTileContainer.add(this.add.text( + 40, 0, + `索引: ${this.selectedTile}`, + { + fontSize: '12px', + fill: '#ffffff', + fontFamily: 'Arial' + } + ).setOrigin(0, 0)); + + // 瓦片行列文本 + this.selectedTileContainer.add(this.add.text( + 40, 16, + `行列: (${col},${row})`, + { + fontSize: '12px', + fill: '#ffffff', + fontFamily: 'Arial' + } + ).setOrigin(0, 0)); } else { - console.error(`selectTile: 纹理 ${this.tilemapKey} 不存在`); + console.error(`selectTile: 纹理 ${tileKey} 不存在`); } } else { console.error(`selectTile: 瓦片索引 ${this.selectedTile} 超出范围 (0-${this.tileCount-1})`); @@ -517,175 +629,15 @@ export default class MapEditTestScene extends Phaser.Scene { } /** - * 设置输入处理 - */ - setupInput() { - // 初始化拖动绘制相关变量 - this.isDrawing = false; - this.lastDrawnTile = null; - - // 鼠标输入 - 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.on('wheel', (pointer, gameObjects, deltaX, deltaY, deltaZ) => { - this.handleWheel(deltaY); - }); - - // 键盘输入 - this.setupKeyboardInput(); - } - - /** - * 设置键盘输入 - */ - setupKeyboardInput() { - // 空格键返回启动场景 - this.input.keyboard.on('keydown-SPACE', () => { - this.scene.start('BootScene'); - }); - - // 数字键1-9选择瓦片 - for (let i = 0; i < 9; i++) { - this.input.keyboard.on(`keydown-${i + 1}`, () => { - if (i < this.tileSet.length) { - this.selectTile(i); - } - }); - } - - // D键删除瓦片 - this.input.keyboard.on('keydown-D', () => { - this.selectedTile = -1; // -1表示删除模式 - }); - - // C键清空地图 - this.input.keyboard.on('keydown-C', () => { - this.clearMap(); - }); - - // S键保存地图 - this.input.keyboard.on('keydown-S', () => { - this.saveMap(); - }); - - // L键加载地图 - this.input.keyboard.on('keydown-L', () => { - this.loadMap(); - }); - } - - /** - * 处理鼠标按下事件 - */ - handleMouseDown(pointer) { - // 检查是否点击了瓦片选择区域 - const tileSelectionRect = new Phaser.Geom.Rectangle(20, 110, 200, 360); - if (Phaser.Geom.Rectangle.Contains(tileSelectionRect, pointer.x, pointer.y)) { - return; // 点击了瓦片选择区域,不处理地图交互 - } - - // 检查是否点击了控制面板 - const controlPanelRect = new Phaser.Geom.Rectangle(this.sys.game.config.width - 210, 10, 200, 150); - if (Phaser.Geom.Rectangle.Contains(controlPanelRect, pointer.x, pointer.y)) { - return; // 点击了控制面板,不处理地图交互 - } - - // 检查是否点击了当前选中瓦片显示区域 - const selectedTileRect = new Phaser.Geom.Rectangle(20, 490, 64, 64); - if (Phaser.Geom.Rectangle.Contains(selectedTileRect, pointer.x, pointer.y)) { - return; // 点击了选中瓦片显示区域,不处理地图交互 - } - - // 如果点击了地图区域 - if (this.selectedTile !== null) { - // 放置或删除瓦片 - this.placeTile(pointer); - } else { - // 开始拖拽地图 - this.isDragging = true; - this.lastDragPosition.x = pointer.x; - this.lastDragPosition.y = pointer.y; - } - } - - /** - * 处理鼠标释放事件 - */ - handleMouseUp(pointer) { - this.isDragging = false; - } - - /** - * 处理鼠标移动事件 - */ - handleMouseMove(pointer) { - if (this.isDragging) { - // 计算拖拽距离 - const deltaX = pointer.x - this.lastDragPosition.x; - const deltaY = pointer.y - this.lastDragPosition.y; - - // 移动相机 - this.cameras.main.scrollX -= deltaX / this.zoomLevel; - this.cameras.main.scrollY -= deltaY / this.zoomLevel; - - // 更新上次拖拽位置 - this.lastDragPosition.x = pointer.x; - this.lastDragPosition.y = pointer.y; - } - } - - /** - * 处理滚轮事件 - */ - handleWheel(deltaY) { - // 放大 - if (deltaY < 0 && this.zoomLevel < 2) { - this.zoomLevel += 0.1; - } - // 缩小 - else if (deltaY > 0 && this.zoomLevel > 0.5) { - this.zoomLevel -= 0.1; - } - - // 更新相机缩放 - this.cameras.main.setZoom(this.zoomLevel); - } - - /** - * 放置瓦片 + * 放置瓦片或设置地形 */ placeTile(pointer) { - // 将屏幕坐标转换为世界坐标 - 确保与鼠标位置完全匹配 + // 将屏幕坐标转换为世界坐标 const worldPoint = this.cameras.main.getWorldPoint(pointer.x, pointer.y); - // 计算瓦片坐标 - 使用地图的瓦片大小32px - const mapTileSize = 32; - - // 确保鼠标点击位置与绘制位置完全一致 - // 使用Math.floor确保点击瓦片的哪个位置都绘制到对应瓦片 - const tileX = Math.floor(worldPoint.x / mapTileSize); - const tileY = Math.floor(worldPoint.y / mapTileSize); + // 计算瓦片坐标 + const tileX = Math.floor(worldPoint.x / this.tileSize); + const tileY = Math.floor(worldPoint.y / this.tileSize); // 检查瓦片坐标是否在地图范围内 if (tileX < 0 || tileX >= this.mapWidth || tileY < 0 || tileY >= this.mapHeight) { @@ -697,11 +649,18 @@ export default class MapEditTestScene extends Phaser.Scene { return; } - // 删除瓦片模式 - if (this.selectedTile === -1) { + // 地形模式:只设置地形,不修改瓦片 + if (this.isTerrainMode) { + // 更新地图数据中的地形ID + this.mapData[tileY][tileX].terrainId = this.selectedTerrain; + console.log(`已设置瓦片(${tileX},${tileY})的地形为: ${this.terrainTypes[this.selectedTerrain].name}`); + } + // 瓦片模式:放置或删除瓦片 + else if (this.selectedTile === -1) { this.removeTile(tileX, tileY); } else { - // 放置瓦片 - 使用单张瓦片图 + // 放置瓦片 + const tileKey = this.tileSet[this.selectedTile]; // 检查该位置是否已有瓦片 const existingTile = this.getTileAt(tileX, tileY); @@ -710,52 +669,24 @@ export default class MapEditTestScene extends Phaser.Scene { existingTile.destroy(); } - // 计算精确的绘制位置 - 确保与鼠标点击的瓦片完全对应 - const drawX = tileX * mapTileSize; - const drawY = tileY * mapTileSize; + // 计算精确的绘制位置 + const drawX = tileX * this.tileSize; + const drawY = tileY * this.tileSize; console.log(`鼠标位置: (${pointer.x}, ${pointer.y}) -> 世界坐标: (${worldPoint.x}, ${worldPoint.y}) -> 瓦片坐标: (${tileX}, ${tileY}) -> 绘制位置: (${drawX}, ${drawY})`); - // 创建新瓦片 - 精确放置在计算出的位置 + // 创建新瓦片 try { - // 根据用户提供的精确信息:瓦片16*16,无间距,18*13排列 - const tileWidth = 16; - const tileHeight = 16; - const columns = 18; - - // 计算瓦片在大图中的位置 - const col = this.selectedTile % columns; - const row = Math.floor(this.selectedTile / columns); - - // 计算裁剪区域 - 正确使用图块对应的左上角像素位置 - const cropX = col * tileWidth; // 图块在大图中的x坐标 - const cropY = row * tileHeight; // 图块在大图中的y坐标 - const cropWidth = tileWidth; - const cropHeight = tileHeight; - - console.log(`选中瓦片索引: ${this.selectedTile} -> 行列: (${col}, ${row}) -> 裁剪区域: (${cropX}, ${cropY}, ${cropWidth}, ${cropHeight})`); - - // 创建image并设置初始位置 - const tile = this.add.image(drawX, drawY, this.tilemapKey); + const tile = this.add.image(drawX, drawY, tileKey); tile.setOrigin(0, 0); - - // 先设置裁剪区域,使用图块在大图中的精确位置 - tile.setCrop(cropX, cropY, cropWidth, cropHeight); - - // 调整瓦片位置,使其相对于目标位置(0,0)显示,而不是原图像位置 - tile.setPosition(drawX - cropX*2, drawY - cropY*2); - - // 然后设置缩放,确保瓦片填满地图网格 - const scaleFactor = mapTileSize / cropWidth; - tile.setScale(scaleFactor); - - console.log(`绘制位置: (${drawX}, ${drawY}), 缩放因子: ${scaleFactor}`); - tile.setDepth(2); this.mapLayer.add(tile); - // 更新地图数据 - this.mapData[tileY][tileX] = this.selectedTile; + // 更新地图数据,包含瓦片索引和地形ID + this.mapData[tileY][tileX] = { + tileIndex: this.selectedTile, + terrainId: this.selectedTerrain + }; } catch (error) { console.error('放置瓦片失败:', error); } @@ -775,8 +706,11 @@ export default class MapEditTestScene extends Phaser.Scene { // 删除现有瓦片 existingTile.destroy(); - // 更新地图数据 - this.mapData[tileY][tileX] = -1; + // 更新地图数据,保留地形信息 + this.mapData[tileY][tileX] = { + tileIndex: -1, + terrainId: this.mapData[tileY][tileX].terrainId + }; } } @@ -805,7 +739,7 @@ export default class MapEditTestScene extends Phaser.Scene { // 重置地图数据 for (let y = 0; y < this.mapHeight; y++) { for (let x = 0; x < this.mapWidth; x++) { - this.mapData[y][x] = -1; + this.mapData[y][x] = { tileIndex: -1, terrainId: 0 }; } } @@ -872,7 +806,10 @@ export default class MapEditTestScene extends Phaser.Scene { // 重新绘制地图 for (let y = 0; y < this.mapHeight; y++) { for (let x = 0; x < this.mapWidth; x++) { - const tileIndex = this.mapData[y][x]; + // 兼容旧数据格式和新数据格式 + const tileData = typeof this.mapData[y][x] === 'object' ? this.mapData[y][x] : { tileIndex: this.mapData[y][x], terrainId: 0 }; + const tileIndex = tileData.tileIndex; + if (tileIndex !== -1 && tileIndex < this.tileSet.length) { const tileKey = this.tileSet[tileIndex]; try { @@ -889,34 +826,4 @@ export default class MapEditTestScene extends Phaser.Scene { console.log('地图已加载'); } - - /** - * 显示提示信息 - */ - showInstructions() { - const instructions = [ - '地图编辑测试场景', - '点击瓦片选择区域选择瓦片', - '在地图上点击放置瓦片', - '选择瓦片后按D键进入删除模式', - '鼠标拖拽移动地图', - '鼠标滚轮缩放地图', - '按C键清空地图', - '按S键保存地图', - '按L键加载地图', - '按空格键返回启动场景' - ]; - - for (let i = 0; i < instructions.length; i++) { - this.add.text(this.sys.game.config.width - 300, 180 + i * 20, instructions[i], { - fontSize: '12px', - fill: '#ffffff', - fontFamily: 'Arial' - }).setScrollFactor(0, 0).setDepth(11); - } - } - - update() { - // 每帧更新 - } } \ No newline at end of file diff --git a/scenes/TmpScene.js b/scenes/TmpScene.js deleted file mode 100644 index 43c30b1..0000000 --- a/scenes/TmpScene.js +++ /dev/null @@ -1,427 +0,0 @@ -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; - this.physics.world.setBounds(0, 0, worldSize, worldSize); - this.cameras.main.setBounds(0, 0, worldSize, worldSize); - // 设置背景色 - this.cameras.main.setBackgroundColor('#222222'); - - // 创建地图编辑区域 - console.log('创建地图编辑区域'); - this.createMapArea(); - - // 创建瓦片选择区域 - 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); - this.mapContainer.setDepth(1); - - // 创建背景网格 - this.createGrid(); - - // 创建地图层(用于放置瓦片) - this.mapLayer = this.add.container(0, 0); - this.mapContainer.add(this.mapLayer); - - // 初始化空地图 - this.mapData = []; - for (let y = 0; y < this.mapHeight; y++) { - this.mapData[y] = []; - for (let x = 0; x < this.mapWidth; x++) { - this.mapData[y][x] = -1; // -1表示空瓦片 - } - } - } - - //创建背景网格 - createGrid() { - // 创建网格线 - const gridGraphics = this.add.graphics(); - gridGraphics.lineStyle(1, 0x444444, 0.5); - - // 绘制垂直线 - for (let x = 0; x <= this.mapWidth; x++) { - gridGraphics.moveTo(x * this.tileSize, 0); - gridGraphics.lineTo(x * this.tileSize, this.mapHeight * this.tileSize); - } - - // 绘制水平线 - for (let y = 0; y <= this.mapHeight; y++) { - gridGraphics.moveTo(0, y * this.tileSize); - gridGraphics.lineTo(this.mapWidth * this.tileSize, y * this.tileSize); - } - - this.mapContainer.add(gridGraphics); - } - - createTileSelectionArea() { - // 瓦片选择区域位置 - const tilePanelX = 10; - const tilePanelY = 10; - - // 瓦片选择区域标题 - this.add.text(tilePanelX, tilePanelY, '瓦片选择', { - 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+this.tileSize); - this.tileSelectionContainer.setScrollFactor(0, 0); - this.tileSelectionContainer.setDepth(11); - - // 当前选中瓦片显示 - 改为使用Container容器,而不是Rectangle,这样可以使用removeAll方法 - 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, 32, 32, 0x333333); - selectedTileBg.setOrigin(0, 0); - selectedTileBg.setDepth(10); - this.selectedTileContainer.add(selectedTileBg); - - 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