mirror of
http://junepve.top:3000/chicrobot/CINC.git
synced 2026-10-30 04:31:29 +08:00
feat(ItemList) 添加周围物品列表功能
This commit is contained in:
1 parent
215e04384c
commit
0ed8c2f6ae
7 files changed
+1349
-14
No files matched your search
+25
-5
@@ -65,7 +65,12 @@ export default class Item extends Phaser.GameObjects.Sprite {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// 绑定点击事件
|
// 绑定点击事件
|
||||||
this.on('pointerdown', () => {
|
this.on('pointerdown', (pointer) => {
|
||||||
|
// 检查鼠标是否在UI元素上,如果是则不处理
|
||||||
|
if (this.scene.inputSystem && this.scene.inputSystem.isMouseOnUI && this.scene.inputSystem.isMouseOnUI(pointer)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// 添加视觉反馈:缩放物品
|
// 添加视觉反馈:缩放物品
|
||||||
this.scene.tweens.add({
|
this.scene.tweens.add({
|
||||||
targets: this,
|
targets: this,
|
||||||
@@ -101,6 +106,12 @@ export default class Item extends Phaser.GameObjects.Sprite {
|
|||||||
* @param {Object} user - 拾取物品的单位
|
* @param {Object} user - 拾取物品的单位
|
||||||
*/
|
*/
|
||||||
pickup(user) {
|
pickup(user) {
|
||||||
|
// 检查是否在交互范围内
|
||||||
|
if (!this.isInInteractionRange(user)) {
|
||||||
|
console.log('距离太远,无法拾取物品');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
console.log(`${user.name} 拾取了 ${this.name}`);
|
console.log(`${user.name} 拾取了 ${this.name}`);
|
||||||
|
|
||||||
// 检查用户是否有背包
|
// 检查用户是否有背包
|
||||||
@@ -109,13 +120,22 @@ export default class Item extends Phaser.GameObjects.Sprite {
|
|||||||
const success = user.inventory.addItem(this);
|
const success = user.inventory.addItem(this);
|
||||||
if (success) {
|
if (success) {
|
||||||
this.inInventory = true;
|
this.inInventory = true;
|
||||||
this.visible = false; // 在场景中隐藏物品
|
|
||||||
this.scene.events.emit('item-picked-up', this, user);
|
// 保存场景引用,避免在destroy后访问undefined
|
||||||
|
const scene = this.scene;
|
||||||
|
|
||||||
// 如果场景中有背包UI,更新UI
|
// 如果场景中有背包UI,更新UI
|
||||||
if (this.scene.inventoryUI) {
|
if (scene && scene.inventoryUI) {
|
||||||
this.scene.inventoryUI.updateUI();
|
scene.inventoryUI.updateUI();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 发射物品拾取事件
|
||||||
|
if (scene && scene.events) {
|
||||||
|
scene.events.emit('item-picked-up', this, user);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 成功拾取后从地图上删除物品,避免潜在bug
|
||||||
|
this.destroy();
|
||||||
} else {
|
} else {
|
||||||
console.log('背包空间不足');
|
console.log('背包空间不足');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ import MovementTestScene6 from './scenes/MovementTestScene6.js';
|
|||||||
import ItemTestScene from './scenes/ItemTestScene.js';
|
import ItemTestScene from './scenes/ItemTestScene.js';
|
||||||
// 导入背包系统测试场景
|
// 导入背包系统测试场景
|
||||||
import InventoryTestScene from './scenes/InventoryTestScene.js';
|
import InventoryTestScene from './scenes/InventoryTestScene.js';
|
||||||
|
// 导入物品列表菜单测试场景
|
||||||
|
import ItemListTestScene from './scenes/ItemListTestScene.js';
|
||||||
|
|
||||||
// 创建游戏的最终配置对象
|
// 创建游戏的最终配置对象
|
||||||
const config = {
|
const config = {
|
||||||
@@ -33,7 +35,8 @@ const config = {
|
|||||||
MovementTestScene5,
|
MovementTestScene5,
|
||||||
MovementTestScene6,
|
MovementTestScene6,
|
||||||
ItemTestScene,
|
ItemTestScene,
|
||||||
InventoryTestScene
|
InventoryTestScene,
|
||||||
|
ItemListTestScene
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+7
-6
@@ -19,14 +19,15 @@ export default class BootScene extends Phaser.Scene {
|
|||||||
|
|
||||||
// 测试场景列表
|
// 测试场景列表
|
||||||
const testScenes = [
|
const testScenes = [
|
||||||
|
{ key: 'ItemListTestScene', name: '测试场景9:物品列表菜单测试' },
|
||||||
{ key: 'InventoryTestScene', name: '测试场景8:背包系统测试' },
|
{ key: 'InventoryTestScene', name: '测试场景8:背包系统测试' },
|
||||||
{ key: 'MovementTestScene6', name: '测试场景6:小地图测试' },
|
|
||||||
{ key: 'MovementTestScene1', name: '测试场景1:相对移动(键盘控制)' },
|
|
||||||
{ key: 'MovementTestScene2', name: '测试场景2:目标点移动' },
|
|
||||||
{ key: 'MovementTestScene3', name: '测试场景3:单位选择与移动命令' },
|
|
||||||
{ key: 'MovementTestScene4', name: '测试场景4:虚拟摇杆控制' },
|
|
||||||
{ key: 'MovementTestScene5', name: '测试场景5:移动更新逻辑' },
|
|
||||||
{ key: 'ItemTestScene', name: '测试场景7:物品系统测试' },
|
{ key: 'ItemTestScene', name: '测试场景7:物品系统测试' },
|
||||||
|
{ key: 'MovementTestScene6', name: '测试场景6:小地图测试' },
|
||||||
|
{ key: 'MovementTestScene5', name: '测试场景5:移动更新逻辑' },
|
||||||
|
{ key: 'MovementTestScene4', name: '测试场景4:虚拟摇杆控制' },
|
||||||
|
{ key: 'MovementTestScene3', name: '测试场景3:单位选择与移动命令' },
|
||||||
|
{ key: 'MovementTestScene2', name: '测试场景2:目标点移动' },
|
||||||
|
{ key: 'MovementTestScene1', name: '测试场景1:相对移动(键盘控制)' },
|
||||||
{ key: 'GameScene', name: '返回主游戏场景' }
|
{ key: 'GameScene', name: '返回主游戏场景' }
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,673 @@
|
|||||||
|
// 物品列表菜单测试场景
|
||||||
|
|
||||||
|
import Unit from '../objects/Unit.js';
|
||||||
|
import { InputSystem } from '../systems/InputSystem.js';
|
||||||
|
import { SelectionSystem } from '../systems/SelectionSystem.js';
|
||||||
|
import { CommandSystem } from '../systems/CommandSystem.js';
|
||||||
|
import { ItemManager } from '../items/ItemManager.js';
|
||||||
|
import { Inventory } from '../items/Inventory.js';
|
||||||
|
import { InventoryUI } from '../ui/InventoryUI.js';
|
||||||
|
import { ItemListUI } from '../ui/ItemListUI.js';
|
||||||
|
|
||||||
|
export default class ItemListTestScene extends Phaser.Scene {
|
||||||
|
constructor() {
|
||||||
|
super({ key: 'ItemListTestScene' });
|
||||||
|
}
|
||||||
|
|
||||||
|
preload() {
|
||||||
|
// 加载物品数据
|
||||||
|
this.load.json('items', './data/items.json');
|
||||||
|
}
|
||||||
|
|
||||||
|
create() {
|
||||||
|
// 物理世界和相机边界
|
||||||
|
const worldSize = 1000;
|
||||||
|
this.physics.world.setBounds(-worldSize, -worldSize, worldSize * 2, worldSize * 2);
|
||||||
|
this.cameras.main.setBounds(-worldSize, -worldSize, worldSize * 2, worldSize * 2);
|
||||||
|
|
||||||
|
// 初始化系统
|
||||||
|
this.inputSystem = new InputSystem(this, {
|
||||||
|
keyboard: true,
|
||||||
|
mouse: true,
|
||||||
|
joystick: false,
|
||||||
|
selection: true
|
||||||
|
});
|
||||||
|
this.selectionSystem = new SelectionSystem(this);
|
||||||
|
this.commandSystem = new CommandSystem(this);
|
||||||
|
|
||||||
|
// 创建单位组
|
||||||
|
this.unitGroup = this.add.group();
|
||||||
|
|
||||||
|
// 创建背景
|
||||||
|
this.createBackground();
|
||||||
|
|
||||||
|
// 创建玩家
|
||||||
|
this.createPlayer();
|
||||||
|
|
||||||
|
// 初始化物品管理器
|
||||||
|
this.itemManager = new ItemManager(this);
|
||||||
|
|
||||||
|
// 初始化背包系统
|
||||||
|
this.initInventory();
|
||||||
|
|
||||||
|
// 创建物品列表UI
|
||||||
|
this.initItemListUI();
|
||||||
|
|
||||||
|
// 创建测试物品
|
||||||
|
this.createTestItems();
|
||||||
|
|
||||||
|
// 设置输入处理
|
||||||
|
this.setupInput();
|
||||||
|
|
||||||
|
// 创建侧边栏UI
|
||||||
|
this.createSidebar();
|
||||||
|
|
||||||
|
// 显示提示信息
|
||||||
|
this.showInstructions();
|
||||||
|
|
||||||
|
// 显示单位状态
|
||||||
|
this.updateUnitStatus(this.player);
|
||||||
|
|
||||||
|
// 显示默认提示
|
||||||
|
this.itemInfoText.setText('未选择任何物品');
|
||||||
|
this.interactionText.setText('按c键打开物品列表菜单');
|
||||||
|
this.generalInfoText.setText('欢迎使用物品列表菜单测试场景');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建背景
|
||||||
|
*/
|
||||||
|
createBackground() {
|
||||||
|
// 创建纯色背景
|
||||||
|
this.add.rectangle(0, 0, this.sys.game.config.width, this.sys.game.config.height, 0x1a1a2e);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建玩家
|
||||||
|
*/
|
||||||
|
createPlayer() {
|
||||||
|
// 为玩家创建纯色纹理
|
||||||
|
if (!this.textures.exists('player')) {
|
||||||
|
const canvas = document.createElement('canvas');
|
||||||
|
canvas.width = 32;
|
||||||
|
canvas.height = 32;
|
||||||
|
const ctx = canvas.getContext('2d');
|
||||||
|
ctx.fillStyle = '#4a90e2';
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(16, 16, 16, 0, Math.PI * 2);
|
||||||
|
ctx.fill();
|
||||||
|
this.textures.addCanvas('player', canvas);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建玩家单位
|
||||||
|
this.player = new Unit(this, 0, 0, 'player');
|
||||||
|
|
||||||
|
// 设置玩家属性
|
||||||
|
this.player.name = '测试玩家';
|
||||||
|
this.player.health = 100;
|
||||||
|
this.player.nutrition = 100;
|
||||||
|
this.player.hydration = 100;
|
||||||
|
this.player.equipWeapon = function(weapon) {
|
||||||
|
this.weapon = weapon;
|
||||||
|
console.log(`${this.name} 装备了 ${weapon.name}`);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 添加到单位组
|
||||||
|
this.unitGroup.add(this.player);
|
||||||
|
|
||||||
|
// 自动选中玩家
|
||||||
|
this.events.emit('select-unit', this.player);
|
||||||
|
|
||||||
|
// 添加碰撞检测
|
||||||
|
this.physics.add.collider(this.unitGroup, this.unitGroup);
|
||||||
|
|
||||||
|
// 保持相机中心在世界原点,与背包测试场景一致
|
||||||
|
this.cameras.main.centerOn(0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化背包系统
|
||||||
|
*/
|
||||||
|
initInventory() {
|
||||||
|
// 创建背包(6x4网格)
|
||||||
|
this.player.inventory = new Inventory(6, 4);
|
||||||
|
|
||||||
|
// 创建背包UI(会自动固定在右侧)
|
||||||
|
this.inventoryUI = new InventoryUI(this, this.player.inventory, 40);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化物品列表UI
|
||||||
|
*/
|
||||||
|
initItemListUI() {
|
||||||
|
// 创建物品列表UI,位于屏幕左下角
|
||||||
|
const x = 10;
|
||||||
|
const y = this.sys.game.config.height - 350;
|
||||||
|
this.itemListUI = new ItemListUI(this, x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建测试物品
|
||||||
|
*/
|
||||||
|
createTestItems() {
|
||||||
|
// 等待物品数据加载完成
|
||||||
|
this.itemManager.onDataLoaded(() => {
|
||||||
|
// 创建初始物品
|
||||||
|
this.createInitialItems();
|
||||||
|
|
||||||
|
// 绑定物品点击事件
|
||||||
|
this.events.on('item-clicked', (item) => {
|
||||||
|
this.showItemInfo(item);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建初始物品
|
||||||
|
*/
|
||||||
|
createInitialItems() {
|
||||||
|
// 在不同位置创建各种类型的物品,确保有些在交互范围内,有些在范围外
|
||||||
|
const itemTypes = ['food', 'tool', 'medical', 'weapon'];
|
||||||
|
|
||||||
|
// 在交互范围内创建物品(距离玩家50像素内)
|
||||||
|
for (let i = 0; i < itemTypes.length; i++) {
|
||||||
|
const x = this.player.x + (i - 2) * 40;
|
||||||
|
const y = this.player.y - 40;
|
||||||
|
this.itemManager.createItemByType(itemTypes[i], x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 在交互范围外创建物品(距离玩家100像素外)
|
||||||
|
for (let i = 0; i < itemTypes.length; i++) {
|
||||||
|
const x = this.player.x + (i - 2) * 120;
|
||||||
|
const y = this.player.y - 120;
|
||||||
|
this.itemManager.createItemByType(itemTypes[i], x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 在场景中随机位置创建更多物品
|
||||||
|
for (let i = 0; i < 8; i++) {
|
||||||
|
const itemIds = ['water_bottle', 'canned_food', 'crowbar', 'first_aid_kit', 'flashlight', 'knife'];
|
||||||
|
const randomItemId = itemIds[Math.floor(Math.random() * itemIds.length)];
|
||||||
|
const x = this.player.x + Phaser.Math.Between(-300, 300);
|
||||||
|
const y = this.player.y + Phaser.Math.Between(-300, 300);
|
||||||
|
|
||||||
|
this.itemManager.createItem(randomItemId, x, y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置输入处理
|
||||||
|
*/
|
||||||
|
setupInput() {
|
||||||
|
// 键盘输入
|
||||||
|
this.cursors = this.input.keyboard.createCursorKeys();
|
||||||
|
|
||||||
|
// 背包快捷键(B键)
|
||||||
|
this.input.keyboard.on('keydown-B', () => {
|
||||||
|
this.inventoryUI.toggle();
|
||||||
|
});
|
||||||
|
|
||||||
|
// 物品列表菜单快捷键(C键)
|
||||||
|
this.input.keyboard.on('keydown-C', () => {
|
||||||
|
this.itemListUI.toggle();
|
||||||
|
});
|
||||||
|
|
||||||
|
// 注册键盘事件
|
||||||
|
this.input.keyboard.on('keydown-ONE', () => {
|
||||||
|
this.itemManager.createRandomItem();
|
||||||
|
});
|
||||||
|
|
||||||
|
this.input.keyboard.on('keydown-TWO', () => {
|
||||||
|
// 在单位附近创建食物类物品
|
||||||
|
const x = this.player.x + Phaser.Math.Between(-100, 100);
|
||||||
|
const y = this.player.y + Phaser.Math.Between(-100, 100);
|
||||||
|
this.itemManager.createItemByType('food', x, y);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.input.keyboard.on('keydown-THREE', () => {
|
||||||
|
// 在单位附近创建工具类物品
|
||||||
|
const x = this.player.x + Phaser.Math.Between(-100, 100);
|
||||||
|
const y = this.player.y + Phaser.Math.Between(-100, 100);
|
||||||
|
this.itemManager.createItemByType('tool', x, y);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.input.keyboard.on('keydown-FOUR', () => {
|
||||||
|
this.itemManager.clearAllItems();
|
||||||
|
this.itemInfoText.setText('所有物品已清理');
|
||||||
|
// 更新物品列表UI
|
||||||
|
if (this.itemListUI.visible) {
|
||||||
|
this.itemListUI.updateItems(this.player, this.itemManager.getActiveItems());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.input.keyboard.on('keydown-SPACE', () => {
|
||||||
|
this.scene.start('BootScene');
|
||||||
|
});
|
||||||
|
|
||||||
|
// 创建交互按钮
|
||||||
|
this.createButtons();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建交互按钮
|
||||||
|
*/
|
||||||
|
createButtons() {
|
||||||
|
const buttonWidth = 200;
|
||||||
|
const buttonHeight = 40;
|
||||||
|
const buttonY = this.sys.game.config.height - 100;
|
||||||
|
|
||||||
|
// 创建随机物品按钮
|
||||||
|
this.createButton(this.sys.game.config.width - buttonWidth - 10, buttonY, '创建随机物品', () => {
|
||||||
|
this.itemManager.createRandomItem();
|
||||||
|
});
|
||||||
|
|
||||||
|
// 创建清理物品按钮
|
||||||
|
this.createButton(this.sys.game.config.width - buttonWidth - 10, buttonY + 50, '清理所有物品', () => {
|
||||||
|
this.itemManager.clearAllItems();
|
||||||
|
this.itemInfoText.setText('所有物品已清理');
|
||||||
|
// 更新物品列表UI
|
||||||
|
if (this.itemListUI.visible) {
|
||||||
|
this.itemListUI.updateItems(this.player, this.itemManager.getActiveItems());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建按钮
|
||||||
|
* @param {number} x - 按钮x坐标
|
||||||
|
* @param {number} y - 按钮y坐标
|
||||||
|
* @param {string} text - 按钮文本
|
||||||
|
* @param {function} callback - 点击回调函数
|
||||||
|
*/
|
||||||
|
createButton(x, y, text, callback) {
|
||||||
|
// 创建按钮背景
|
||||||
|
const button = this.add.rectangle(x, y, 200, 40, 0x333333);
|
||||||
|
button.setOrigin(0.5, 0.5);
|
||||||
|
button.setScrollFactor(0, 0); // 固定在屏幕上,不随相机移动
|
||||||
|
button.setDepth(15); // 设置深度,确保显示在场景说明文本上方
|
||||||
|
|
||||||
|
// 创建按钮文本
|
||||||
|
const buttonText = this.add.text(x, y, text, {
|
||||||
|
fontSize: '16px',
|
||||||
|
fill: '#ffffff',
|
||||||
|
fontFamily: 'Arial'
|
||||||
|
});
|
||||||
|
buttonText.setOrigin(0.5, 0.5);
|
||||||
|
buttonText.setScrollFactor(0, 0); // 固定在屏幕上,不随相机移动
|
||||||
|
buttonText.setDepth(16); // 设置深度,确保显示在按钮背景上方
|
||||||
|
|
||||||
|
// 设置按钮交互
|
||||||
|
button.setInteractive({
|
||||||
|
useHandCursor: true
|
||||||
|
});
|
||||||
|
|
||||||
|
// 按钮事件
|
||||||
|
button.on('pointerdown', callback);
|
||||||
|
|
||||||
|
button.on('pointerover', () => {
|
||||||
|
button.setFillStyle(0x555555);
|
||||||
|
});
|
||||||
|
|
||||||
|
button.on('pointerout', () => {
|
||||||
|
button.setFillStyle(0x333333);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 显示物品信息
|
||||||
|
* @param {Item} item - 物品实例
|
||||||
|
*/
|
||||||
|
showItemInfo(item) {
|
||||||
|
const unit = this.player;
|
||||||
|
const isInRange = item.isInInteractionRange(unit);
|
||||||
|
|
||||||
|
const info = item.getInfo();
|
||||||
|
let infoText = `${info.name}\n`;
|
||||||
|
infoText += `${info.description}\n`;
|
||||||
|
infoText += `类型: ${info.type}\n`;
|
||||||
|
infoText += `重量: ${info.weight}kg\n`;
|
||||||
|
infoText += `体积: ${info.volume}L\n`;
|
||||||
|
infoText += `价值: ${info.value}金币\n`;
|
||||||
|
infoText += `尺寸: ${info.gridSize.width}x${info.gridSize.height}\n`;
|
||||||
|
infoText += `距离: ${Math.round(Math.sqrt(Math.pow(item.x - unit.x, 2) + Math.pow(item.y - unit.y, 2)))}m\n`;
|
||||||
|
|
||||||
|
// 显示物品属性
|
||||||
|
infoText += '属性: ';
|
||||||
|
const properties = [];
|
||||||
|
for (const [key, value] of Object.entries(info.properties)) {
|
||||||
|
properties.push(`${key}: ${value}`);
|
||||||
|
}
|
||||||
|
infoText += properties.join(', ');
|
||||||
|
|
||||||
|
this.itemInfoText.setText(infoText);
|
||||||
|
|
||||||
|
// 显示交互提示
|
||||||
|
if (isInRange) {
|
||||||
|
this.interactionText.setText('按E键拾取物品,按Q键快捷使用');
|
||||||
|
// 注册E键拾取物品
|
||||||
|
this.input.keyboard.once('keydown-E', () => {
|
||||||
|
item.pickup(unit);
|
||||||
|
// 物品拾取后,更新物品列表UI
|
||||||
|
if (this.itemListUI.visible) {
|
||||||
|
this.itemListUI.updateItems(this.player, this.itemManager.getActiveItems());
|
||||||
|
}
|
||||||
|
this.interactionText.setText('');
|
||||||
|
this.itemInfoText.setText('未选择任何物品');
|
||||||
|
});
|
||||||
|
// 注册Q键快捷使用物品
|
||||||
|
this.input.keyboard.once('keydown-Q', () => {
|
||||||
|
item.use(unit);
|
||||||
|
// 更新物品列表UI
|
||||||
|
if (this.itemListUI.visible) {
|
||||||
|
this.itemListUI.updateItems(this.player, this.itemManager.getActiveItems());
|
||||||
|
}
|
||||||
|
this.interactionText.setText('');
|
||||||
|
this.itemInfoText.setText('未选择任何物品');
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.interactionText.setText('距离太远,无法交互');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 显示提示信息
|
||||||
|
*/
|
||||||
|
showInstructions() {
|
||||||
|
const titleText = this.add.text(10, 10, '物品列表菜单测试场景', {
|
||||||
|
fontSize: '20px',
|
||||||
|
fill: '#ffffff'
|
||||||
|
});
|
||||||
|
titleText.setScrollFactor(0, 0); // 固定在屏幕上,不随相机移动
|
||||||
|
titleText.setDepth(10); // 设置深度,确保显示在侧边栏上方
|
||||||
|
|
||||||
|
const moveText = this.add.text(10, 40, 'WASD或方向键移动单位,左键点击选择单位', {
|
||||||
|
fontSize: '16px',
|
||||||
|
fill: '#ffffff'
|
||||||
|
});
|
||||||
|
moveText.setScrollFactor(0, 0); // 固定在屏幕上,不随相机移动
|
||||||
|
moveText.setDepth(10); // 设置深度,确保显示在侧边栏上方
|
||||||
|
|
||||||
|
const itemText = this.add.text(10, 70, '点击物品查看详细信息,交互范围50像素', {
|
||||||
|
fontSize: '16px',
|
||||||
|
fill: '#ffffff'
|
||||||
|
});
|
||||||
|
itemText.setScrollFactor(0, 0); // 固定在屏幕上,不随相机移动
|
||||||
|
itemText.setDepth(10); // 设置深度,确保显示在侧边栏上方
|
||||||
|
|
||||||
|
const key1Text = this.add.text(10, 100, '按1键:创建随机物品', {
|
||||||
|
fontSize: '16px',
|
||||||
|
fill: '#ffffff'
|
||||||
|
});
|
||||||
|
key1Text.setScrollFactor(0, 0); // 固定在屏幕上,不随相机移动
|
||||||
|
key1Text.setDepth(10); // 设置深度,确保显示在侧边栏上方
|
||||||
|
|
||||||
|
const key2Text = this.add.text(10, 130, '按2键:创建食物类物品', {
|
||||||
|
fontSize: '16px',
|
||||||
|
fill: '#ffffff'
|
||||||
|
});
|
||||||
|
key2Text.setScrollFactor(0, 0); // 固定在屏幕上,不随相机移动
|
||||||
|
key2Text.setDepth(10); // 设置深度,确保显示在侧边栏上方
|
||||||
|
|
||||||
|
const key3Text = this.add.text(10, 160, '按3键:创建工具类物品', {
|
||||||
|
fontSize: '16px',
|
||||||
|
fill: '#ffffff'
|
||||||
|
});
|
||||||
|
key3Text.setScrollFactor(0, 0); // 固定在屏幕上,不随相机移动
|
||||||
|
key3Text.setDepth(10); // 设置深度,确保显示在侧边栏上方
|
||||||
|
|
||||||
|
const key4Text = this.add.text(10, 190, '按4键:清理所有物品', {
|
||||||
|
fontSize: '16px',
|
||||||
|
fill: '#ffffff'
|
||||||
|
});
|
||||||
|
key4Text.setScrollFactor(0, 0); // 固定在屏幕上,不随相机移动
|
||||||
|
key4Text.setDepth(10); // 设置深度,确保显示在侧边栏上方
|
||||||
|
|
||||||
|
const keyBText = this.add.text(10, 220, '按B键:打开/关闭背包', {
|
||||||
|
fontSize: '16px',
|
||||||
|
fill: '#ffffff'
|
||||||
|
});
|
||||||
|
keyBText.setScrollFactor(0, 0); // 固定在屏幕上,不随相机移动
|
||||||
|
keyBText.setDepth(10); // 设置深度,确保显示在侧边栏上方
|
||||||
|
|
||||||
|
const keyCText = this.add.text(10, 250, '按C键:打开/关闭物品列表菜单', {
|
||||||
|
fontSize: '16px',
|
||||||
|
fill: '#ffff00'
|
||||||
|
});
|
||||||
|
keyCText.setScrollFactor(0, 0); // 固定在屏幕上,不随相机移动
|
||||||
|
keyCText.setDepth(10); // 设置深度,确保显示在侧边栏上方
|
||||||
|
|
||||||
|
const spaceText = this.add.text(10, 280, '按空格键返回启动场景', {
|
||||||
|
fontSize: '16px',
|
||||||
|
fill: '#ffffff'
|
||||||
|
});
|
||||||
|
spaceText.setScrollFactor(0, 0); // 固定在屏幕上,不随相机移动
|
||||||
|
spaceText.setDepth(10); // 设置深度,确保显示在侧边栏上方
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建侧边栏UI
|
||||||
|
*/
|
||||||
|
createSidebar() {
|
||||||
|
// 侧边栏宽度
|
||||||
|
const sidebarWidth = 300;
|
||||||
|
const sidebarDepth = 5; // 侧边栏深度值,确保显示在游戏元素上方,但低于场景说明文本
|
||||||
|
|
||||||
|
// 创建侧边栏背景
|
||||||
|
this.sidebar = this.add.rectangle(
|
||||||
|
this.sys.game.config.width - sidebarWidth,
|
||||||
|
0,
|
||||||
|
sidebarWidth,
|
||||||
|
this.sys.game.config.height,
|
||||||
|
0x111111,
|
||||||
|
0.9
|
||||||
|
);
|
||||||
|
this.sidebar.setOrigin(0, 0);
|
||||||
|
this.sidebar.setScrollFactor(0, 0); // 固定在屏幕上,不随相机移动
|
||||||
|
this.sidebar.setDepth(sidebarDepth); // 设置深度,确保显示在游戏元素上方
|
||||||
|
|
||||||
|
// 添加侧边栏边框
|
||||||
|
this.sidebarBorder = this.add.rectangle(
|
||||||
|
this.sys.game.config.width - sidebarWidth,
|
||||||
|
0,
|
||||||
|
sidebarWidth,
|
||||||
|
this.sys.game.config.height,
|
||||||
|
0x444444,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
this.sidebarBorder.setOrigin(0, 0);
|
||||||
|
this.sidebarBorder.setStrokeStyle(2, 0x444444, 1);
|
||||||
|
this.sidebarBorder.setScrollFactor(0, 0); // 固定在屏幕上,不随相机移动
|
||||||
|
this.sidebarBorder.setDepth(sidebarDepth + 1); // 设置深度,确保显示在侧边栏背景上方
|
||||||
|
|
||||||
|
// 侧边栏标题
|
||||||
|
const title = this.add.text(
|
||||||
|
this.sys.game.config.width - sidebarWidth + 20,
|
||||||
|
20,
|
||||||
|
'游戏信息面板',
|
||||||
|
{
|
||||||
|
fontSize: '20px',
|
||||||
|
fill: '#ffffff',
|
||||||
|
fontWeight: 'bold'
|
||||||
|
}
|
||||||
|
);
|
||||||
|
title.setScrollFactor(0, 0); // 固定在屏幕上,不随相机移动
|
||||||
|
title.setDepth(sidebarDepth + 2); // 设置深度,确保显示在侧边栏边框上方
|
||||||
|
|
||||||
|
// 通用信息区域标题
|
||||||
|
const generalTitle = this.add.text(
|
||||||
|
this.sys.game.config.width - sidebarWidth + 20,
|
||||||
|
50,
|
||||||
|
'通用信息:',
|
||||||
|
{
|
||||||
|
fontSize: '16px',
|
||||||
|
fill: '#4CAF50',
|
||||||
|
fontWeight: 'bold'
|
||||||
|
}
|
||||||
|
);
|
||||||
|
generalTitle.setScrollFactor(0, 0); // 固定在屏幕上,不随相机移动
|
||||||
|
generalTitle.setDepth(sidebarDepth + 2); // 设置深度,确保显示在侧边栏边框上方
|
||||||
|
|
||||||
|
this.generalInfoText = this.add.text(
|
||||||
|
this.sys.game.config.width - sidebarWidth + 20,
|
||||||
|
75,
|
||||||
|
'',
|
||||||
|
{
|
||||||
|
fontSize: '14px',
|
||||||
|
fill: '#ffffff',
|
||||||
|
wordWrap: {
|
||||||
|
width: sidebarWidth - 40
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
this.generalInfoText.setScrollFactor(0, 0); // 固定在屏幕上,不随相机移动
|
||||||
|
this.generalInfoText.setDepth(sidebarDepth + 2); // 设置深度,确保显示在侧边栏边框上方
|
||||||
|
|
||||||
|
// 角色属性区域标题
|
||||||
|
const characterTitle = this.add.text(
|
||||||
|
this.sys.game.config.width - sidebarWidth + 20,
|
||||||
|
120,
|
||||||
|
'角色属性:',
|
||||||
|
{
|
||||||
|
fontSize: '16px',
|
||||||
|
fill: '#2196F3',
|
||||||
|
fontWeight: 'bold'
|
||||||
|
}
|
||||||
|
);
|
||||||
|
characterTitle.setScrollFactor(0, 0); // 固定在屏幕上,不随相机移动
|
||||||
|
characterTitle.setDepth(sidebarDepth + 2); // 设置深度,确保显示在侧边栏边框上方
|
||||||
|
|
||||||
|
this.unitStatusText = this.add.text(
|
||||||
|
this.sys.game.config.width - sidebarWidth + 20,
|
||||||
|
145,
|
||||||
|
'',
|
||||||
|
{
|
||||||
|
fontSize: '14px',
|
||||||
|
fill: '#ffffff',
|
||||||
|
wordWrap: {
|
||||||
|
width: sidebarWidth - 40
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
this.unitStatusText.setScrollFactor(0, 0); // 固定在屏幕上,不随相机移动
|
||||||
|
this.unitStatusText.setDepth(sidebarDepth + 2); // 设置深度,确保显示在侧边栏边框上方
|
||||||
|
|
||||||
|
// 交互提示区域标题
|
||||||
|
const interactionTitle = this.add.text(
|
||||||
|
this.sys.game.config.width - sidebarWidth + 20,
|
||||||
|
220,
|
||||||
|
'交互提示:',
|
||||||
|
{
|
||||||
|
fontSize: '16px',
|
||||||
|
fill: '#FFEB3B',
|
||||||
|
fontWeight: 'bold'
|
||||||
|
}
|
||||||
|
);
|
||||||
|
interactionTitle.setScrollFactor(0, 0); // 固定在屏幕上,不随相机移动
|
||||||
|
interactionTitle.setDepth(sidebarDepth + 2); // 设置深度,确保显示在侧边栏边框上方
|
||||||
|
|
||||||
|
this.interactionText = this.add.text(
|
||||||
|
this.sys.game.config.width - sidebarWidth + 20,
|
||||||
|
245,
|
||||||
|
'',
|
||||||
|
{
|
||||||
|
fontSize: '14px',
|
||||||
|
fill: '#ffff00',
|
||||||
|
wordWrap: {
|
||||||
|
width: sidebarWidth - 40
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
this.interactionText.setScrollFactor(0, 0); // 固定在屏幕上,不随相机移动
|
||||||
|
this.interactionText.setDepth(sidebarDepth + 2); // 设置深度,确保显示在侧边栏边框上方
|
||||||
|
|
||||||
|
// 物品详情区域标题
|
||||||
|
const itemTitle = this.add.text(
|
||||||
|
this.sys.game.config.width - sidebarWidth + 20,
|
||||||
|
350,
|
||||||
|
'物品详情:',
|
||||||
|
{
|
||||||
|
fontSize: '16px',
|
||||||
|
fill: '#FF9800',
|
||||||
|
fontWeight: 'bold'
|
||||||
|
}
|
||||||
|
);
|
||||||
|
itemTitle.setScrollFactor(0, 0); // 固定在屏幕上,不随相机移动
|
||||||
|
itemTitle.setDepth(sidebarDepth + 2); // 设置深度,确保显示在侧边栏边框上方
|
||||||
|
|
||||||
|
this.itemInfoText = this.add.text(
|
||||||
|
this.sys.game.config.width - sidebarWidth + 20,
|
||||||
|
375,
|
||||||
|
'',
|
||||||
|
{
|
||||||
|
fontSize: '14px',
|
||||||
|
fill: '#ffffff',
|
||||||
|
wordWrap: {
|
||||||
|
width: sidebarWidth - 40
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
this.itemInfoText.setScrollFactor(0, 0); // 固定在屏幕上,不随相机移动
|
||||||
|
this.itemInfoText.setDepth(sidebarDepth + 2); // 设置深度,确保显示在侧边栏边框上方
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新单位状态显示
|
||||||
|
* @param {Unit} unit - 单位实例
|
||||||
|
*/
|
||||||
|
updateUnitStatus(unit) {
|
||||||
|
let statusText = `${unit.name} 状态:\n`;
|
||||||
|
statusText += `生命值: ${unit.health}\n`;
|
||||||
|
statusText += `营养值: ${unit.nutrition}\n`;
|
||||||
|
statusText += `水分值: ${unit.hydration}\n`;
|
||||||
|
if (unit.weapon) {
|
||||||
|
statusText += `武器: ${unit.weapon.name}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.unitStatusText.setText(statusText);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 显示消息
|
||||||
|
* @param {string} message - 消息内容
|
||||||
|
*/
|
||||||
|
showMessage(message) {
|
||||||
|
// 创建消息
|
||||||
|
const messageText = this.add.text(
|
||||||
|
this.player.x,
|
||||||
|
this.player.y - 50,
|
||||||
|
message,
|
||||||
|
{
|
||||||
|
font: '20px Arial',
|
||||||
|
fill: '#ffffff',
|
||||||
|
backgroundColor: '#000000',
|
||||||
|
padding: { x: 10, y: 5 }
|
||||||
|
}
|
||||||
|
);
|
||||||
|
messageText.setOrigin(0.5, 0.5);
|
||||||
|
messageText.setDepth(20);
|
||||||
|
messageText.setScrollFactor(1); // 允许消息随相机移动
|
||||||
|
|
||||||
|
// 3秒后自动移除消息
|
||||||
|
this.time.delayedCall(3000, () => {
|
||||||
|
if (messageText) {
|
||||||
|
messageText.destroy();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
update() {
|
||||||
|
this.inputSystem.update();
|
||||||
|
|
||||||
|
// 更新物品列表UI(如果可见)
|
||||||
|
this.itemListUI.update();
|
||||||
|
|
||||||
|
// 更新所有单位
|
||||||
|
this.unitGroup.getChildren().forEach(unit => {
|
||||||
|
if (unit.update) {
|
||||||
|
unit.update();
|
||||||
|
}
|
||||||
|
unit.updateMovement();
|
||||||
|
// 更新单位状态显示
|
||||||
|
this.updateUnitStatus(unit);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -31,6 +31,19 @@ export default class ItemTestScene extends Phaser.Scene {
|
|||||||
// 创建单位组
|
// 创建单位组
|
||||||
this.unitGroup = this.add.group();
|
this.unitGroup = this.add.group();
|
||||||
|
|
||||||
|
// 创建玩家纹理
|
||||||
|
if (!this.textures.exists('player')) {
|
||||||
|
const canvas = document.createElement('canvas');
|
||||||
|
canvas.width = 32;
|
||||||
|
canvas.height = 32;
|
||||||
|
const ctx = canvas.getContext('2d');
|
||||||
|
ctx.fillStyle = '#4a90e2';
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(16, 16, 16, 0, Math.PI * 2);
|
||||||
|
ctx.fill();
|
||||||
|
this.textures.addCanvas('player', canvas);
|
||||||
|
}
|
||||||
|
|
||||||
// 创建一个测试单位
|
// 创建一个测试单位
|
||||||
const unit = new Unit(this, 0, 0, 'player');
|
const unit = new Unit(this, 0, 0, 'player');
|
||||||
// 为单位添加一些属性,用于测试物品效果
|
// 为单位添加一些属性,用于测试物品效果
|
||||||
|
|||||||
+36
-2
@@ -293,7 +293,41 @@ export class InputSystem {
|
|||||||
return this.isMouseOnMiniMap(pointer) ||
|
return this.isMouseOnMiniMap(pointer) ||
|
||||||
this.isMouseOnJoystick(pointer) ||
|
this.isMouseOnJoystick(pointer) ||
|
||||||
this.isMouseOnSidebar(pointer) ||
|
this.isMouseOnSidebar(pointer) ||
|
||||||
this.isMouseOnInventoryUI(pointer);
|
this.isMouseOnInventoryUI(pointer) ||
|
||||||
|
this.isMouseOnItemListUI(pointer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查鼠标是否在物品列表菜单上
|
||||||
|
* @param {Phaser.Input.Pointer} pointer - 指针事件对象
|
||||||
|
* @returns {boolean} - 如果鼠标在物品列表菜单上返回true,否则返回false
|
||||||
|
*/
|
||||||
|
isMouseOnItemListUI(pointer) {
|
||||||
|
// 检查场景中是否有物品列表UI
|
||||||
|
if (!this.scene.itemListUI) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取物品列表UI实例
|
||||||
|
const itemListUI = this.scene.itemListUI;
|
||||||
|
// 获取鼠标在屏幕上的坐标
|
||||||
|
const mouseX = pointer.x;
|
||||||
|
const mouseY = pointer.y;
|
||||||
|
|
||||||
|
// 检查UI是否可见
|
||||||
|
if (!itemListUI.visible) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 直接使用UI的位置和尺寸来计算边界,不依赖container
|
||||||
|
const uiX = itemListUI.x;
|
||||||
|
const uiY = itemListUI.y;
|
||||||
|
const uiWidth = itemListUI.width;
|
||||||
|
const uiHeight = itemListUI.height;
|
||||||
|
|
||||||
|
// 检查鼠标坐标是否在物品列表UI边界内
|
||||||
|
return mouseX >= uiX && mouseX <= uiX + uiWidth &&
|
||||||
|
mouseY >= uiY && mouseY <= uiY + uiHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -420,7 +454,7 @@ export class InputSystem {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const pickupDistance = 100; // 最大拾取距离
|
const pickupDistance = 50; // 最大拾取距离,与物品交互距离保持一致
|
||||||
let closestItem = null;
|
let closestItem = null;
|
||||||
let minDistance = pickupDistance;
|
let minDistance = pickupDistance;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,591 @@
|
|||||||
|
// 物品列表菜单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();
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user