feat(inventory) 添加背包物品移动、拾取到背包、丢弃背包物品功能

This commit is contained in:
chicrobot committed 2026-01-05 02:55:08 +08:00
1 parent ae26209a24
commit 35e10a2abc
6 files changed
+408 -115

No files matched your search

+187 -52
View File
@@ -36,13 +36,13 @@ export class InventoryUI {
}
/**
* 计算背包位置(相机右侧)
* 计算背包位置(屏幕左上角)
*/
calculatePosition() {
// 背包宽度
const inventoryWidth = this.inventory.width * this.cellSize + 20;
// 固定在屏幕右侧,有一定边距
this.x = this.scene.sys.game.config.width - inventoryWidth - 20;
// 固定在屏幕左上角,有一定边距
this.x = 20;
this.y = 20;
}
@@ -237,35 +237,34 @@ export class InventoryUI {
}
/**
* 物品精灵鼠标按下事件,用于启动拖动
* 物品精灵鼠标按下事件,用于记录点击状态和位置
* @param {Object} item - 物品
* @param {Phaser.Input.Pointer} pointer - 指针对象
*/
onItemSpriteDown(item, pointer) {
console.log('onItemSpriteDown:', item.name, '开始拖动');
this.isDragging = true;
this.draggedItem = item;
console.log('onItemSpriteDown:', item.name, '记录点击位置');
// 记录点击的物品和位置,用于判断是否开始拖动
this.clickItem = item;
this.clickPosition = { x: pointer.x, y: pointer.y };
this.clickTime = Date.now();
// 如果没有选中物品,先选中
if (!this.selectedItem || this.selectedItem.id !== item.id) {
// 切换选择状态:如果已选中,则取消选择;如果未选中,则选中
if (this.selectedItem && this.selectedItem.id === item.id) {
// 如果点击的是已选中物品,取消选择
console.log('点击已选中物品精灵,取消选择:', item.name);
this.deselectItem();
} else {
// 如果没有选中物品或点击的是另一个物品,选中当前物品
if (this.selectedItem) {
this.deselectItem();
}
this.selectItem(item);
}
// 获取物品精灵
const spriteObj = this.itemSprites.get(item.id);
if (spriteObj) {
this.draggedSprite = spriteObj.sprite;
// 计算鼠标偏移量
const spriteRect = this.draggedSprite.getBounds();
this.dragOffset.x = pointer.x - this.container.x - spriteRect.x;
this.dragOffset.y = pointer.y - this.container.y - spriteRect.y;
// 提升精灵深度,确保拖动时显示在最上层
this.draggedSprite.setDepth(10);
}
// 不立即开始拖动,只在鼠标移动超过阈值时才开始
this.isDragging = false;
this.draggedItem = null;
this.draggedSprite = null;
}
/**
@@ -286,13 +285,13 @@ export class InventoryUI {
}
}
// 如果正在拖动,更新精灵位置
// 如果正在拖动,更新精灵位置,使其中心锁定在鼠标位置
if (this.isDragging && this.draggedSprite) {
// 更新拖动精灵的位置
const localX = pointer.x - this.container.x - this.dragOffset.x;
const localY = pointer.y - this.container.y - this.dragOffset.y;
// 更新拖动精灵的位置,使其中心锁定在鼠标位置
const spriteX = pointer.x - this.container.x - this.spriteCenterOffset.x;
const spriteY = pointer.y - this.container.y - this.spriteCenterOffset.y;
this.draggedSprite.setPosition(localX, localY);
this.draggedSprite.setPosition(spriteX, spriteY);
// 检查鼠标是否悬停在某个单元格上
this.updateDragHover(pointer);
@@ -307,22 +306,94 @@ export class InventoryUI {
this.isDragging = true;
this.draggedItem = this.clickItem;
// 拖动时隐藏物品详情
this.hideItemInfo();
// 获取物品精灵
const spriteObj = this.itemSprites.get(this.draggedItem.id);
if (spriteObj) {
this.draggedSprite = spriteObj.sprite;
// 计算鼠标偏移量
// 获取精灵尺寸
const spriteRect = this.draggedSprite.getBounds();
this.dragOffset.x = this.clickPosition.x - this.container.x - spriteRect.x;
this.dragOffset.y = this.clickPosition.y - this.container.y - spriteRect.y;
// 计算精灵中心偏移(用于后续位置计算)
this.spriteCenterOffset = {
x: spriteRect.width / 2,
y: spriteRect.height / 2
};
// 提升精灵深度,确保拖动时显示在最上层
this.draggedSprite.setDepth(10);
// 创建预计放下位置的预览精灵
this.createDragPreview();
}
}
/**
* 更新拖动时的悬停状态
* 创建预计放下位置的预览
*/
createDragPreview() {
if (this.dragPreview) {
this.dragPreview.destroy();
this.dragPreview = null;
}
const { width, height } = this.draggedItem.gridSize;
const previewWidth = width * this.cellSize;
const previewHeight = height * this.cellSize;
// 创建预览矩形(半透明,用于显示预计放下位置)
this.dragPreview = this.scene.add.rectangle(0, 0, previewWidth, previewHeight, 0xffffff, 0.3);
this.dragPreview.setOrigin(0, 0);
this.dragPreview.setStrokeStyle(2, 0x00ff00, 1);
this.dragPreview.setScrollFactor(0); // 固定在屏幕上
this.dragPreview.setDepth(5); // 确保预览在背景之上,在拖动的精灵之下
this.container.add(this.dragPreview);
}
/**
* 更新预计放下位置的预览
* @param {number} centerX - 预计放下的中心X坐标
* @param {number} centerY - 预计放下的中心Y坐标
*/
updateDragPreview(centerX, centerY) {
if (!this.dragPreview || !this.draggedItem) return;
const { width, height } = this.draggedItem.gridSize;
// 计算预计放下位置的左上角坐标
const topLeftX = Math.floor(centerX - (width - 1) / 2);
const topLeftY = Math.floor(centerY - (height - 1) / 2);
// 更新预览位置
const previewX = 10 + topLeftX * this.cellSize;
const previewY = 10 + topLeftY * this.cellSize;
this.dragPreview.setPosition(previewX, previewY);
// 根据是否可以放置更新预览颜色
const canPlace = this.inventory.canPlaceItem(this.draggedItem, centerX, centerY);
if (canPlace) {
this.dragPreview.setFillStyle(0x00ff00, 0.3);
this.dragPreview.setStrokeStyle(2, 0x00ff00, 1);
} else {
this.dragPreview.setFillStyle(0xff0000, 0.3);
this.dragPreview.setStrokeStyle(2, 0xff0000, 1);
}
}
/**
* 销毁预计放下位置的预览
*/
destroyDragPreview() {
if (this.dragPreview) {
this.dragPreview.destroy();
this.dragPreview = null;
}
}
/**
* 更新拖动时的悬停状态和预览位置
* @param {Phaser.Input.Pointer} pointer - 指针对象
*/
updateDragHover(pointer) {
@@ -345,11 +416,24 @@ export class InventoryUI {
// 设置新的悬停单元格
this.onCellHovered(cellX, cellY);
}
// 更新预计放下位置的预览
this.updateDragPreview(cellX, cellY);
// 确保预览可见
if (this.dragPreview) {
this.dragPreview.setVisible(true);
}
} else {
// 鼠标移出背包范围,移除悬停效果
if (this.hoveredCell) {
this.onCellUnhovered();
}
// 隐藏预览
if (this.dragPreview) {
this.dragPreview.setVisible(false);
}
}
}
@@ -377,15 +461,52 @@ export class InventoryUI {
if (cellX >= 0 && cellX < this.inventory.width && cellY >= 0 && cellY < this.inventory.height) {
const targetItem = this.inventory.getItemAt(cellX, cellY);
if (targetItem) {
// 如果目标位置有物品,尝试交换
success = this.inventory.swapItems(this.draggedItem.id, targetItem.id);
// 检查新位置是否与旧位置完全相同
const isSamePosition = this.draggedItem.position.x === cellX && this.draggedItem.position.y === cellY;
// 如果目标位置是物品原来的位置且位置完全相同,直接取消拖动
if (targetItem && targetItem.id === this.draggedItem.id && isSamePosition) {
console.log('拖动到原起始位置,直接取消拖动');
success = true;
} else if (targetItem) {
if (targetItem.id === this.draggedItem.id) {
// 如果目标位置是当前拖动物品的非起始位置,尝试移动
console.log('拖动到当前物品的非起始位置,尝试移动:', this.draggedItem.name, '到位置:', { cellX, cellY });
success = this.inventory.moveItem(this.draggedItem.id, cellX, cellY);
console.log('移动结果:', success);
} else {
// 如果目标位置有其他物品,尝试交换
success = this.inventory.swapItems(this.draggedItem.id, targetItem.id);
}
} else {
// 如果目标位置是空的,尝试移动
success = this.inventory.moveItem(this.draggedItem.id, cellX, cellY);
}
} else {
// 鼠标释放位置不在背包范围内,执行丢弃操作
console.log('拖动出背包,执行丢弃操作:', this.draggedItem.name);
// 获取物品ID和基本信息
const itemId = this.draggedItem.itemTypeId || this.draggedItem.id;
// 从背包中移除物品
const removedItem = this.inventory.removeItem(this.draggedItem.id);
if (removedItem) {
// 在角色位置创建该物品
if (this.scene.itemManager) {
const player = this.scene.player;
this.scene.itemManager.createItem(itemId, player.x, player.y);
console.log('已在角色位置创建丢弃的物品:', removedItem.name);
}
success = true;
}
}
// 销毁预计放下位置的预览
this.destroyDragPreview();
// 重置拖动状态
this.isDragging = false;
if (this.draggedSprite) {
@@ -403,7 +524,7 @@ export class InventoryUI {
}
this.draggedItem = null;
this.dragOffset = { x: 0, y: 0 };
this.spriteCenterOffset = null;
// 移除悬停效果
if (this.hoveredCell) {
@@ -428,9 +549,17 @@ export class InventoryUI {
if (this.selectedItem) {
console.log('已有选中物品:', this.selectedItem.name, 'ID:', this.selectedItem.id);
// 如果点击的是已选中物品,直接取消选择(无论点击的是哪个位置)
if (item && item.id === this.selectedItem.id) {
console.log('点击已选中物品,取消选择:', item.name);
this.deselectItem();
return;
}
// 尝试移动或交换物品
let success = false;
// 如果有选中的物品,尝试移动它
if (item) {
// 如果点击的是另一个物品,尝试交换
console.log('点击另一个物品,尝试交换:', item.name, 'ID:', item.id);
@@ -471,11 +600,12 @@ export class InventoryUI {
// 高亮显示悬停的单元格
this.gridCells[y][x].setFillStyle(0x888888, 0.7);
// 显示物品信息
const item = this.inventory.getItemAt(x, y);
// 只有在没有选中物品且不是拖动状态时,才不显示物品信息(根据需求,只在选中时显示)
// 注释掉显示物品信息的逻辑,只在选中时显示
/* const item = this.inventory.getItemAt(x, y);
if (item) {
this.showItemInfo(item);
}
} */
}
/**
@@ -489,8 +619,11 @@ export class InventoryUI {
this.hoveredCell = null;
}
// 隐藏物品信息
this.hideItemInfo();
// 如果有选中物品,不要隐藏物品信息
if (!this.selectedItem) {
// 隐藏物品信息
this.hideItemInfo();
}
}
/**
@@ -505,6 +638,9 @@ export class InventoryUI {
if (spriteObj) {
spriteObj.border.setStrokeStyle(2, 0xffff00, 1); // 黄色边框表示选中
}
// 显示选中物品的详情
this.showItemInfo(item);
console.log('选中物品:', item.name);
}
@@ -520,6 +656,8 @@ export class InventoryUI {
spriteObj.border.setStrokeStyle(1, 0xffffff, 1); // 恢复白色边框
}
// 隐藏物品详情
this.hideItemInfo();
console.log('取消选择物品:', this.selectedItem.name);
this.selectedItem = null;
}
@@ -603,16 +741,21 @@ export class InventoryUI {
}
/**
* 添加物品精灵
* 添加物品精灵(基于物品中心坐标)
* @param {Object} item - 物品
*/
addItemSprite(item) {
const { x, y } = item.position;
// 获取物品中心坐标和尺寸
const { x: centerX, y: centerY } = item.position;
const { width, height } = item.gridSize;
// 计算左上角坐标
const topLeftX = Math.floor(centerX - (width - 1) / 2);
const topLeftY = Math.floor(centerY - (height - 1) / 2);
// 计算精灵位置和大小
const spriteX = 10 + x * this.cellSize;
const spriteY = 10 + y * this.cellSize;
const spriteX = 10 + topLeftX * this.cellSize;
const spriteY = 10 + topLeftY * this.cellSize;
const spriteWidth = width * this.cellSize;
const spriteHeight = height * this.cellSize;
@@ -636,20 +779,12 @@ export class InventoryUI {
this.onItemSpriteDown(item, pointer);
return false; // 阻止事件冒泡
});
sprite.on('pointerup', (pointer) => {
this.onItemSpriteUp(item, pointer);
return false; // 阻止事件冒泡
});
border.setInteractive();
border.on('pointerdown', (pointer) => {
this.onItemSpriteDown(item, pointer);
return false; // 阻止事件冒泡
});
border.on('pointerup', (pointer) => {
this.onItemSpriteUp(item, pointer);
return false; // 阻止事件冒泡
});
this.container.add(sprite);
this.container.add(border);