mirror of
http://junepve.top:3000/chicrobot/CINC.git
synced 2026-10-30 06:41:29 +08:00
feat(inventory) 添加网格背包系统
This commit is contained in:
1 parent
b9a5180293
commit
ae26209a24
8 files changed
+1836
-22
No files matched your search
+30
-6
@@ -8,9 +8,13 @@
|
|||||||
"weight": 1.0,
|
"weight": 1.0,
|
||||||
"volume": 1.0,
|
"volume": 1.0,
|
||||||
"value": 5,
|
"value": 5,
|
||||||
|
"gridSize": {
|
||||||
|
"width": 1,
|
||||||
|
"height": 2
|
||||||
|
},
|
||||||
"sprite": {
|
"sprite": {
|
||||||
"width": 16,
|
"width": 16,
|
||||||
"height": 16,
|
"height": 32,
|
||||||
"color": 255
|
"color": 255
|
||||||
},
|
},
|
||||||
"properties": {
|
"properties": {
|
||||||
@@ -27,6 +31,10 @@
|
|||||||
"weight": 1.5,
|
"weight": 1.5,
|
||||||
"volume": 1.0,
|
"volume": 1.0,
|
||||||
"value": 10,
|
"value": 10,
|
||||||
|
"gridSize": {
|
||||||
|
"width": 1,
|
||||||
|
"height": 1
|
||||||
|
},
|
||||||
"sprite": {
|
"sprite": {
|
||||||
"width": 16,
|
"width": 16,
|
||||||
"height": 16,
|
"height": 16,
|
||||||
@@ -46,9 +54,13 @@
|
|||||||
"weight": 3.0,
|
"weight": 3.0,
|
||||||
"volume": 2.0,
|
"volume": 2.0,
|
||||||
"value": 20,
|
"value": 20,
|
||||||
|
"gridSize": {
|
||||||
|
"width": 1,
|
||||||
|
"height": 3
|
||||||
|
},
|
||||||
"sprite": {
|
"sprite": {
|
||||||
"width": 16,
|
"width": 16,
|
||||||
"height": 16,
|
"height": 48,
|
||||||
"color": 8421504
|
"color": 8421504
|
||||||
},
|
},
|
||||||
"properties": {
|
"properties": {
|
||||||
@@ -64,9 +76,13 @@
|
|||||||
"weight": 1.0,
|
"weight": 1.0,
|
||||||
"volume": 1.5,
|
"volume": 1.5,
|
||||||
"value": 30,
|
"value": 30,
|
||||||
|
"gridSize": {
|
||||||
|
"width": 2,
|
||||||
|
"height": 2
|
||||||
|
},
|
||||||
"sprite": {
|
"sprite": {
|
||||||
"width": 16,
|
"width": 32,
|
||||||
"height": 16,
|
"height": 32,
|
||||||
"color": 16777215
|
"color": 16777215
|
||||||
},
|
},
|
||||||
"properties": {
|
"properties": {
|
||||||
@@ -82,9 +98,13 @@
|
|||||||
"weight": 0.5,
|
"weight": 0.5,
|
||||||
"volume": 0.5,
|
"volume": 0.5,
|
||||||
"value": 15,
|
"value": 15,
|
||||||
|
"gridSize": {
|
||||||
|
"width": 1,
|
||||||
|
"height": 2
|
||||||
|
},
|
||||||
"sprite": {
|
"sprite": {
|
||||||
"width": 16,
|
"width": 16,
|
||||||
"height": 16,
|
"height": 32,
|
||||||
"color": 16776960
|
"color": 16776960
|
||||||
},
|
},
|
||||||
"properties": {
|
"properties": {
|
||||||
@@ -100,9 +120,13 @@
|
|||||||
"weight": 0.8,
|
"weight": 0.8,
|
||||||
"volume": 1.0,
|
"volume": 1.0,
|
||||||
"value": 12,
|
"value": 12,
|
||||||
|
"gridSize": {
|
||||||
|
"width": 1,
|
||||||
|
"height": 2
|
||||||
|
},
|
||||||
"sprite": {
|
"sprite": {
|
||||||
"width": 16,
|
"width": 16,
|
||||||
"height": 16,
|
"height": 32,
|
||||||
"color": 4194304
|
"color": 4194304
|
||||||
},
|
},
|
||||||
"properties": {
|
"properties": {
|
||||||
|
|||||||
@@ -0,0 +1,389 @@
|
|||||||
|
// 背包系统:管理拾取的物品,支持网格类型的背包系统
|
||||||
|
|
||||||
|
export class Inventory {
|
||||||
|
constructor(width = 6, height = 4) {
|
||||||
|
this.width = width; // 背包宽度(网格数)
|
||||||
|
this.height = height; // 背包高度(网格数)
|
||||||
|
this.grid = this.createGrid(); // 创建背包网格
|
||||||
|
this.items = new Map(); // 存储背包中的物品
|
||||||
|
this.totalWeight = 0; // 总重量
|
||||||
|
this.totalVolume = 0; // 总体积
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建背包网格
|
||||||
|
* @returns {Array} 二维数组表示的网格
|
||||||
|
*/
|
||||||
|
createGrid() {
|
||||||
|
const grid = [];
|
||||||
|
for (let y = 0; y < this.height; y++) {
|
||||||
|
grid[y] = [];
|
||||||
|
for (let x = 0; x < this.width; x++) {
|
||||||
|
grid[y][x] = null; // null表示空格子
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return grid;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查物品是否可以放置在指定位置
|
||||||
|
* @param {Object} item - 要放置的物品
|
||||||
|
* @param {number} x - 起始X坐标
|
||||||
|
* @param {number} y - 起始Y坐标
|
||||||
|
* @returns {boolean} 是否可以放置
|
||||||
|
*/
|
||||||
|
canPlaceItem(item, x, y) {
|
||||||
|
const { gridSize } = item;
|
||||||
|
|
||||||
|
// 检查物品尺寸是否在背包范围内
|
||||||
|
if (x < 0 || y < 0 || x + gridSize.width > this.width || y + 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 cellItemId = this.grid[gridY][gridX];
|
||||||
|
|
||||||
|
// 如果格子不是空的,且不是物品自身,则不能放置
|
||||||
|
if (cellItemId !== null && cellItemId !== item.id) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 放置物品到背包中
|
||||||
|
* @param {Object} item - 要放置的物品
|
||||||
|
* @param {number} x - 起始X坐标
|
||||||
|
* @param {number} y - 起始Y坐标
|
||||||
|
* @returns {boolean} 是否放置成功
|
||||||
|
*/
|
||||||
|
placeItem(item, x, y) {
|
||||||
|
if (!this.canPlaceItem(item, x, y)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { gridSize } = item;
|
||||||
|
|
||||||
|
// 记录物品在背包中的位置信息 - 同时设置position和positionInInventory确保一致性
|
||||||
|
item.position = { x, y };
|
||||||
|
item.positionInInventory = { x, y };
|
||||||
|
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.items.set(item.id, item);
|
||||||
|
this.totalWeight += item.weight;
|
||||||
|
this.totalVolume += item.volume;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查找物品在背包中的位置
|
||||||
|
* @param {string} itemId - 物品ID
|
||||||
|
* @returns {Object|null} 物品位置信息
|
||||||
|
*/
|
||||||
|
findItemPosition(itemId) {
|
||||||
|
for (let y = 0; y < this.height; y++) {
|
||||||
|
for (let x = 0; x < this.width; x++) {
|
||||||
|
if (this.grid[y][x] === itemId) {
|
||||||
|
return { x, y };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从背包中移除物品
|
||||||
|
* @param {string} itemId - 物品ID
|
||||||
|
* @returns {Object|null} 移除的物品
|
||||||
|
*/
|
||||||
|
removeItem(itemId) {
|
||||||
|
const item = this.items.get(itemId);
|
||||||
|
if (!item) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { position, gridSize } = item;
|
||||||
|
|
||||||
|
// 清空网格
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新物品信息和统计数据 - 同时清空position和positionInInventory
|
||||||
|
item.position = null;
|
||||||
|
item.positionInInventory = null;
|
||||||
|
item.inventory = null;
|
||||||
|
item.inInventory = false;
|
||||||
|
this.items.delete(itemId);
|
||||||
|
this.totalWeight -= item.weight;
|
||||||
|
this.totalVolume -= item.volume;
|
||||||
|
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 移动背包中的物品
|
||||||
|
* @param {string} itemId - 物品ID
|
||||||
|
* @param {number} newX - 新的X坐标
|
||||||
|
* @param {number} newY - 新的Y坐标
|
||||||
|
* @returns {boolean} 是否移动成功
|
||||||
|
*/
|
||||||
|
moveItem(itemId, newX, newY) {
|
||||||
|
console.log('moveItem called:', itemId, 'to position:', { newX, newY });
|
||||||
|
const item = this.items.get(itemId);
|
||||||
|
if (!item) {
|
||||||
|
console.log('moveItem failed: 物品不存在');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('移动物品:', item.name, '当前位置:', item.position, '目标位置:', { newX, newY });
|
||||||
|
|
||||||
|
// 检查新位置是否可以放置
|
||||||
|
if (!this.canPlaceItem(item, newX, newY)) {
|
||||||
|
console.log('moveItem failed: 新位置无法放置物品');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存旧位置信息
|
||||||
|
const oldX = item.position.x;
|
||||||
|
const oldY = item.position.y;
|
||||||
|
const { gridSize } = item;
|
||||||
|
|
||||||
|
console.log('清空旧位置网格:', { oldX, oldY }, '尺寸:', gridSize);
|
||||||
|
// 清空旧位置的网格
|
||||||
|
for (let dy = 0; dy < gridSize.height; dy++) {
|
||||||
|
for (let dx = 0; dx < gridSize.width; dx++) {
|
||||||
|
this.grid[oldY + dy][oldX + dx] = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新物品位置
|
||||||
|
item.position = { x: newX, y: newY };
|
||||||
|
item.positionInInventory = { x: newX, y: newY };
|
||||||
|
console.log('更新物品位置:', item.position);
|
||||||
|
|
||||||
|
// 更新新位置的网格
|
||||||
|
for (let dy = 0; dy < gridSize.height; dy++) {
|
||||||
|
for (let dx = 0; dx < gridSize.width; dx++) {
|
||||||
|
this.grid[newY + dy][newX + dx] = item.id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('moveItem success:', item.name, '已移动到', { newX, newY });
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 交换两个物品的位置
|
||||||
|
* @param {string} itemId1 - 第一个物品ID
|
||||||
|
* @param {string} itemId2 - 第二个物品ID
|
||||||
|
* @returns {boolean} 是否交换成功
|
||||||
|
*/
|
||||||
|
swapItems(itemId1, itemId2) {
|
||||||
|
console.log('swapItems called:', itemId1, '与', itemId2);
|
||||||
|
const item1 = this.items.get(itemId1);
|
||||||
|
const item2 = this.items.get(itemId2);
|
||||||
|
|
||||||
|
if (!item1 || !item2) {
|
||||||
|
console.log('swapItems failed: 物品不存在');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('交换物品:', item1.name, '与', item2.name);
|
||||||
|
console.log('物品1位置:', item1.position, '物品2位置:', item2.position);
|
||||||
|
|
||||||
|
const pos1 = { ...item1.position };
|
||||||
|
const pos2 = { ...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('swapItems failed: 物品1无法放置到物品2的位置,回滚');
|
||||||
|
this.placeItem(item1, pos1.x, pos1.y);
|
||||||
|
this.placeItem(item2, pos2.x, pos2.y);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('尝试将物品2放置到物品1的位置:', pos1);
|
||||||
|
if (!this.placeItem(item2, pos1.x, pos1.y)) {
|
||||||
|
// 回滚:将第一个物品放回原位置,第二个物品也放回原位置
|
||||||
|
console.log('swapItems failed: 物品2无法放置到物品1的位置,回滚');
|
||||||
|
this.removeItem(itemId1);
|
||||||
|
this.placeItem(item1, pos1.x, pos1.y);
|
||||||
|
this.placeItem(item2, pos2.x, pos2.y);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('swapItems success: 物品交换成功');
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查找第一个可用的空位
|
||||||
|
* @param {Object} item - 要放置的物品
|
||||||
|
* @returns {Object|null} 可用位置
|
||||||
|
*/
|
||||||
|
findEmptySpot(item) {
|
||||||
|
const { gridSize } = item;
|
||||||
|
|
||||||
|
// 确保物品尺寸不会超过背包尺寸
|
||||||
|
if (gridSize.width > this.width || gridSize.height > this.height) {
|
||||||
|
console.log(`物品尺寸(${gridSize.width}x${gridSize.height})超过背包尺寸(${this.width}x${this.height})`);
|
||||||
|
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 };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("未找到可用空位");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加物品到背包(自动寻找空位)
|
||||||
|
* @param {Object} item - 要添加的物品
|
||||||
|
* @returns {boolean} 是否添加成功
|
||||||
|
*/
|
||||||
|
addItem(item) {
|
||||||
|
const spot = this.findEmptySpot(item);
|
||||||
|
if (!spot) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.placeItem(item, spot.x, spot.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取背包中指定位置的物品
|
||||||
|
* @param {number} x - X坐标
|
||||||
|
* @param {number} y - Y坐标
|
||||||
|
* @returns {Object|null} 物品
|
||||||
|
*/
|
||||||
|
getItemAt(x, y) {
|
||||||
|
if (x < 0 || y < 0 || x >= this.width || y >= this.height) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const itemId = this.grid[y][x];
|
||||||
|
return itemId ? this.items.get(itemId) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取背包中的所有物品
|
||||||
|
* @returns {Map} 物品映射
|
||||||
|
*/
|
||||||
|
getAllItems() {
|
||||||
|
return this.items;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取背包的统计信息
|
||||||
|
* @returns {Object} 统计信息
|
||||||
|
*/
|
||||||
|
getStats() {
|
||||||
|
return {
|
||||||
|
width: this.width,
|
||||||
|
height: this.height,
|
||||||
|
totalItems: this.items.size,
|
||||||
|
totalWeight: this.totalWeight,
|
||||||
|
totalVolume: this.totalVolume,
|
||||||
|
emptySlots: this.calculateEmptySlots()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算空位数
|
||||||
|
* @returns {number} 空位数
|
||||||
|
*/
|
||||||
|
calculateEmptySlots() {
|
||||||
|
let count = 0;
|
||||||
|
for (let y = 0; y < this.height; y++) {
|
||||||
|
for (let x = 0; x < this.width; x++) {
|
||||||
|
if (this.grid[y][x] === null) {
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清空背包
|
||||||
|
*/
|
||||||
|
clear() {
|
||||||
|
this.grid = this.createGrid();
|
||||||
|
this.items.clear();
|
||||||
|
this.totalWeight = 0;
|
||||||
|
this.totalVolume = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导出背包数据
|
||||||
|
* @returns {Object} 背包数据
|
||||||
|
*/
|
||||||
|
exportData() {
|
||||||
|
const items = [];
|
||||||
|
this.items.forEach(item => {
|
||||||
|
items.push({
|
||||||
|
...item.getInfo(),
|
||||||
|
position: item.position
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
width: this.width,
|
||||||
|
height: this.height,
|
||||||
|
items: items
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导入背包数据
|
||||||
|
* @param {Object} data - 背包数据
|
||||||
|
*/
|
||||||
|
importData(data) {
|
||||||
|
this.clear();
|
||||||
|
this.width = data.width;
|
||||||
|
this.height = data.height;
|
||||||
|
this.grid = this.createGrid();
|
||||||
|
|
||||||
|
data.items.forEach(itemData => {
|
||||||
|
// 这里需要重新创建物品实例
|
||||||
|
// 假设Item类有一个静态方法createFromData
|
||||||
|
const item = Item.createFromData(itemData);
|
||||||
|
if (item) {
|
||||||
|
this.placeItem(item, itemData.position.x, itemData.position.y);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
+50
-4
@@ -3,7 +3,7 @@
|
|||||||
export default class Item extends Phaser.GameObjects.Sprite {
|
export default class Item extends Phaser.GameObjects.Sprite {
|
||||||
constructor(scene, x, y, itemData) {
|
constructor(scene, x, y, itemData) {
|
||||||
// 从itemData中提取精灵属性
|
// 从itemData中提取精灵属性
|
||||||
const { id, name, description, type, weight, volume, value, sprite, properties } = itemData;
|
const { id, name, description, type, weight, volume, value, gridSize, sprite, properties } = itemData;
|
||||||
|
|
||||||
// 创建一个简单的图形纹理作为物品精灵
|
// 创建一个简单的图形纹理作为物品精灵
|
||||||
if (!scene.textures.exists(id)) {
|
if (!scene.textures.exists(id)) {
|
||||||
@@ -36,15 +36,22 @@ export default class Item extends Phaser.GameObjects.Sprite {
|
|||||||
super(scene, x, y, id);
|
super(scene, x, y, id);
|
||||||
|
|
||||||
// 设置物品属性
|
// 设置物品属性
|
||||||
this.id = id;
|
this.itemTypeId = id; // 物品类型ID,用于识别物品类型
|
||||||
|
this.id = `${id}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; // 生成唯一ID
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.description = description;
|
this.description = description;
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.weight = weight;
|
this.weight = weight;
|
||||||
this.volume = volume;
|
this.volume = volume;
|
||||||
this.value = value;
|
this.value = value;
|
||||||
|
this.gridSize = gridSize || { width: 1, height: 1 }; // 默认1x1网格
|
||||||
this.properties = properties;
|
this.properties = properties;
|
||||||
|
|
||||||
|
// 背包相关属性
|
||||||
|
this.position = null; // 在背包中的位置
|
||||||
|
this.positionInInventory = null; // 在背包中的位置(保持向后兼容)
|
||||||
|
this.inInventory = false;
|
||||||
|
|
||||||
// 添加到场景中
|
// 添加到场景中
|
||||||
scene.add.existing(this);
|
scene.add.existing(this);
|
||||||
scene.physics.add.existing(this, true); // 静态物体,不响应物理碰撞
|
scene.physics.add.existing(this, true); // 静态物体,不响应物理碰撞
|
||||||
@@ -95,10 +102,48 @@ export default class Item extends Phaser.GameObjects.Sprite {
|
|||||||
*/
|
*/
|
||||||
pickup(user) {
|
pickup(user) {
|
||||||
console.log(`${user.name} 拾取了 ${this.name}`);
|
console.log(`${user.name} 拾取了 ${this.name}`);
|
||||||
// 这里可以添加物品到背包的逻辑
|
|
||||||
// 暂时简单移除物品
|
// 检查用户是否有背包
|
||||||
|
if (user.inventory) {
|
||||||
|
// 尝试将物品添加到背包
|
||||||
|
const success = user.inventory.addItem(this);
|
||||||
|
if (success) {
|
||||||
|
this.inInventory = true;
|
||||||
|
this.visible = false; // 在场景中隐藏物品
|
||||||
|
this.scene.events.emit('item-picked-up', this, user);
|
||||||
|
} else {
|
||||||
|
console.log('背包空间不足');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 如果没有背包系统,简单移除物品
|
||||||
this.destroy();
|
this.destroy();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从背包中使用物品
|
||||||
|
* @param {Object} user - 使用物品的单位
|
||||||
|
*/
|
||||||
|
useFromInventory(user) {
|
||||||
|
// 使用物品
|
||||||
|
this.use(user);
|
||||||
|
|
||||||
|
// 从背包中移除物品
|
||||||
|
if (user.inventory) {
|
||||||
|
user.inventory.removeItem(this.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建物品实例(静态方法)
|
||||||
|
* @param {Object} itemData - 物品数据
|
||||||
|
* @returns {Item} 物品实例
|
||||||
|
*/
|
||||||
|
static createFromData(itemData) {
|
||||||
|
// 这个方法需要在具体场景中实现
|
||||||
|
// 因为需要场景上下文来创建精灵
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 使用物品
|
* 使用物品
|
||||||
@@ -198,6 +243,7 @@ export default class Item extends Phaser.GameObjects.Sprite {
|
|||||||
weight: this.weight,
|
weight: this.weight,
|
||||||
volume: this.volume,
|
volume: this.volume,
|
||||||
value: this.value,
|
value: this.value,
|
||||||
|
gridSize: this.gridSize,
|
||||||
properties: this.properties
|
properties: this.properties
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
// 物品管理器:负责加载、管理和创建物品
|
// 物品管理器:负责加载、管理和创建物品
|
||||||
|
|
||||||
|
// 静态导入Item类
|
||||||
|
import Item from './Item.js';
|
||||||
|
|
||||||
export class ItemManager {
|
export class ItemManager {
|
||||||
constructor(scene) {
|
constructor(scene) {
|
||||||
this.scene = scene;
|
this.scene = scene;
|
||||||
@@ -198,8 +201,7 @@ export class ItemManager {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 动态导入Item类
|
// 使用静态导入的Item类创建物品
|
||||||
import('./Item.js').then(({ default: Item }) => {
|
|
||||||
const item = new Item(this.scene, x, y, itemData);
|
const item = new Item(this.scene, x, y, itemData);
|
||||||
|
|
||||||
// 将物品添加到活动物品集合中
|
// 将物品添加到活动物品集合中
|
||||||
@@ -211,7 +213,6 @@ export class ItemManager {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return item;
|
return item;
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -254,8 +255,7 @@ export class ItemManager {
|
|||||||
// 随机选择一个该类型的物品
|
// 随机选择一个该类型的物品
|
||||||
const randomItemData = typeItems[Math.floor(Math.random() * typeItems.length)];
|
const randomItemData = typeItems[Math.floor(Math.random() * typeItems.length)];
|
||||||
|
|
||||||
// 动态导入Item类
|
// 使用静态导入的Item类创建物品
|
||||||
import('./Item.js').then(({ default: Item }) => {
|
|
||||||
const item = new Item(this.scene, x, y, randomItemData);
|
const item = new Item(this.scene, x, y, randomItemData);
|
||||||
|
|
||||||
// 将物品添加到活动物品集合中
|
// 将物品添加到活动物品集合中
|
||||||
@@ -267,7 +267,6 @@ export class ItemManager {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return item;
|
return item;
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ import MovementTestScene5 from './scenes/MovementTestScene5.js';
|
|||||||
import MovementTestScene6 from './scenes/MovementTestScene6.js';
|
import MovementTestScene6 from './scenes/MovementTestScene6.js';
|
||||||
// 导入物品系统测试场景
|
// 导入物品系统测试场景
|
||||||
import ItemTestScene from './scenes/ItemTestScene.js';
|
import ItemTestScene from './scenes/ItemTestScene.js';
|
||||||
|
// 导入背包系统测试场景
|
||||||
|
import InventoryTestScene from './scenes/InventoryTestScene.js';
|
||||||
|
|
||||||
// 创建游戏的最终配置对象
|
// 创建游戏的最终配置对象
|
||||||
const config = {
|
const config = {
|
||||||
@@ -30,7 +32,8 @@ const config = {
|
|||||||
MovementTestScene4,
|
MovementTestScene4,
|
||||||
MovementTestScene5,
|
MovementTestScene5,
|
||||||
MovementTestScene6,
|
MovementTestScene6,
|
||||||
ItemTestScene
|
ItemTestScene,
|
||||||
|
InventoryTestScene
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -39,10 +42,10 @@ const config = {
|
|||||||
window.game = new Phaser.Game(config);
|
window.game = new Phaser.Game(config);
|
||||||
|
|
||||||
// 添加一个函数,用于从控制台切换到测试场景
|
// 添加一个函数,用于从控制台切换到测试场景
|
||||||
window.startMovementTest = function(sceneIndex = 1) {
|
window.startTest = function(sceneIndex = 1) {
|
||||||
const sceneKey = `MovementTestScene${sceneIndex}`;
|
const scenes = window.game.scene.scenes;
|
||||||
if (window.game.scene.getIndex(sceneKey) !== -1) {
|
if (sceneIndex >= 0 && sceneIndex < scenes.length) {
|
||||||
window.game.scene.start(sceneKey);
|
window.game.scene.start(scenes[sceneIndex]);
|
||||||
console.log(`已切换到测试场景${sceneIndex}`);
|
console.log(`已切换到测试场景${sceneIndex}`);
|
||||||
} else {
|
} else {
|
||||||
console.error(`测试场景${sceneIndex}不存在`);
|
console.error(`测试场景${sceneIndex}不存在`);
|
||||||
|
|||||||
+2
-1
@@ -19,12 +19,13 @@ export default class BootScene extends Phaser.Scene {
|
|||||||
|
|
||||||
// 测试场景列表
|
// 测试场景列表
|
||||||
const testScenes = [
|
const testScenes = [
|
||||||
|
{ key: 'InventoryTestScene', name: '测试场景8:背包系统测试' },
|
||||||
|
{ key: 'MovementTestScene6', name: '测试场景6:小地图测试' },
|
||||||
{ key: 'MovementTestScene1', name: '测试场景1:相对移动(键盘控制)' },
|
{ key: 'MovementTestScene1', name: '测试场景1:相对移动(键盘控制)' },
|
||||||
{ key: 'MovementTestScene2', name: '测试场景2:目标点移动' },
|
{ key: 'MovementTestScene2', name: '测试场景2:目标点移动' },
|
||||||
{ key: 'MovementTestScene3', name: '测试场景3:单位选择与移动命令' },
|
{ key: 'MovementTestScene3', name: '测试场景3:单位选择与移动命令' },
|
||||||
{ key: 'MovementTestScene4', name: '测试场景4:虚拟摇杆控制' },
|
{ key: 'MovementTestScene4', name: '测试场景4:虚拟摇杆控制' },
|
||||||
{ key: 'MovementTestScene5', name: '测试场景5:移动更新逻辑' },
|
{ key: 'MovementTestScene5', name: '测试场景5:移动更新逻辑' },
|
||||||
{ key: 'MovementTestScene6', name: '测试场景6:小地图测试' },
|
|
||||||
{ key: 'ItemTestScene', name: '测试场景7:物品系统测试' },
|
{ key: 'ItemTestScene', name: '测试场景7:物品系统测试' },
|
||||||
{ key: 'GameScene', name: '返回主游戏场景' }
|
{ key: 'GameScene', name: '返回主游戏场景' }
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -0,0 +1,631 @@
|
|||||||
|
// 背包系统测试场景
|
||||||
|
|
||||||
|
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';
|
||||||
|
|
||||||
|
export default class InventoryTestScene extends Phaser.Scene {
|
||||||
|
constructor() {
|
||||||
|
super({ key: 'InventoryTestScene' });
|
||||||
|
}
|
||||||
|
|
||||||
|
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.cameras.main.centerOn(0, 0);
|
||||||
|
|
||||||
|
// 初始化系统
|
||||||
|
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();
|
||||||
|
|
||||||
|
// 创建测试物品
|
||||||
|
this.createTestItems();
|
||||||
|
|
||||||
|
// 设置输入处理
|
||||||
|
this.setupInput();
|
||||||
|
|
||||||
|
// 创建侧边栏UI
|
||||||
|
this.createSidebar();
|
||||||
|
|
||||||
|
// 显示提示信息
|
||||||
|
this.showInstructions();
|
||||||
|
|
||||||
|
// 显示单位状态
|
||||||
|
this.updateUnitStatus(this.player);
|
||||||
|
|
||||||
|
// 显示默认提示
|
||||||
|
this.itemInfoText.setText('未选择任何物品');
|
||||||
|
this.interactionText.setText('点击物品查看详细信息,交互范围50像素');
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建测试物品
|
||||||
|
*/
|
||||||
|
createTestItems() {
|
||||||
|
// 等待物品数据加载完成
|
||||||
|
this.itemManager.onDataLoaded(() => {
|
||||||
|
// 创建初始物品
|
||||||
|
this.createInitialItems();
|
||||||
|
|
||||||
|
// 绑定物品点击事件
|
||||||
|
this.events.on('item-clicked', (item) => {
|
||||||
|
this.showItemInfo(item);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建初始物品
|
||||||
|
*/
|
||||||
|
createInitialItems() {
|
||||||
|
// 在不同位置创建各种类型的物品
|
||||||
|
const itemTypes = ['food', 'tool', 'medical', 'weapon'];
|
||||||
|
|
||||||
|
for (let i = 0; i < itemTypes.length; i++) {
|
||||||
|
const x = this.player.x + (i - 2) * 200;
|
||||||
|
const y = this.player.y - 200;
|
||||||
|
this.itemManager.createItemByType(itemTypes[i], x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 在场景中随机位置创建更多物品
|
||||||
|
for (let i = 0; i < 6; 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();
|
||||||
|
|
||||||
|
// 背包快捷键(I键)
|
||||||
|
this.input.keyboard.on('keydown-I', () => {
|
||||||
|
this.inventoryUI.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('所有物品已清理');
|
||||||
|
});
|
||||||
|
|
||||||
|
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('所有物品已清理');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建按钮
|
||||||
|
* @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
|
||||||
|
this.inventoryUI.updateUI();
|
||||||
|
this.interactionText.setText('');
|
||||||
|
this.itemInfoText.setText('未选择任何物品');
|
||||||
|
});
|
||||||
|
// 注册Q键快捷使用物品
|
||||||
|
this.input.keyboard.once('keydown-Q', () => {
|
||||||
|
item.use(unit);
|
||||||
|
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, '点击物品查看信息,右键移动到物品位置', {
|
||||||
|
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 keyIText = this.add.text(10, 220, '按I键:打开/关闭背包', {
|
||||||
|
fontSize: '16px',
|
||||||
|
fill: '#ffffff'
|
||||||
|
});
|
||||||
|
keyIText.setScrollFactor(0, 0); // 固定在屏幕上,不随相机移动
|
||||||
|
keyIText.setDepth(10); // 设置深度,确保显示在侧边栏上方
|
||||||
|
|
||||||
|
const spaceText = this.add.text(10, 250, '按空格键返回启动场景', {
|
||||||
|
fontSize: '16px',
|
||||||
|
fill: '#ffffff'
|
||||||
|
});
|
||||||
|
spaceText.setScrollFactor(0, 0); // 固定在屏幕上,不随相机移动
|
||||||
|
spaceText.setDepth(10); // 设置深度,确保显示在侧边栏上方
|
||||||
|
|
||||||
|
// 显示背包操作说明
|
||||||
|
const inventoryText = this.add.text(10, 280, '背包操作:点击物品选择,再次点击取消选择,点击空位移动物品', {
|
||||||
|
fontSize: '14px',
|
||||||
|
fill: '#ffff00'
|
||||||
|
});
|
||||||
|
inventoryText.setScrollFactor(0, 0); // 固定在屏幕上,不随相机移动
|
||||||
|
inventoryText.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();
|
||||||
|
|
||||||
|
// 更新所有单位
|
||||||
|
this.unitGroup.getChildren().forEach(unit => {
|
||||||
|
if (unit.update) {
|
||||||
|
unit.update();
|
||||||
|
}
|
||||||
|
unit.updateMovement();
|
||||||
|
// 更新单位状态显示
|
||||||
|
this.updateUnitStatus(unit);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,721 @@
|
|||||||
|
// 背包UI组件:负责显示背包界面和处理用户交互
|
||||||
|
|
||||||
|
export class InventoryUI {
|
||||||
|
constructor(scene, inventory, cellSize = 40) {
|
||||||
|
this.scene = scene;
|
||||||
|
this.inventory = inventory;
|
||||||
|
this.cellSize = cellSize;
|
||||||
|
this.isOpen = false;
|
||||||
|
this.selectedItem = null;
|
||||||
|
this.hoveredCell = null;
|
||||||
|
|
||||||
|
// 拖动相关属性
|
||||||
|
this.isDragging = false;
|
||||||
|
this.draggedItem = null;
|
||||||
|
this.draggedSprite = null;
|
||||||
|
this.dragOffset = { x: 0, y: 0 };
|
||||||
|
|
||||||
|
this.container = null;
|
||||||
|
this.gridCells = [];
|
||||||
|
this.itemSprites = new Map();
|
||||||
|
|
||||||
|
// 计算背包位置(相机右侧)
|
||||||
|
this.calculatePosition();
|
||||||
|
|
||||||
|
this.createUI();
|
||||||
|
|
||||||
|
// 添加全局鼠标移动事件监听
|
||||||
|
this.scene.input.on('pointermove', (pointer) => {
|
||||||
|
this.onMouseMove(pointer);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 添加全局鼠标释放事件监听
|
||||||
|
this.scene.input.on('pointerup', (pointer) => {
|
||||||
|
this.onMouseUp(pointer);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算背包位置(相机右侧)
|
||||||
|
*/
|
||||||
|
calculatePosition() {
|
||||||
|
// 背包宽度
|
||||||
|
const inventoryWidth = this.inventory.width * this.cellSize + 20;
|
||||||
|
// 固定在屏幕右侧,有一定边距
|
||||||
|
this.x = this.scene.sys.game.config.width - inventoryWidth - 20;
|
||||||
|
this.y = 20;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建背包UI
|
||||||
|
*/
|
||||||
|
createUI() {
|
||||||
|
// 创建容器
|
||||||
|
this.container = this.scene.add.container(this.x, this.y);
|
||||||
|
this.container.setDepth(100);
|
||||||
|
this.container.setScrollFactor(0); // 固定在屏幕上,不随相机移动
|
||||||
|
|
||||||
|
// 创建背景
|
||||||
|
const background = this.scene.add.rectangle(
|
||||||
|
0, 0,
|
||||||
|
this.inventory.width * this.cellSize + 20,
|
||||||
|
this.inventory.height * this.cellSize + 20,
|
||||||
|
0x333333, 0.9
|
||||||
|
);
|
||||||
|
background.setOrigin(0, 0);
|
||||||
|
background.setScrollFactor(0); // 固定在屏幕上
|
||||||
|
background.setDepth(-1); // 确保背景在所有内容下方
|
||||||
|
|
||||||
|
// 添加交互,阻止点击事件穿透到下方地图
|
||||||
|
background.setInteractive();
|
||||||
|
background.on('pointerdown', (pointer) => {
|
||||||
|
return false; // 阻止事件冒泡
|
||||||
|
});
|
||||||
|
background.on('pointerover', (pointer) => {
|
||||||
|
return false; // 阻止事件冒泡
|
||||||
|
});
|
||||||
|
background.on('pointerout', (pointer) => {
|
||||||
|
return false; // 阻止事件冒泡
|
||||||
|
});
|
||||||
|
|
||||||
|
this.container.add(background);
|
||||||
|
|
||||||
|
// 创建网格
|
||||||
|
this.createGrid();
|
||||||
|
|
||||||
|
// 创建关闭按钮
|
||||||
|
this.createCloseButton();
|
||||||
|
|
||||||
|
// 创建统计信息
|
||||||
|
this.createStats();
|
||||||
|
|
||||||
|
// 初始化时隐藏
|
||||||
|
this.hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建背包网格
|
||||||
|
*/
|
||||||
|
createGrid() {
|
||||||
|
const offsetX = 10;
|
||||||
|
const offsetY = 10;
|
||||||
|
|
||||||
|
for (let y = 0; y < this.inventory.height; y++) {
|
||||||
|
this.gridCells[y] = [];
|
||||||
|
for (let x = 0; x < this.inventory.width; x++) {
|
||||||
|
const cellX = offsetX + x * this.cellSize;
|
||||||
|
const cellY = offsetY + y * this.cellSize;
|
||||||
|
|
||||||
|
// 创建单元格
|
||||||
|
const cell = this.scene.add.rectangle(cellX, cellY, this.cellSize, this.cellSize, 0x666666, 0.5);
|
||||||
|
cell.setOrigin(0, 0);
|
||||||
|
cell.setStrokeStyle(1, 0xffffff);
|
||||||
|
cell.setScrollFactor(0); // 固定在屏幕上,不随相机移动
|
||||||
|
|
||||||
|
// 添加交互
|
||||||
|
cell.setInteractive();
|
||||||
|
|
||||||
|
// 绑定点击事件
|
||||||
|
cell.on('pointerdown', (pointer) => {
|
||||||
|
console.log('单元格点击事件触发:', { x, y });
|
||||||
|
this.onCellClicked(x, y);
|
||||||
|
return false; // 阻止事件冒泡
|
||||||
|
});
|
||||||
|
cell.on('pointerover', (pointer) => {
|
||||||
|
this.onCellHovered(x, y);
|
||||||
|
return false; // 阻止事件冒泡
|
||||||
|
});
|
||||||
|
cell.on('pointerout', (pointer) => {
|
||||||
|
this.onCellUnhovered();
|
||||||
|
return false; // 阻止事件冒泡
|
||||||
|
});
|
||||||
|
|
||||||
|
this.container.add(cell);
|
||||||
|
this.gridCells[y][x] = cell;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建关闭按钮
|
||||||
|
*/
|
||||||
|
createCloseButton() {
|
||||||
|
const buttonX = this.inventory.width * this.cellSize + 25;
|
||||||
|
const buttonY = 10;
|
||||||
|
|
||||||
|
const button = this.scene.add.text(buttonX, buttonY, 'X', {
|
||||||
|
font: '24px Arial',
|
||||||
|
fill: '#ffffff'
|
||||||
|
});
|
||||||
|
button.setInteractive({
|
||||||
|
useHandCursor: true
|
||||||
|
});
|
||||||
|
|
||||||
|
button.on('pointerdown', (pointer) => {
|
||||||
|
this.hide();
|
||||||
|
return false; // 阻止事件冒泡
|
||||||
|
});
|
||||||
|
button.on('pointerover', (pointer) => {
|
||||||
|
button.setFill('#ff0000');
|
||||||
|
return false; // 阻止事件冒泡
|
||||||
|
});
|
||||||
|
button.on('pointerout', (pointer) => {
|
||||||
|
button.setFill('#ffffff');
|
||||||
|
return false; // 阻止事件冒泡
|
||||||
|
});
|
||||||
|
|
||||||
|
this.container.add(button);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建统计信息
|
||||||
|
*/
|
||||||
|
createStats() {
|
||||||
|
const statsX = this.inventory.width * this.cellSize + 30;
|
||||||
|
const statsY = 50;
|
||||||
|
|
||||||
|
// 创建统计信息背景
|
||||||
|
const statsBackground = this.scene.add.rectangle(
|
||||||
|
statsX - 20,
|
||||||
|
statsY - 20,
|
||||||
|
120, 100,
|
||||||
|
0x222222, 0.9
|
||||||
|
);
|
||||||
|
statsBackground.setOrigin(0, 0);
|
||||||
|
statsBackground.setScrollFactor(0);
|
||||||
|
|
||||||
|
// 添加交互,阻止事件穿透
|
||||||
|
statsBackground.setInteractive();
|
||||||
|
statsBackground.on('pointerdown', (pointer) => {
|
||||||
|
return false; // 阻止事件冒泡
|
||||||
|
});
|
||||||
|
|
||||||
|
this.container.add(statsBackground);
|
||||||
|
|
||||||
|
this.statsText = this.scene.add.text(statsX, statsY, '', {
|
||||||
|
font: '12px Arial',
|
||||||
|
fill: '#ffffff'
|
||||||
|
});
|
||||||
|
|
||||||
|
this.container.add(this.statsText);
|
||||||
|
this.updateStats();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新统计信息
|
||||||
|
*/
|
||||||
|
updateStats() {
|
||||||
|
const stats = this.inventory.getStats();
|
||||||
|
this.statsText.setText([
|
||||||
|
`物品数量: ${stats.totalItems}`,
|
||||||
|
`总重量: ${stats.totalWeight.toFixed(1)}`,
|
||||||
|
`总体积: ${stats.totalVolume.toFixed(1)}`,
|
||||||
|
`空位数: ${stats.emptySlots}`
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 物品精灵点击事件
|
||||||
|
* @param {Object} item - 点击的物品
|
||||||
|
*/
|
||||||
|
onItemSpriteClicked(item) {
|
||||||
|
console.log('onItemSpriteClicked:', item.name, 'ID:', item.id);
|
||||||
|
if (this.selectedItem && this.selectedItem.id === item.id) {
|
||||||
|
// 如果点击的是已选中的物品,取消选择
|
||||||
|
console.log('点击已选中物品,取消选择:', item.name);
|
||||||
|
this.deselectItem();
|
||||||
|
} else {
|
||||||
|
// 如果已经有选中的物品,先取消选择
|
||||||
|
if (this.selectedItem) {
|
||||||
|
console.log('已有选中物品,先取消选择:', this.selectedItem.name);
|
||||||
|
this.deselectItem();
|
||||||
|
}
|
||||||
|
// 选中当前物品
|
||||||
|
console.log('选中物品:', item.name);
|
||||||
|
this.selectItem(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 物品精灵鼠标按下事件,用于启动拖动
|
||||||
|
* @param {Object} item - 物品
|
||||||
|
* @param {Phaser.Input.Pointer} pointer - 指针对象
|
||||||
|
*/
|
||||||
|
onItemSpriteDown(item, pointer) {
|
||||||
|
console.log('onItemSpriteDown:', item.name, '开始拖动');
|
||||||
|
this.isDragging = true;
|
||||||
|
this.draggedItem = item;
|
||||||
|
|
||||||
|
// 如果没有选中物品,先选中
|
||||||
|
if (!this.selectedItem || this.selectedItem.id !== item.id) {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 全局鼠标移动事件处理
|
||||||
|
* @param {Phaser.Input.Pointer} pointer - 指针对象
|
||||||
|
*/
|
||||||
|
onMouseMove(pointer) {
|
||||||
|
// 如果还没有开始拖动,检查是否应该开始拖动
|
||||||
|
if (this.clickItem && !this.isDragging) {
|
||||||
|
const distance = Math.sqrt(
|
||||||
|
Math.pow(pointer.x - this.clickPosition.x, 2) +
|
||||||
|
Math.pow(pointer.y - this.clickPosition.y, 2)
|
||||||
|
);
|
||||||
|
|
||||||
|
// 如果移动距离超过阈值,开始拖动
|
||||||
|
if (distance > 5) {
|
||||||
|
this.startDragging();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果正在拖动,更新精灵位置
|
||||||
|
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;
|
||||||
|
|
||||||
|
this.draggedSprite.setPosition(localX, localY);
|
||||||
|
|
||||||
|
// 检查鼠标是否悬停在某个单元格上
|
||||||
|
this.updateDragHover(pointer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 开始拖动物品
|
||||||
|
*/
|
||||||
|
startDragging() {
|
||||||
|
console.log('startDragging:', this.clickItem.name);
|
||||||
|
this.isDragging = true;
|
||||||
|
this.draggedItem = this.clickItem;
|
||||||
|
|
||||||
|
// 获取物品精灵
|
||||||
|
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.draggedSprite.setDepth(10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新拖动时的悬停状态
|
||||||
|
* @param {Phaser.Input.Pointer} pointer - 指针对象
|
||||||
|
*/
|
||||||
|
updateDragHover(pointer) {
|
||||||
|
// 计算鼠标在背包容器内的位置
|
||||||
|
const localX = pointer.x - this.container.x;
|
||||||
|
const localY = pointer.y - this.container.y;
|
||||||
|
|
||||||
|
// 计算对应的单元格坐标
|
||||||
|
const cellX = Math.floor((localX - 10) / this.cellSize);
|
||||||
|
const cellY = Math.floor((localY - 10) / this.cellSize);
|
||||||
|
|
||||||
|
// 检查坐标是否在背包范围内
|
||||||
|
if (cellX >= 0 && cellX < this.inventory.width && cellY >= 0 && cellY < this.inventory.height) {
|
||||||
|
// 更新悬停单元格
|
||||||
|
if (!this.hoveredCell || this.hoveredCell.x !== cellX || this.hoveredCell.y !== cellY) {
|
||||||
|
// 移除之前的悬停效果
|
||||||
|
if (this.hoveredCell) {
|
||||||
|
this.onCellUnhovered();
|
||||||
|
}
|
||||||
|
// 设置新的悬停单元格
|
||||||
|
this.onCellHovered(cellX, cellY);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 鼠标移出背包范围,移除悬停效果
|
||||||
|
if (this.hoveredCell) {
|
||||||
|
this.onCellUnhovered();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 全局鼠标释放事件处理
|
||||||
|
* @param {Phaser.Input.Pointer} pointer - 指针对象
|
||||||
|
*/
|
||||||
|
onMouseUp(pointer) {
|
||||||
|
// 如果正在拖动,处理拖动结束
|
||||||
|
if (this.isDragging && this.draggedItem) {
|
||||||
|
console.log('onMouseUp: 结束拖动');
|
||||||
|
|
||||||
|
// 计算鼠标在背包容器内的位置
|
||||||
|
const localX = pointer.x - this.container.x;
|
||||||
|
const localY = pointer.y - this.container.y;
|
||||||
|
|
||||||
|
// 计算对应的单元格坐标
|
||||||
|
const cellX = Math.floor((localX - 10) / this.cellSize);
|
||||||
|
const cellY = Math.floor((localY - 10) / this.cellSize);
|
||||||
|
|
||||||
|
// 尝试将物品放置到新位置
|
||||||
|
let success = false;
|
||||||
|
|
||||||
|
// 检查坐标是否在背包范围内
|
||||||
|
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);
|
||||||
|
} else {
|
||||||
|
// 如果目标位置是空的,尝试移动
|
||||||
|
success = this.inventory.moveItem(this.draggedItem.id, cellX, cellY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重置拖动状态
|
||||||
|
this.isDragging = false;
|
||||||
|
if (this.draggedSprite) {
|
||||||
|
this.draggedSprite.setDepth(1); // 恢复精灵深度
|
||||||
|
this.draggedSprite = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新UI
|
||||||
|
if (success) {
|
||||||
|
this.deselectItem();
|
||||||
|
this.updateUI();
|
||||||
|
} else {
|
||||||
|
// 如果移动失败,重置精灵位置
|
||||||
|
this.updateUI();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.draggedItem = null;
|
||||||
|
this.dragOffset = { x: 0, y: 0 };
|
||||||
|
|
||||||
|
// 移除悬停效果
|
||||||
|
if (this.hoveredCell) {
|
||||||
|
this.onCellUnhovered();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重置点击状态
|
||||||
|
this.clickItem = null;
|
||||||
|
this.clickPosition = null;
|
||||||
|
this.clickTime = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 单元格点击事件
|
||||||
|
* @param {number} x - 单元格X坐标
|
||||||
|
* @param {number} y - 单元格Y坐标
|
||||||
|
*/
|
||||||
|
onCellClicked(x, y) {
|
||||||
|
console.log('onCellClicked:', { x, y });
|
||||||
|
const item = this.inventory.getItemAt(x, y);
|
||||||
|
|
||||||
|
if (this.selectedItem) {
|
||||||
|
console.log('已有选中物品:', this.selectedItem.name, 'ID:', this.selectedItem.id);
|
||||||
|
let success = false;
|
||||||
|
|
||||||
|
// 如果有选中的物品,尝试移动它
|
||||||
|
if (item) {
|
||||||
|
// 如果点击的是另一个物品,尝试交换
|
||||||
|
console.log('点击另一个物品,尝试交换:', item.name, 'ID:', item.id);
|
||||||
|
success = this.inventory.swapItems(this.selectedItem.id, item.id);
|
||||||
|
console.log('交换结果:', success);
|
||||||
|
} else {
|
||||||
|
// 如果点击的是空位,尝试移动
|
||||||
|
console.log('点击空位,尝试移动物品:', this.selectedItem.name, '到位置:', { x, y });
|
||||||
|
success = this.inventory.moveItem(this.selectedItem.id, x, y);
|
||||||
|
console.log('移动结果:', success);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
// 只有操作成功时才取消选择并更新UI
|
||||||
|
console.log('操作成功,取消选择并更新UI');
|
||||||
|
this.deselectItem();
|
||||||
|
this.updateUI();
|
||||||
|
} else {
|
||||||
|
console.log('操作失败,保持选择状态');
|
||||||
|
}
|
||||||
|
} else if (item) {
|
||||||
|
// 如果没有选中的物品,选中点击的物品
|
||||||
|
console.log('没有选中物品,选中点击的物品:', item.name);
|
||||||
|
this.selectItem(item);
|
||||||
|
} else {
|
||||||
|
console.log('点击空位,没有选中物品,无操作');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 单元格悬停事件
|
||||||
|
* @param {number} x - 单元格X坐标
|
||||||
|
* @param {number} y - 单元格Y坐标
|
||||||
|
*/
|
||||||
|
onCellHovered(x, y) {
|
||||||
|
this.hoveredCell = { x, y };
|
||||||
|
|
||||||
|
// 高亮显示悬停的单元格
|
||||||
|
this.gridCells[y][x].setFillStyle(0x888888, 0.7);
|
||||||
|
|
||||||
|
// 显示物品信息
|
||||||
|
const item = this.inventory.getItemAt(x, y);
|
||||||
|
if (item) {
|
||||||
|
this.showItemInfo(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 单元格取消悬停事件
|
||||||
|
*/
|
||||||
|
onCellUnhovered() {
|
||||||
|
if (this.hoveredCell) {
|
||||||
|
// 恢复单元格颜色
|
||||||
|
const { x, y } = this.hoveredCell;
|
||||||
|
this.gridCells[y][x].setFillStyle(0x666666, 0.5);
|
||||||
|
this.hoveredCell = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 隐藏物品信息
|
||||||
|
this.hideItemInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 选择物品
|
||||||
|
* @param {Object} item - 要选择的物品
|
||||||
|
*/
|
||||||
|
selectItem(item) {
|
||||||
|
this.selectedItem = item;
|
||||||
|
|
||||||
|
// 高亮显示物品边框,不改变单元格颜色
|
||||||
|
const spriteObj = this.itemSprites.get(item.id);
|
||||||
|
if (spriteObj) {
|
||||||
|
spriteObj.border.setStrokeStyle(2, 0xffff00, 1); // 黄色边框表示选中
|
||||||
|
}
|
||||||
|
console.log('选中物品:', item.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 取消选择物品
|
||||||
|
*/
|
||||||
|
deselectItem() {
|
||||||
|
if (!this.selectedItem) return;
|
||||||
|
|
||||||
|
// 恢复物品边框颜色,不改变单元格颜色
|
||||||
|
const spriteObj = this.itemSprites.get(this.selectedItem.id);
|
||||||
|
if (spriteObj) {
|
||||||
|
spriteObj.border.setStrokeStyle(1, 0xffffff, 1); // 恢复白色边框
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('取消选择物品:', this.selectedItem.name);
|
||||||
|
this.selectedItem = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 显示物品信息
|
||||||
|
* @param {Object} item - 物品
|
||||||
|
*/
|
||||||
|
showItemInfo(item) {
|
||||||
|
// 创建物品信息面板
|
||||||
|
this.itemInfoPanel = this.scene.add.rectangle(0, 0, 200, 100, 0x000000, 0.8);
|
||||||
|
this.itemInfoPanel.setOrigin(0, 0);
|
||||||
|
this.itemInfoPanel.setStrokeStyle(1, 0xffffff);
|
||||||
|
this.itemInfoPanel.setPosition(
|
||||||
|
this.container.x + this.inventory.width * this.cellSize + 50,
|
||||||
|
this.container.y + this.inventory.height * this.cellSize + 20
|
||||||
|
);
|
||||||
|
|
||||||
|
// 阻止事件冒泡,防止点击物品信息面板时触发角色移动
|
||||||
|
this.itemInfoPanel.setInteractive();
|
||||||
|
this.itemInfoPanel.on('pointerdown', (pointer) => {
|
||||||
|
return false; // 阻止事件冒泡
|
||||||
|
});
|
||||||
|
|
||||||
|
// 文本本身不需要交互,只需要面板阻止事件即可
|
||||||
|
// 如果需要文本也阻止事件,可以添加setInteractive和事件处理
|
||||||
|
this.itemInfoPanel.setScrollFactor(0); // 固定在屏幕上
|
||||||
|
|
||||||
|
// 添加物品信息文本
|
||||||
|
this.itemInfoText = this.scene.add.text(
|
||||||
|
this.itemInfoPanel.x + 10,
|
||||||
|
this.itemInfoPanel.y + 10,
|
||||||
|
`${item.name}\n${item.description}\n重量: ${item.weight}\n价值: ${item.value}`,
|
||||||
|
{ font: '12px Arial', fill: '#ffffff', wordWrap: { width: 180 } }
|
||||||
|
);
|
||||||
|
this.itemInfoText.setScrollFactor(0); // 固定在屏幕上
|
||||||
|
|
||||||
|
this.container.add(this.itemInfoPanel);
|
||||||
|
this.container.add(this.itemInfoText);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 隐藏物品信息
|
||||||
|
*/
|
||||||
|
hideItemInfo() {
|
||||||
|
if (this.itemInfoPanel) {
|
||||||
|
this.itemInfoPanel.destroy();
|
||||||
|
this.itemInfoText.destroy();
|
||||||
|
this.itemInfoPanel = null;
|
||||||
|
this.itemInfoText = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新UI显示
|
||||||
|
*/
|
||||||
|
updateUI() {
|
||||||
|
// 移除所有物品精灵和边框
|
||||||
|
this.itemSprites.forEach(spriteObj => {
|
||||||
|
spriteObj.sprite.destroy();
|
||||||
|
spriteObj.border.destroy();
|
||||||
|
});
|
||||||
|
this.itemSprites.clear();
|
||||||
|
|
||||||
|
// 重新添加所有物品精灵
|
||||||
|
const items = this.inventory.getAllItems();
|
||||||
|
if (items instanceof Map) {
|
||||||
|
// 如果是Map对象,使用Map的forEach方法
|
||||||
|
items.forEach(item => {
|
||||||
|
this.addItemSprite(item);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// 如果是数组,使用数组的forEach方法
|
||||||
|
items.forEach(item => {
|
||||||
|
this.addItemSprite(item);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新统计信息
|
||||||
|
this.updateStats();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加物品精灵
|
||||||
|
* @param {Object} item - 物品
|
||||||
|
*/
|
||||||
|
addItemSprite(item) {
|
||||||
|
const { x, y } = item.position;
|
||||||
|
const { width, height } = item.gridSize;
|
||||||
|
|
||||||
|
// 计算精灵位置和大小
|
||||||
|
const spriteX = 10 + x * this.cellSize;
|
||||||
|
const spriteY = 10 + y * this.cellSize;
|
||||||
|
const spriteWidth = width * this.cellSize;
|
||||||
|
const spriteHeight = height * this.cellSize;
|
||||||
|
|
||||||
|
// 创建精灵 - 使用物品类型ID而不是唯一ID,因为纹理是用物品类型ID创建的
|
||||||
|
const sprite = this.scene.add.sprite(spriteX, spriteY, item.itemTypeId || item.id);
|
||||||
|
sprite.setOrigin(0, 0);
|
||||||
|
sprite.setDisplaySize(spriteWidth, spriteHeight);
|
||||||
|
sprite.setScrollFactor(0); // 固定在屏幕上
|
||||||
|
sprite.setDepth(1); // 确保精灵在单元格之上
|
||||||
|
|
||||||
|
// 添加边框
|
||||||
|
const border = this.scene.add.rectangle(spriteX, spriteY, spriteWidth, spriteHeight, 0xffffff, 0);
|
||||||
|
border.setOrigin(0, 0);
|
||||||
|
border.setStrokeStyle(1, 0xffffff);
|
||||||
|
border.setScrollFactor(0); // 固定在屏幕上
|
||||||
|
border.setDepth(2); // 确保边框在精灵之上
|
||||||
|
|
||||||
|
// 处理物品精灵的点击事件
|
||||||
|
sprite.setInteractive();
|
||||||
|
sprite.on('pointerdown', (pointer) => {
|
||||||
|
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);
|
||||||
|
this.itemSprites.set(item.id, { sprite, border });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 显示背包
|
||||||
|
*/
|
||||||
|
show() {
|
||||||
|
this.container.setVisible(true);
|
||||||
|
this.isOpen = true;
|
||||||
|
this.updateUI();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 隐藏背包
|
||||||
|
*/
|
||||||
|
hide() {
|
||||||
|
this.container.setVisible(false);
|
||||||
|
this.isOpen = false;
|
||||||
|
this.deselectItem();
|
||||||
|
this.hideItemInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 切换背包显示
|
||||||
|
*/
|
||||||
|
toggle() {
|
||||||
|
if (this.isOpen) {
|
||||||
|
this.hide();
|
||||||
|
} else {
|
||||||
|
this.show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加物品到背包并更新UI
|
||||||
|
* @param {Object} item - 要添加的物品
|
||||||
|
* @returns {boolean} 是否添加成功
|
||||||
|
*/
|
||||||
|
addItem(item) {
|
||||||
|
const success = this.inventory.addItem(item);
|
||||||
|
if (success) {
|
||||||
|
this.updateUI();
|
||||||
|
}
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从背包中移除物品并更新UI
|
||||||
|
* @param {string} itemId - 物品ID
|
||||||
|
* @returns {Object|null} 移除的物品
|
||||||
|
*/
|
||||||
|
removeItem(itemId) {
|
||||||
|
const item = this.inventory.removeItem(itemId);
|
||||||
|
if (item) {
|
||||||
|
this.updateUI();
|
||||||
|
}
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 销毁背包UI
|
||||||
|
*/
|
||||||
|
destroy() {
|
||||||
|
this.container.destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user