mirror of
http://junepve.top:3000/chicrobot/CINC.git
synced 2026-10-30 01:31:29 +08:00
922 lines
29 KiB
JavaScript
922 lines
29 KiB
JavaScript
// 地图编辑测试场景
|
||
// 防御性编程原则:
|
||
// 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() {
|
||
// 每帧更新
|
||
}
|
||
} |