mirror of
http://junepve.top:3000/chicrobot/CINC.git
synced 2026-10-29 22:51:29 +08:00
feat(inventory) 添加背包物品移动、拾取到背包、丢弃背包物品功能
This commit is contained in:
1 parent
ae26209a24
commit
35e10a2abc
6 files changed
+408
-115
No files matched your search
+84
-54
@@ -26,25 +26,29 @@ export class Inventory {
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查物品是否可以放置在指定位置
|
||||
* 检查物品是否可以放置在指定位置(以物品中心为基础点)
|
||||
* @param {Object} item - 要放置的物品
|
||||
* @param {number} x - 起始X坐标
|
||||
* @param {number} y - 起始Y坐标
|
||||
* @param {number} centerX - 中心X坐标
|
||||
* @param {number} centerY - 中心Y坐标
|
||||
* @returns {boolean} 是否可以放置
|
||||
*/
|
||||
canPlaceItem(item, x, y) {
|
||||
canPlaceItem(item, centerX, centerY) {
|
||||
const { gridSize } = item;
|
||||
|
||||
// 将中心坐标转换为左上角坐标
|
||||
const topLeftX = Math.floor(centerX - (gridSize.width - 1) / 2);
|
||||
const topLeftY = Math.floor(centerY - (gridSize.height - 1) / 2);
|
||||
|
||||
// 检查物品尺寸是否在背包范围内
|
||||
if (x < 0 || y < 0 || x + gridSize.width > this.width || y + gridSize.height > this.height) {
|
||||
if (topLeftX < 0 || topLeftY < 0 || topLeftX + gridSize.width > this.width || topLeftY + gridSize.height > this.height) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检查物品占用的所有格子是否都是空的或属于物品自身
|
||||
for (let dy = 0; dy < gridSize.height; dy++) {
|
||||
for (let dx = 0; dx < gridSize.width; dx++) {
|
||||
const gridX = x + dx;
|
||||
const gridY = y + dy;
|
||||
const gridX = topLeftX + dx;
|
||||
const gridY = topLeftY + dy;
|
||||
const cellItemId = this.grid[gridY][gridX];
|
||||
|
||||
// 如果格子不是空的,且不是物品自身,则不能放置
|
||||
@@ -58,29 +62,39 @@ export class Inventory {
|
||||
}
|
||||
|
||||
/**
|
||||
* 放置物品到背包中
|
||||
* 放置物品到背包中(以物品中心为基础点)
|
||||
* @param {Object} item - 要放置的物品
|
||||
* @param {number} x - 起始X坐标
|
||||
* @param {number} y - 起始Y坐标
|
||||
* @param {number} centerX - 中心X坐标
|
||||
* @param {number} centerY - 中心Y坐标
|
||||
* @returns {boolean} 是否放置成功
|
||||
*/
|
||||
placeItem(item, x, y) {
|
||||
if (!this.canPlaceItem(item, x, y)) {
|
||||
placeItem(item, centerX, centerY) {
|
||||
if (!this.canPlaceItem(item, centerX, centerY)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const { gridSize } = item;
|
||||
|
||||
// 将中心坐标转换为左上角坐标
|
||||
const topLeftX = Math.floor(centerX - (gridSize.width - 1) / 2);
|
||||
const topLeftY = Math.floor(centerY - (gridSize.height - 1) / 2);
|
||||
|
||||
// 计算物品中心坐标(用于物品信息)
|
||||
const centerPos = {
|
||||
x: centerX,
|
||||
y: centerY
|
||||
};
|
||||
|
||||
// 记录物品在背包中的位置信息 - 同时设置position和positionInInventory确保一致性
|
||||
item.position = { x, y };
|
||||
item.positionInInventory = { x, y };
|
||||
item.position = centerPos;
|
||||
item.positionInInventory = centerPos;
|
||||
item.inventory = this;
|
||||
item.inInventory = true;
|
||||
|
||||
// 更新网格和物品列表
|
||||
for (let dy = 0; dy < gridSize.height; dy++) {
|
||||
for (let dx = 0; dx < gridSize.width; dx++) {
|
||||
this.grid[y + dy][x + dx] = item.id;
|
||||
this.grid[topLeftY + dy][topLeftX + dx] = item.id;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,10 +134,14 @@ export class Inventory {
|
||||
|
||||
const { position, gridSize } = item;
|
||||
|
||||
// 计算物品占用的左上角坐标(从中心坐标转换)
|
||||
const topLeftX = Math.floor(position.x - (gridSize.width - 1) / 2);
|
||||
const topLeftY = Math.floor(position.y - (gridSize.height - 1) / 2);
|
||||
|
||||
// 清空网格
|
||||
for (let dy = 0; dy < gridSize.height; dy++) {
|
||||
for (let dx = 0; dx < gridSize.width; dx++) {
|
||||
this.grid[position.y + dy][position.x + dx] = null;
|
||||
this.grid[topLeftY + dy][topLeftX + dx] = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,59 +158,65 @@ export class Inventory {
|
||||
}
|
||||
|
||||
/**
|
||||
* 移动背包中的物品
|
||||
* 移动背包中的物品(以物品中心为基础点)
|
||||
* @param {string} itemId - 物品ID
|
||||
* @param {number} newX - 新的X坐标
|
||||
* @param {number} newY - 新的Y坐标
|
||||
* @param {number} newCenterX - 新的中心X坐标
|
||||
* @param {number} newCenterY - 新的中心Y坐标
|
||||
* @returns {boolean} 是否移动成功
|
||||
*/
|
||||
moveItem(itemId, newX, newY) {
|
||||
console.log('moveItem called:', itemId, 'to position:', { newX, newY });
|
||||
moveItem(itemId, newCenterX, newCenterY) {
|
||||
console.log('moveItem called:', itemId, 'to center position:', { newCenterX, newCenterY });
|
||||
const item = this.items.get(itemId);
|
||||
if (!item) {
|
||||
console.log('moveItem failed: 物品不存在');
|
||||
return false;
|
||||
}
|
||||
|
||||
console.log('移动物品:', item.name, '当前位置:', item.position, '目标位置:', { newX, newY });
|
||||
console.log('移动物品:', item.name, '当前中心位置:', item.position, '目标中心位置:', { newCenterX, newCenterY });
|
||||
|
||||
// 检查新位置是否可以放置
|
||||
if (!this.canPlaceItem(item, newX, newY)) {
|
||||
if (!this.canPlaceItem(item, newCenterX, newCenterY)) {
|
||||
console.log('moveItem failed: 新位置无法放置物品');
|
||||
return false;
|
||||
}
|
||||
|
||||
// 保存旧位置信息
|
||||
const oldX = item.position.x;
|
||||
const oldY = item.position.y;
|
||||
const { gridSize } = item;
|
||||
|
||||
console.log('清空旧位置网格:', { oldX, oldY }, '尺寸:', gridSize);
|
||||
// 清空旧位置的网格
|
||||
// 清空旧位置的网格(需要先计算旧位置的左上角坐标)
|
||||
const oldCenterX = item.position.x;
|
||||
const oldCenterY = item.position.y;
|
||||
const oldTopLeftX = Math.floor(oldCenterX - (gridSize.width - 1) / 2);
|
||||
const oldTopLeftY = Math.floor(oldCenterY - (gridSize.height - 1) / 2);
|
||||
|
||||
console.log('清空旧位置网格:', { oldTopLeftX, oldTopLeftY }, '尺寸:', gridSize);
|
||||
for (let dy = 0; dy < gridSize.height; dy++) {
|
||||
for (let dx = 0; dx < gridSize.width; dx++) {
|
||||
this.grid[oldY + dy][oldX + dx] = null;
|
||||
this.grid[oldTopLeftY + dy][oldTopLeftX + dx] = null;
|
||||
}
|
||||
}
|
||||
|
||||
// 更新物品位置
|
||||
item.position = { x: newX, y: newY };
|
||||
item.positionInInventory = { x: newX, y: newY };
|
||||
console.log('更新物品位置:', item.position);
|
||||
// 更新物品位置为新的中心坐标
|
||||
const newCenterPos = { x: newCenterX, y: newCenterY };
|
||||
item.position = newCenterPos;
|
||||
item.positionInInventory = newCenterPos;
|
||||
console.log('更新物品中心位置:', item.position);
|
||||
|
||||
// 更新新位置的网格(需要计算新位置的左上角坐标)
|
||||
const newTopLeftX = Math.floor(newCenterX - (gridSize.width - 1) / 2);
|
||||
const newTopLeftY = Math.floor(newCenterY - (gridSize.height - 1) / 2);
|
||||
|
||||
// 更新新位置的网格
|
||||
for (let dy = 0; dy < gridSize.height; dy++) {
|
||||
for (let dx = 0; dx < gridSize.width; dx++) {
|
||||
this.grid[newY + dy][newX + dx] = item.id;
|
||||
this.grid[newTopLeftY + dy][newTopLeftX + dx] = item.id;
|
||||
}
|
||||
}
|
||||
|
||||
console.log('moveItem success:', item.name, '已移动到', { newX, newY });
|
||||
console.log('moveItem success:', item.name, '已移动到中心位置', { newCenterX, newCenterY });
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 交换两个物品的位置
|
||||
* 交换两个物品的位置(以物品中心为基础点)
|
||||
* @param {string} itemId1 - 第一个物品ID
|
||||
* @param {string} itemId2 - 第二个物品ID
|
||||
* @returns {boolean} 是否交换成功
|
||||
@@ -208,33 +232,34 @@ export class Inventory {
|
||||
}
|
||||
|
||||
console.log('交换物品:', item1.name, '与', item2.name);
|
||||
console.log('物品1位置:', item1.position, '物品2位置:', item2.position);
|
||||
console.log('物品1中心位置:', item1.position, '物品2中心位置:', item2.position);
|
||||
|
||||
const pos1 = { ...item1.position };
|
||||
const pos2 = { ...item2.position };
|
||||
// 保存两个物品的中心位置
|
||||
const centerPos1 = { ...item1.position };
|
||||
const centerPos2 = { ...item2.position };
|
||||
|
||||
// 移除两个物品
|
||||
console.log('移除两个物品');
|
||||
this.removeItem(itemId1);
|
||||
this.removeItem(itemId2);
|
||||
|
||||
// 尝试在对方位置放置
|
||||
console.log('尝试将物品1放置到物品2的位置:', pos2);
|
||||
if (!this.placeItem(item1, pos2.x, pos2.y)) {
|
||||
// 尝试在对方位置放置(使用中心坐标)
|
||||
console.log('尝试将物品1放置到物品2的中心位置:', centerPos2);
|
||||
if (!this.placeItem(item1, centerPos2.x, centerPos2.y)) {
|
||||
// 回滚:将两个物品放回原位置
|
||||
console.log('swapItems failed: 物品1无法放置到物品2的位置,回滚');
|
||||
this.placeItem(item1, pos1.x, pos1.y);
|
||||
this.placeItem(item2, pos2.x, pos2.y);
|
||||
this.placeItem(item1, centerPos1.x, centerPos1.y);
|
||||
this.placeItem(item2, centerPos2.x, centerPos2.y);
|
||||
return false;
|
||||
}
|
||||
|
||||
console.log('尝试将物品2放置到物品1的位置:', pos1);
|
||||
if (!this.placeItem(item2, pos1.x, pos1.y)) {
|
||||
console.log('尝试将物品2放置到物品1的中心位置:', centerPos1);
|
||||
if (!this.placeItem(item2, centerPos1.x, centerPos1.y)) {
|
||||
// 回滚:将第一个物品放回原位置,第二个物品也放回原位置
|
||||
console.log('swapItems failed: 物品2无法放置到物品1的位置,回滚');
|
||||
this.removeItem(itemId1);
|
||||
this.placeItem(item1, pos1.x, pos1.y);
|
||||
this.placeItem(item2, pos2.x, pos2.y);
|
||||
this.placeItem(item1, centerPos1.x, centerPos1.y);
|
||||
this.placeItem(item2, centerPos2.x, centerPos2.y);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -243,9 +268,9 @@ export class Inventory {
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找第一个可用的空位
|
||||
* 查找第一个可用的空位(返回中心坐标)
|
||||
* @param {Object} item - 要放置的物品
|
||||
* @returns {Object|null} 可用位置
|
||||
* @returns {Object|null} 可用中心位置
|
||||
*/
|
||||
findEmptySpot(item) {
|
||||
const { gridSize } = item;
|
||||
@@ -256,11 +281,16 @@ export class Inventory {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 遍历背包网格,寻找可用位置
|
||||
// 遍历背包网格,寻找可用位置(以左上角为起点)
|
||||
for (let y = 0; y <= this.height - gridSize.height; y++) {
|
||||
for (let x = 0; x <= this.width - gridSize.width; x++) {
|
||||
if (this.canPlaceItem(item, x, y)) {
|
||||
return { x, y };
|
||||
// 计算该左上角位置对应的中心坐标
|
||||
// 注意:这里需要确保与canPlaceItem方法中的坐标转换对称
|
||||
const centerX = x + (gridSize.width - 1) / 2;
|
||||
const centerY = y + (gridSize.height - 1) / 2;
|
||||
|
||||
if (this.canPlaceItem(item, centerX, centerY)) {
|
||||
return { x: centerX, y: centerY };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,6 +111,11 @@ export default class Item extends Phaser.GameObjects.Sprite {
|
||||
this.inInventory = true;
|
||||
this.visible = false; // 在场景中隐藏物品
|
||||
this.scene.events.emit('item-picked-up', this, user);
|
||||
|
||||
// 如果场景中有背包UI,更新UI
|
||||
if (this.scene.inventoryUI) {
|
||||
this.scene.inventoryUI.updateUI();
|
||||
}
|
||||
} else {
|
||||
console.log('背包空间不足');
|
||||
}
|
||||
|
||||
@@ -278,6 +278,27 @@ export class ItemManager {
|
||||
return this.itemTemplates.get(itemId) || null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前场景中的所有活动物品
|
||||
* @returns {Array} - 活动物品数组
|
||||
*/
|
||||
getActiveItems() {
|
||||
return Array.from(this.activeItems);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理所有物品
|
||||
*/
|
||||
clearAllItems() {
|
||||
// 销毁所有活动物品
|
||||
this.activeItems.forEach(item => {
|
||||
item.destroy();
|
||||
});
|
||||
|
||||
// 清空活动物品集合
|
||||
this.activeItems.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有物品模板
|
||||
* @returns {Map} - 所有物品模板的Map对象
|
||||
|
||||
@@ -180,8 +180,8 @@ export default class InventoryTestScene extends Phaser.Scene {
|
||||
// 键盘输入
|
||||
this.cursors = this.input.keyboard.createCursorKeys();
|
||||
|
||||
// 背包快捷键(I键)
|
||||
this.input.keyboard.on('keydown-I', () => {
|
||||
// 背包快捷键(B键)
|
||||
this.input.keyboard.on('keydown-B', () => {
|
||||
this.inventoryUI.toggle();
|
||||
});
|
||||
|
||||
@@ -381,7 +381,7 @@ export default class InventoryTestScene extends Phaser.Scene {
|
||||
key4Text.setScrollFactor(0, 0); // 固定在屏幕上,不随相机移动
|
||||
key4Text.setDepth(10); // 设置深度,确保显示在侧边栏上方
|
||||
|
||||
const keyIText = this.add.text(10, 220, '按I键:打开/关闭背包', {
|
||||
const keyIText = this.add.text(10, 220, '按B键:打开/关闭背包', {
|
||||
fontSize: '16px',
|
||||
fill: '#ffffff'
|
||||
});
|
||||
|
||||
+108
-6
@@ -39,6 +39,12 @@ export class InputSystem {
|
||||
// 右移键:同时支持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();
|
||||
@@ -85,8 +91,8 @@ export class InputSystem {
|
||||
// 处理鼠标/触摸按下事件
|
||||
// @param {Object} pointer - 指针事件对象,包含鼠标/触摸的位置等信息
|
||||
onPointerDown(pointer) {
|
||||
// 检查鼠标是否在小地图、摇杆或侧边栏上,如果是则不处理
|
||||
if (this.isMouseOnMiniMap(pointer) || this.isMouseOnJoystick(pointer) || this.isMouseOnSidebar(pointer)) {
|
||||
// 检查鼠标是否在UI元素上,如果是则不处理
|
||||
if (this.isMouseOnUI(pointer)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -157,8 +163,8 @@ export class InputSystem {
|
||||
// 处理鼠标/触摸移动事件
|
||||
// @param {Object} pointer - 指针事件对象
|
||||
onPointerMove(pointer) {
|
||||
// 检查鼠标是否在小地图、摇杆或侧边栏上,如果是则不处理
|
||||
if (this.isMouseOnMiniMap(pointer) || this.isMouseOnJoystick(pointer) || this.isMouseOnSidebar(pointer)) {
|
||||
// 检查鼠标是否在UI元素上,如果是则不处理
|
||||
if (this.isMouseOnUI(pointer)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -193,8 +199,8 @@ export class InputSystem {
|
||||
// 处理鼠标/触摸释放事件
|
||||
// @param {Object} pointer - 指针事件对象
|
||||
onPointerUp(pointer) {
|
||||
// 检查鼠标是否在小地图、摇杆或侧边栏上,如果是则不处理
|
||||
if (this.isMouseOnMiniMap(pointer) || this.isMouseOnJoystick(pointer) || this.isMouseOnSidebar(pointer)) {
|
||||
// 检查鼠标是否在UI元素上,如果是则不处理
|
||||
if (this.isMouseOnUI(pointer)) {
|
||||
// 重置框选起始点和拖动状态
|
||||
this.selectionStart = null;
|
||||
this.dragStart = null;
|
||||
@@ -278,6 +284,18 @@ export class InputSystem {
|
||||
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 - 指针事件对象
|
||||
@@ -349,4 +367,88 @@ export class InputSystem {
|
||||
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: 没有在拾取范围内的物品');
|
||||
}
|
||||
}
|
||||
}
|
||||
+187
-52
@@ -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);
|
||||
|
||||
Reference in new issue
Block a user