// 输入系统:处理玩家的键盘、鼠标和触摸输入,将输入转换为游戏事件 // 从ControlMode.js导入清除选择和设置激活单位的函数 import { clearSelection, setActiveUnit } from './ControlMode.js'; // 导出InputSystem类,用于创建输入系统实例 export class InputSystem { // 构造函数:初始化输入系统 // @param {Object} scene - 游戏场景实例 // @param {Object} config - 输入配置选项 constructor(scene, config = {}) { // 保存场景引用,用于访问游戏世界和发送事件 this.scene = scene; // 用于存储框选开始时的鼠标世界坐标 // 初始值为null,表示当前没有进行框选操作 this.selectionStart = null; // 移动方式开关配置 this.enabledModes = { keyboard: config.keyboard !== false, // 键盘控制 mouse: config.mouse !== false, // 鼠标点击移动 joystick: config.joystick !== false, // 虚拟摇杆控制 selection: config.selection !== false // 框选功能 }; // 初始化键盘输入 // 获取Phaser的键盘输入管理器 const kb = scene.input.keyboard; // 创建按键映射对象,将游戏功能与键盘按键关联 this.keys = { // 上移键:同时支持W键和方向上键 up: [kb.addKey(Phaser.Input.Keyboard.KeyCodes.W), kb.addKey(Phaser.Input.Keyboard.KeyCodes.UP)], // 下移键:同时支持S键和方向下键 down: [kb.addKey(Phaser.Input.Keyboard.KeyCodes.S), kb.addKey(Phaser.Input.Keyboard.KeyCodes.DOWN)], // 左移键:同时支持A键和方向左键 left: [kb.addKey(Phaser.Input.Keyboard.KeyCodes.A), kb.addKey(Phaser.Input.Keyboard.KeyCodes.LEFT)], // 右移键:同时支持D键和方向右键 right: [kb.addKey(Phaser.Input.Keyboard.KeyCodes.D), kb.addKey(Phaser.Input.Keyboard.KeyCodes.RIGHT)] }; // 添加F键快捷拾取功能 this.fKey = kb.addKey(Phaser.Input.Keyboard.KeyCodes.F); this.fKey.on('down', () => { this.handleQuickPickup(); }); // 禁用浏览器右键菜单,避免影响游戏操作 scene.input.mouse.disableContextMenu(); // 注册鼠标/触摸事件监听器 // 当鼠标/触摸按下时,调用onPointerDown方法 scene.input.on('pointerdown', this.onPointerDown, this); // 当鼠标/触摸移动时,调用onPointerMove方法 scene.input.on('pointermove', this.onPointerMove, this); // 当鼠标/触摸释放时,调用onPointerUp方法 scene.input.on('pointerup', this.onPointerUp, this); } // 检查按键组中是否有任何按键被按下 // @param {Array} keyGroup - 按键对象数组 // @returns {boolean} - 如果有任何按键被按下则返回true,否则返回false isKeyDown(keyGroup) { // 使用some方法检查数组中是否有任何一个按键处于按下状态 return keyGroup.some(key => key.isDown); } // update方法:每帧自动调用(约60次/秒),用于处理持续的输入 update() { // 只有在键盘模式启用时才处理键盘输入 if (this.enabledModes.keyboard) { // 创建一个方向对象,用于存储移动方向 const dir = { x: 0, y: 0 }; // 检查左移键是否被按下,如果是则x方向减1(向左移动) if (this.isKeyDown(this.keys.left)) dir.x -= 1; // 检查右移键是否被按下,如果是则x方向加1(向右移动) if (this.isKeyDown(this.keys.right)) dir.x += 1; // 检查上移键是否被按下,如果是则y方向减1(向上移动) if (this.isKeyDown(this.keys.up)) dir.y -= 1; // 检查下移键是否被按下,如果是则y方向加1(向下移动) if (this.isKeyDown(this.keys.down)) dir.y += 1; // 无论是否有按键按下,都发送移动事件 // 当dir.x和dir.y都为0时,单位会停止移动 this.scene.events.emit('move-relative', dir); } } // 处理鼠标/触摸按下事件 // @param {Object} pointer - 指针事件对象,包含鼠标/触摸的位置等信息 onPointerDown(pointer) { // 检查鼠标是否在UI元素上,如果是则不处理 if (this.isMouseOnUI(pointer)) { return; } // 如果是左键按下 if (pointer.leftButtonDown()) { // 获取鼠标在游戏世界中的坐标(不是屏幕坐标) const worldX = pointer.worldX; const worldY = pointer.worldY; // 检查是否点中了某个单位 let clickedUnit = null; // 遍历场景中的所有单位 this.scene.unitGroup.getChildren().forEach(unit => { // 获取单位的边界矩形 const bounds = unit.getBounds(); // 检查鼠标世界坐标是否在单位的边界内 if (worldX >= bounds.x && worldX <= bounds.right && worldY >= bounds.y && worldY <= bounds.bottom) { // 如果点中了单位,存储该单位 clickedUnit = unit; } }); // 检查是否点中了某个物品 let clickedItem = null; // 遍历场景中的所有物品 if (this.scene.itemManager && this.scene.itemManager.getActiveItems) { this.scene.itemManager.getActiveItems().forEach(item => { // 获取物品的边界矩形 const bounds = item.getBounds(); // 检查鼠标世界坐标是否在物品的边界内 if (worldX >= bounds.x && worldX <= bounds.right && worldY >= bounds.y && worldY <= bounds.bottom) { // 如果点中了物品,存储该物品 clickedItem = item; } }); } if (clickedUnit) { // 如果点中了单位,发送选择单位事件 this.scene.events.emit('select-unit', clickedUnit); // 存储点击位置,用于判断是否是拖动操作 this.dragStart = { x: worldX, y: worldY, clickedUnit: clickedUnit }; // 不设置selectionStart,避免触发框选 this.selectionStart = null; } else if (clickedItem) { // 如果点中了物品,不执行后续操作,避免触发移动 this.selectionStart = null; this.dragStart = null; } else if (this.enabledModes.selection && !this.isCameraLocked()) { // 如果没有点中单位,且框选功能启用,且相机未锁定,开始框选操作 // 存储框选开始时的世界坐标 this.selectionStart = { x: worldX, y: worldY }; // 没有点中单位,清除dragStart this.dragStart = null; } } else if (pointer.rightButtonDown() && this.enabledModes.mouse) { // 如果是右键按下且鼠标模式启用,发送移动到指定点的事件 const worldX = pointer.worldX; const worldY = pointer.worldY; this.scene.events.emit('move-to-point', worldX, worldY); // 右键点击,清除dragStart this.dragStart = null; } } // 处理鼠标/触摸移动事件 // @param {Object} pointer - 指针事件对象 onPointerMove(pointer) { // 检查鼠标是否在UI元素上,如果是则不处理 if (this.isMouseOnUI(pointer)) { return; } // 如果当前正在进行框选操作且框选功能启用 if (this.selectionStart && this.enabledModes.selection) { // 获取当前鼠标在游戏世界中的坐标 const currentWorldX = pointer.worldX; const currentWorldY = pointer.worldY; // 计算框选区域的矩形 // 确保矩形的x和y是左上角坐标 const rect = new Phaser.Geom.Rectangle( // 使用Math.min确保x是较小的值(左上角) Math.min(this.selectionStart.x, currentWorldX), // 使用Math.min确保y是较小的值(左上角) Math.min(this.selectionStart.y, currentWorldY), // 计算矩形宽度 Math.abs(currentWorldX - this.selectionStart.x), // 计算矩形高度 Math.abs(currentWorldY - this.selectionStart.y) ); // 只有当框选区域足够大时才发送选择更新事件 if (rect.width > 5 || rect.height > 5) { // 发送选择更新事件,更新框选区域的显示 this.scene.events.emit('selection-start', this.selectionStart); this.scene.events.emit('selection-update', rect); } } } // 处理鼠标/触摸释放事件 // @param {Object} pointer - 指针事件对象 onPointerUp(pointer) { // 检查鼠标是否在UI元素上,如果是则不处理 if (this.isMouseOnUI(pointer)) { // 重置框选起始点和拖动状态 this.selectionStart = null; this.dragStart = null; return; } // 处理拖动单位的逻辑 if (this.dragStart) { // 获取当前鼠标在游戏世界中的坐标 const currentWorldX = pointer.worldX; const currentWorldY = pointer.worldY; // 计算拖动距离 const dx = currentWorldX - this.dragStart.x; const dy = currentWorldY - this.dragStart.y; const dragDistance = Math.sqrt(dx * dx + dy * dy); // 如果拖动距离超过阈值(10像素),视为拖动操作 if (dragDistance > 10 && this.enabledModes.mouse) { // 发送移动到指定点的事件,让单位移动到释放位置 this.scene.events.emit('move-to-point', currentWorldX, currentWorldY); } // 重置拖动状态 this.dragStart = null; } // 如果当前正在进行框选操作且框选功能启用 else if (this.selectionStart && this.enabledModes.selection) { // 获取当前鼠标在游戏世界中的坐标 const currentWorldX = pointer.worldX; const currentWorldY = pointer.worldY; // 计算最终的框选区域 const rect = new Phaser.Geom.Rectangle( Math.min(this.selectionStart.x, currentWorldX), Math.min(this.selectionStart.y, currentWorldY), Math.abs(currentWorldX - this.selectionStart.x), Math.abs(currentWorldY - this.selectionStart.y) ); // 如果框选区域太小(宽度和高度都小于10像素),视为点击地面移动 if (rect.width < 10 && rect.height < 10 && this.enabledModes.mouse) { // 发送移动到指定点的事件(仅在鼠标模式启用时) this.scene.events.emit('move-to-point', pointer.worldX, pointer.worldY); } else { // 否则执行框选操作 this.scene.events.emit('selection-end', rect); } // 重置框选起始点,结束框选操作 this.selectionStart = null; } } /** * 检查鼠标是否在小地图上 * @param {Phaser.Input.Pointer} pointer - 指针事件对象 * @returns {boolean} - 如果鼠标在小地图上返回true,否则返回false */ isMouseOnMiniMap(pointer) { // 检查场景中是否有小地图 if (!this.scene.miniMap) { return false; } // 获取小地图实例 const miniMap = this.scene.miniMap; // 获取鼠标在屏幕上的坐标 const mouseX = pointer.x; const mouseY = pointer.y; // 计算小地图的边界(包括锁定按钮区域) const mapLeft = miniMap.x; // 小地图左边界 const mapRight = miniMap.x + miniMap.mapSize; // 小地图右边界 const mapTop = miniMap.y; // 小地图上边界 // 小地图下边界,包括锁定按钮(按钮高度25,边距5) const mapBottom = miniMap.y + miniMap.mapSize + 30; // 检查鼠标坐标是否在小地图边界内 return mouseX >= mapLeft && mouseX <= mapRight && mouseY >= mapTop && mouseY <= mapBottom; } /** * 检查鼠标是否在UI元素上 * @param {Phaser.Input.Pointer} pointer - 指针事件对象 * @returns {boolean} - 如果鼠标在UI元素上返回true,否则返回false */ isMouseOnUI(pointer) { return this.isMouseOnMiniMap(pointer) || this.isMouseOnJoystick(pointer) || this.isMouseOnSidebar(pointer) || this.isMouseOnInventoryUI(pointer); } /** * 检查鼠标是否在虚拟摇杆上 * @param {Phaser.Input.Pointer} pointer - 指针事件对象 * @returns {boolean} - 如果鼠标在摇杆上返回true,否则返回false */ isMouseOnJoystick(pointer) { // 检查场景中是否有虚拟摇杆 if (!this.scene.virtualJoystick) { return false; } // 获取虚拟摇杆实例 const joystick = this.scene.virtualJoystick; // 获取鼠标在屏幕上的坐标 const mouseX = pointer.x; const mouseY = pointer.y; // 计算摇杆的边界(包括摇杆和操作区域) const joystickRadius = joystick.radius * 2; // 操作区域是摇杆半径的两倍 const joystickLeft = joystick.centerX - joystickRadius; // 摇杆左边界 const joystickRight = joystick.centerX + joystickRadius; // 摇杆右边界 const joystickTop = joystick.centerY - joystickRadius; // 摇杆上边界 const joystickBottom = joystick.centerY + joystickRadius; // 摇杆下边界 // 检查鼠标坐标是否在摇杆边界内 return mouseX >= joystickLeft && mouseX <= joystickRight && mouseY >= joystickTop && mouseY <= joystickBottom; } /** * 检查相机是否被锁定到某个角色 * @returns {boolean} - 如果相机被锁定返回true,否则返回false */ isCameraLocked() { // 检查ControlMode模块是否存在且cameraLocked属性已定义 if (typeof window !== 'undefined' && window.cameraLocked !== undefined) { return window.cameraLocked; } // 检查场景中是否有小地图且处于锁定状态 if (this.scene.miniMap && this.scene.miniMap.isLocked) { return this.scene.miniMap.isLocked; } return false; } /** * 检查鼠标是否在侧边栏上 * @param {Phaser.Input.Pointer} pointer - 指针事件对象 * @returns {boolean} - 如果鼠标在侧边栏上返回true,否则返回false */ isMouseOnSidebar(pointer) { // 检查场景中是否有侧边栏 if (!this.scene.sidebar) { return false; } // 获取侧边栏实例 const sidebar = this.scene.sidebar; // 获取鼠标在屏幕上的坐标 const mouseX = pointer.x; const mouseY = pointer.y; // 获取侧边栏的边界 const bounds = sidebar.getBounds(); // 检查鼠标坐标是否在侧边栏边界内 return mouseX >= bounds.x && mouseX <= bounds.right && mouseY >= bounds.y && mouseY <= bounds.bottom; } /** * 检查鼠标是否在背包UI上 * @param {Phaser.Input.Pointer} pointer - 指针事件对象 * @returns {boolean} - 如果鼠标在背包UI上返回true,否则返回false */ isMouseOnInventoryUI(pointer) { // 检查场景中是否有背包UI if (!this.scene.inventoryUI) { return false; } // 获取背包UI实例 const inventoryUI = this.scene.inventoryUI; // 获取鼠标在屏幕上的坐标 const mouseX = pointer.x; const mouseY = pointer.y; // 获取背包UI的边界 const container = inventoryUI.container; if (!container) { return false; } const containerBounds = container.getBounds(); // 检查鼠标坐标是否在背包UI边界内 return mouseX >= containerBounds.x && mouseX <= containerBounds.right && mouseY >= containerBounds.y && mouseY <= containerBounds.bottom; } /** * 处理F键快捷拾取功能 */ handleQuickPickup() { console.log('handleQuickPickup: 尝试快捷拾取物品'); // 确保场景中有玩家和物品管理器 if (!this.scene.player || !this.scene.itemManager) { console.log('handleQuickPickup: 缺少玩家或物品管理器'); return; } const player = this.scene.player; const itemManager = this.scene.itemManager; // 获取所有活动物品 const activeItems = itemManager.getActiveItems ? itemManager.getActiveItems() : []; if (activeItems.length === 0) { console.log('handleQuickPickup: 没有可拾取的物品'); return; } const pickupDistance = 100; // 最大拾取距离 let closestItem = null; let minDistance = pickupDistance; // 找到距离玩家最近且在拾取范围内的物品 activeItems.forEach(item => { // 计算物品与玩家的距离 const dx = item.x - player.x; const dy = item.y - player.y; const distance = Math.sqrt(dx * dx + dy * dy); // 如果物品在拾取范围内且距离更近,更新最近物品 if (distance < minDistance) { minDistance = distance; closestItem = item; } }); // 如果找到最近物品,尝试拾取 if (closestItem) { console.log(`handleQuickPickup: 拾取距离最近的物品: ${closestItem.name},距离: ${Math.round(minDistance)}`); closestItem.pickup(player); // 更新背包UI,确保物品立即显示在背包中 if (this.scene.inventoryUI) { this.scene.inventoryUI.updateUI(); } } else { console.log('handleQuickPickup: 没有在拾取范围内的物品'); } } }