// 物品列表菜单UI类,用于显示可交互范围内的所有物品 export class ItemListUI { constructor(scene, x, y, width = 250, height = 300) { this.scene = scene; this.x = x; this.y = y; this.width = width; this.height = height; this.visible = false; this.items = []; this.selectedItemId = null; // 保存选中物品的ID,而不是索引 this.interactionRange = 50; // 与Item类中的交互距离保持一致 this.itemListElements = []; this.optionsElements = []; // 初始化玩家位置缓存,用于优化更新频率 this.lastPlayerX = 0; this.lastPlayerY = 0; // 添加更新间隔,限制距离信息的更新频率(毫秒) this.updateInterval = 100; this.lastUpdateTime = 0; this.createUI(); } /** * 创建UI元素 */ createUI() { console.log('ItemListUI.createUI: 开始创建物品列表UI'); // 直接在场景中创建元素,不使用容器,避免容器带来的坐标问题 // 背景 this.background = this.scene.add.rectangle(this.x, this.y, this.width, this.height, 0x111111, 0.95); this.background.setOrigin(0, 0); this.background.setStrokeStyle(2, 0x444444, 1); this.background.setScrollFactor(0, 0); // 固定在屏幕上 this.background.setDepth(20); // 设置深度,确保显示在其他UI上方 // 标题 this.title = this.scene.add.text(this.x + 10, this.y + 5, '可交互物品', { fontSize: '16px', fill: '#ffffff', fontWeight: 'bold', fontFamily: 'Arial' }); this.title.setOrigin(0, 0); this.title.setScrollFactor(0, 0); this.title.setDepth(21); // 物品列表区域 this.listX = this.x; this.listY = this.y + 30; this.listHeight = this.height - 60; console.log('ItemListUI.createUI: 物品列表区域设置完成,位置:', this.listX, this.listY, '高度:', this.listHeight); // 关闭按钮 - 直接在场景中创建 this.closeButton = this.scene.add.rectangle(this.x + this.width - 25, this.y + 10, 20, 20, 0x555555); this.closeButton.setOrigin(0.5, 0.5); this.closeButton.setScrollFactor(0, 0); this.closeButton.setDepth(21); this.closeText = this.scene.add.text(this.x + this.width - 25, this.y + 10, 'X', { fontSize: '14px', fill: '#ffffff', fontFamily: 'Arial' }); this.closeText.setOrigin(0.5, 0.5); this.closeText.setScrollFactor(0, 0); this.closeText.setDepth(22); // 关闭按钮交互 - 确保正确绑定 this.closeButton.setInteractive({ useHandCursor: true }); this.closeButton.on('pointerdown', () => { console.log('ItemListUI: 关闭按钮点击'); this.hide(); }); this.closeButton.on('pointerover', () => { console.log('ItemListUI: 关闭按钮悬停'); this.closeButton.setFillStyle(0x777777); }); this.closeButton.on('pointerout', () => { console.log('ItemListUI: 关闭按钮移出'); this.closeButton.setFillStyle(0x555555); }); // 初始隐藏状态 this.setVisible(this.visible); console.log('ItemListUI.createUI: UI创建完成,初始可见性:', this.visible); } /** * 更新物品列表,显示可交互范围内的所有物品 * @param {Object} unit - 玩家单位 * @param {Array} items - 所有物品数组 */ updateItems(unit, items) { // 过滤出可交互范围内且未被拾取的物品,并计算距离 const newItems = []; items.forEach(item => { if (item.isInInteractionRange(unit) && !item.inInventory) { // 计算物品与玩家的距离 const dx = item.x - unit.x; const dy = item.y - unit.y; item.distance = Math.sqrt(dx * dx + dy * dy); newItems.push(item); } }); // 按距离从小到大排序物品列表 newItems.sort((a, b) => a.distance - b.distance); // 检查物品列表是否发生变化(物品数量或ID变化) const itemsChanged = this.isItemsChanged(newItems); // 获取当前时间 const currentTime = Date.now(); // 更新物品列表 this.items = newItems; // 检查选中的物品是否仍然在列表中,如果不在则清除选中状态 if (this.selectedItemId) { const isItemStillInList = newItems.some(item => item.id === this.selectedItemId); if (!isItemStillInList) { this.selectedItemId = null; this.clearItemOptions(); } } // 只有在以下情况之一时才刷新列表: // 1. 物品列表发生变化(物品数量或ID变化) // 2. 距离上一次更新超过指定间隔(用于更新距离信息) if (itemsChanged || (currentTime - this.lastUpdateTime > this.updateInterval)) { // 刷新列表 this.refreshList(); this.lastUpdateTime = currentTime; } } /** * 检查物品列表是否发生变化 * @param {Array} newItems - 新的物品列表 * @returns {boolean} - 列表是否发生变化 */ isItemsChanged(newItems) { // 如果物品数量不同,列表肯定发生了变化 if (newItems.length !== this.items.length) { return true; } // 如果列表为空,无需进一步检查 if (newItems.length === 0) { return false; } // 检查物品ID是否完全相同 const oldIds = this.items.map(item => item.id).sort(); const newIds = newItems.map(item => item.id).sort(); for (let i = 0; i < oldIds.length; i++) { if (oldIds[i] !== newIds[i]) { return true; } } return false; } /** * 刷新物品列表显示 */ refreshList() { console.log('ItemListUI.refreshList: 开始刷新物品列表,物品数量:', this.items.length); // 清除现有列表项 - 移除所有动态创建的列表项 if (this.itemListElements) { this.itemListElements.forEach(element => { element.destroy(); }); } this.itemListElements = []; // 如果没有可交互物品,显示提示 if (this.items.length === 0) { const noItemsText = this.scene.add.text(this.x + 10, this.listY + 10, '没有可交互物品', { fontSize: '14px', fill: '#888888', fontFamily: 'Arial' }); noItemsText.setOrigin(0, 0); noItemsText.setScrollFactor(0, 0); // 固定在屏幕上 noItemsText.setDepth(21); this.itemListElements.push(noItemsText); return; } // 创建新的列表项 this.items.forEach((item, index) => { const itemHeight = 30; // 计算在屏幕上的实际Y坐标 const y = this.listY + index * itemHeight; // 根据物品ID判断是否选中,而不是索引 const isSelected = this.selectedItemId === item.id; // 列表项背景 - 直接使用屏幕坐标 const background = this.scene.add.rectangle(this.x + 5, y + itemHeight / 2, this.width - 10, itemHeight - 5, isSelected ? 0x444444 : 0x222222); background.setOrigin(0, 0.5); background.setScrollFactor(0, 0); // 固定在屏幕上 // 保存原始颜色,用于悬停效果 background.originalColor = isSelected ? 0x444444 : 0x222222; background.isSelected = isSelected; // 确保背景在合适的深度 background.setDepth(21); // 物品名称 const nameText = this.scene.add.text(this.x + 15, y + 5, item.name, { fontSize: '14px', fill: '#ffffff', fontFamily: 'Arial' }); nameText.setOrigin(0, 0); nameText.setScrollFactor(0, 0); nameText.setDepth(22); // 物品类型 const typeText = this.scene.add.text(this.x + 15, y + 20, `[${item.type}]`, { fontSize: '12px', fill: '#888888', fontFamily: 'Arial' }); typeText.setOrigin(0, 0); typeText.setScrollFactor(0, 0); typeText.setDepth(22); // 距离信息 - 使用预计算的距离,提高性能 const distanceText = this.scene.add.text(this.x + this.width - 60, y + 15, `${Math.round(item.distance)}m`, { fontSize: '12px', fill: '#4CAF50', fontFamily: 'Arial' }); distanceText.setOrigin(0, 0.5); distanceText.setScrollFactor(0, 0); distanceText.setDepth(22); // 按键提示 let keyText = null; if (index < 9) { keyText = this.scene.add.text(this.x + this.width - 30, y + 15, `${index + 1}`, { fontSize: '12px', fill: '#FFC107', fontFamily: 'Arial', fontWeight: 'bold' }); keyText.setOrigin(0, 0.5); keyText.setScrollFactor(0, 0); keyText.setDepth(22); } // 设置背景为可交互,并确保它能接收鼠标事件 background.setInteractive({ useHandCursor: true }); // 点击事件 background.on('pointerdown', () => { console.log(`Item clicked: ${item.name}, index: ${index}, id: ${item.id}`); this.selectItem(index); }); // 悬停效果 background.on('pointerover', () => { console.log(`Item hovered: ${item.name}, index: ${index}`); if (!background.isSelected) { background.setFillStyle(0x333333); } }); // 移出效果 background.on('pointerout', () => { console.log(`Item unhovered: ${item.name}, index: ${index}`); background.setFillStyle(background.originalColor); }); // 保存元素引用,便于后续清理 this.itemListElements.push(background); this.itemListElements.push(nameText); this.itemListElements.push(typeText); this.itemListElements.push(distanceText); if (keyText) { this.itemListElements.push(keyText); } console.log(`ItemListUI: 为物品 ${item.name} 创建了列表项,索引 ${index},位置 Y: ${y}`); }); } /** * 选择物品 * @param {number} index - 物品索引 */ selectItem(index) { console.log('selectItem called with index:', index, 'items length:', this.items.length); if (index >= 0 && index < this.items.length) { // 使用物品ID来保存选中状态,而不是索引 const item = this.items[index]; this.selectedItemId = item.id; // 触发物品点击事件,与现有系统保持一致 this.scene.events.emit('item-clicked', item); // 显示操作选项 this.showItemOptions(item); // 更新列表显示,高亮选中项 this.refreshList(); } } /** * 显示物品操作选项 * @param {Item} item - 物品实例 */ showItemOptions(item) { // 清除现有的操作选项 this.clearItemOptions(); // 选项按钮宽度 const buttonWidth = 80; const buttonHeight = 30; const buttonSpacing = 5; // 创建拾取按钮 this.createOptionButton(this.x + 5, this.y + this.height - 55, '拾取', () => { item.pickup(this.scene.player); // 更新背包UI if (this.scene.inventoryUI) { this.scene.inventoryUI.updateUI(); } // 更新物品列表 this.updateItems(this.scene.player, this.scene.itemManager.getActiveItems()); // 清除操作选项 this.clearItemOptions(); }); // 创建使用按钮 this.createOptionButton(this.x + buttonWidth + buttonSpacing + 5, this.y + this.height - 55, '使用', () => { item.use(this.scene.player); // 更新物品列表 this.updateItems(this.scene.player, this.scene.itemManager.getActiveItems()); // 清除操作选项 this.clearItemOptions(); }); // 创建查看详情按钮 this.createOptionButton(this.x + (buttonWidth + buttonSpacing) * 2 + 5, this.y + this.height - 55, '详情', () => { // 显示物品详情,与现有系统保持一致 this.scene.events.emit('item-clicked', item); }); // 显示交互提示 if (this.scene.interactionText) { this.scene.interactionText.setText('点击按钮或按对应快捷键操作'); } } /** * 创建操作选项按钮 * @param {number} x - 按钮x坐标 * @param {number} y - 按钮y坐标 * @param {string} text - 按钮文本 * @param {function} callback - 点击回调函数 */ createOptionButton(x, y, text, callback) { // 按钮背景 - 直接使用屏幕坐标 const button = this.scene.add.rectangle(x, y + 15, 80, 30, 0x333333); button.setOrigin(0, 0.5); button.setStrokeStyle(1, 0x666666, 1); button.setScrollFactor(0, 0); // 固定在屏幕上 button.setDepth(23); // 按钮文本 const buttonText = this.scene.add.text(x + 40, y + 15, text, { fontSize: '12px', fill: '#ffffff', fontFamily: 'Arial', align: 'center' }); buttonText.setOrigin(0.5, 0.5); buttonText.setScrollFactor(0, 0); buttonText.setDepth(24); // 设置交互 button.setInteractive({ useHandCursor: true }); // 鼠标事件 button.on('pointerdown', callback); button.on('pointerover', () => { button.setFillStyle(0x555555); }); button.on('pointerout', () => { button.setFillStyle(0x333333); }); // 保存元素引用,便于后续清理 this.optionsElements.push(button); this.optionsElements.push(buttonText); } /** * 清除物品操作选项 */ clearItemOptions() { if (this.optionsElements) { this.optionsElements.forEach(element => { element.destroy(); }); } this.optionsElements = []; } /** * 设置UI可见性 * @param {boolean} visible - 是否可见 */ setVisible(visible) { this.visible = visible; // 显示/隐藏所有UI元素 if (this.background) { this.background.visible = visible; } if (this.title) { this.title.visible = visible; } if (this.closeButton) { this.closeButton.visible = visible; } if (this.closeText) { this.closeText.visible = visible; } // 显示/隐藏列表项 if (this.itemListElements) { this.itemListElements.forEach(element => { element.visible = visible; }); } // 显示/隐藏操作选项 this.clearItemOptions(); } /** * 打开物品列表 */ show() { this.setVisible(true); // 更新物品列表 if (this.scene.itemManager && this.scene.player) { this.updateItems(this.scene.player, this.scene.itemManager.getActiveItems()); } } /** * 隐藏物品列表 */ hide() { this.setVisible(false); this.selectedItemId = null; // 清除选中状态 } /** * 切换物品列表的显示状态 */ toggle() { if (this.visible) { this.hide(); } else { this.show(); } } /** * 更新UI位置 * @param {number} x - 新的x坐标 * @param {number} y - 新的y坐标 */ setPosition(x, y) { this.x = x; this.y = y; // 更新所有UI元素的位置 if (this.background) { this.background.setPosition(x, y); } if (this.title) { this.title.setPosition(x + 10, y + 5); } if (this.closeButton) { this.closeButton.setPosition(x + this.width - 25, y + 10); } if (this.closeText) { this.closeText.setPosition(x + this.width - 25, y + 10); } // 更新列表区域位置 this.listX = x; this.listY = y + 30; // 刷新列表以更新位置 if (this.visible) { this.refreshList(); } } /** * 更新物品列表,应该在每一帧调用 */ update() { if (this.visible && this.scene.itemManager && this.scene.player) { const player = this.scene.player; const allItems = this.scene.itemManager.getActiveItems(); // 检查玩家位置是否变化 const playerPosChanged = this.lastPlayerX !== player.x || this.lastPlayerY !== player.y; if (playerPosChanged) { // 只有在玩家位置变化时才过滤物品并更新,避免不必要的计算 this.updateItems(player, allItems); // 保存当前玩家位置 this.lastPlayerX = player.x; this.lastPlayerY = player.y; } else { // 玩家位置未变化,检查物品列表是否变化 // 先过滤出可交互范围内的物品 const filteredItems = []; allItems.forEach(item => { if (item.isInInteractionRange(player) && !item.inInventory) { filteredItems.push(item); } }); // 检查过滤后的物品列表是否变化 const itemsChanged = this.isItemsChanged(filteredItems); if (itemsChanged) { this.updateItems(player, allItems); } } } } /** * 销毁UI */ destroy() { // 销毁所有UI元素 if (this.background) this.background.destroy(); if (this.title) this.title.destroy(); if (this.closeButton) this.closeButton.destroy(); if (this.closeText) this.closeText.destroy(); // 销毁所有列表项 if (this.itemListElements) { this.itemListElements.forEach(element => { element.destroy(); }); } // 销毁所有操作选项 this.clearItemOptions(); } }