feat(item) 添加物品系统

This commit is contained in:
chicrobot committed 2026-01-04 00:55:01 +08:00
1 parent 606e833f33
commit 2f7210f47e
4 files changed
+1127

No files matched your search

+204
View File
@@ -0,0 +1,204 @@
// 物品类:表示游戏中的单个物品实体
export default class Item extends Phaser.GameObjects.Sprite {
constructor(scene, x, y, itemData) {
// 从itemData中提取精灵属性
const { id, name, description, type, weight, volume, value, sprite, properties } = itemData;
// 创建一个简单的图形纹理作为物品精灵
if (!scene.textures.exists(id)) {
const canvas = document.createElement('canvas');
canvas.width = sprite.width;
canvas.height = sprite.height;
const ctx = canvas.getContext('2d');
// 设置物品颜色
ctx.fillStyle = `#${sprite.color.toString(16).padStart(6, '0')}`;
ctx.fillRect(0, 0, sprite.width, sprite.height);
// 添加物品边框
ctx.strokeStyle = '#000000';
ctx.lineWidth = 1;
ctx.strokeRect(0, 0, sprite.width, sprite.height);
// 添加物品ID的第一个字符作为图标
ctx.fillStyle = '#ffffff';
ctx.font = `${sprite.width * 0.8}px Arial`;
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText(id.charAt(0).toUpperCase(), sprite.width / 2, sprite.height / 2);
// 将canvas添加为纹理
scene.textures.addCanvas(id, canvas);
}
// 调用父类构造函数创建精灵
super(scene, x, y, id);
// 设置物品属性
this.id = id;
this.name = name;
this.description = description;
this.type = type;
this.weight = weight;
this.volume = volume;
this.value = value;
this.properties = properties;
// 添加到场景中
scene.add.existing(this);
scene.physics.add.existing(this, true); // 静态物体,不响应物理碰撞
// 设置物品的碰撞体
this.setSize(sprite.width, sprite.height);
// 设置物品的交互性
this.setInteractive({
useHandCursor: true
});
// 绑定点击事件
this.on('pointerdown', () => {
// 添加视觉反馈:缩放物品
this.scene.tweens.add({
targets: this,
scale: { from: 1, to: 1.2, duration: 100, yoyo: true, repeat: 1 }
});
this.onItemClicked();
});
}
/**
* 检查单位是否在交互距离内
* @param {Object} unit - 要检查的单位
* @returns {boolean} - 是否在交互距离内
*/
isInInteractionRange(unit) {
const maxDistance = 50; // 最大交互距离
const dx = this.x - unit.x;
const dy = this.y - unit.y;
const distance = Math.sqrt(dx * dx + dy * dy);
return distance <= maxDistance;
}
/**
* 物品被点击时的处理函数
*/
onItemClicked() {
// 触发物品点击事件
this.scene.events.emit('item-clicked', this);
}
/**
* 拾取物品
* @param {Object} user - 拾取物品的单位
*/
pickup(user) {
console.log(`${user.name} 拾取了 ${this.name}`);
// 这里可以添加物品到背包的逻辑
// 暂时简单移除物品
this.destroy();
}
/**
* 使用物品
* @param {Object} user - 使用物品的单位
*/
use(user) {
// 根据物品类型和属性执行不同的使用逻辑
switch (this.type) {
case 'food':
this.useFood(user);
break;
case 'medical':
this.useMedical(user);
break;
case 'tool':
this.useTool(user);
break;
case 'weapon':
this.equipWeapon(user);
break;
default:
console.log(`无法使用物品:${this.name}`);
}
}
/**
* 使用食物物品
* @param {Object} user - 使用物品的单位
*/
useFood(user) {
if (this.properties.drinkable) {
// 处理饮水逻辑
console.log(`${user.name} 喝了 ${this.name}`);
if (user.hydration !== undefined) {
user.hydration = Math.min(100, user.hydration + this.properties.hydration);
}
} else if (this.properties.edible) {
// 处理进食逻辑
console.log(`${user.name} 吃了 ${this.name}`);
if (user.nutrition !== undefined) {
user.nutrition = Math.min(100, user.nutrition + this.properties.nutrition);
}
if (user.hydration !== undefined) {
user.hydration = Math.min(100, user.hydration + (this.properties.hydration || 0));
}
}
// 使用后移除物品
this.destroy();
}
/**
* 使用医疗物品
* @param {Object} user - 使用物品的单位
*/
useMedical(user) {
if (this.properties.heal_amount && user.health !== undefined) {
console.log(`${user.name} 使用了 ${this.name}`);
user.health = Math.min(100, user.health + this.properties.heal_amount);
// 使用后移除物品
this.destroy();
}
}
/**
* 使用工具物品
* @param {Object} user - 使用物品的单位
*/
useTool(user) {
console.log(`${user.name} 使用了工具:${this.name}`);
// 工具使用逻辑可以根据具体工具类型扩展
}
/**
* 装备武器
* @param {Object} user - 装备武器的单位
*/
equipWeapon(user) {
console.log(`${user.name} 装备了武器:${this.name}`);
// 装备武器逻辑
if (user.equipWeapon) {
user.equipWeapon(this);
}
// 装备后可以选择保留物品或移除,这里选择保留
}
/**
* 获取物品的详细信息
* @returns {Object} 物品信息对象
*/
getInfo() {
return {
id: this.id,
name: this.name,
description: this.description,
type: this.type,
weight: this.weight,
volume: this.volume,
value: this.value,
properties: this.properties
};
}
}
+315
View File
@@ -0,0 +1,315 @@
// 物品管理器:负责加载、管理和创建物品
export class ItemManager {
constructor(scene) {
this.scene = scene;
this.itemTemplates = new Map(); // 存储物品模板
this.activeItems = new Set(); // 存储当前场景中的物品实例
this.isDataLoaded = false; // 标记物品数据是否已加载
this.loadCallbacks = []; // 存储数据加载完成后的回调函数
// 加载物品数据
this.loadItemData();
}
/**
* 注册数据加载完成回调
* @param {Function} callback - 回调函数
*/
onDataLoaded(callback) {
if (this.isDataLoaded) {
callback();
} else {
this.loadCallbacks.push(callback);
}
}
/**
* 加载物品数据
*/
async loadItemData() {
try {
// 从JSON文件加载物品数据
const response = await fetch('./data/items.json');
const data = await response.json();
// 存储物品模板
data.items.forEach(itemData => {
this.itemTemplates.set(itemData.id, itemData);
});
console.log(`成功加载 ${this.itemTemplates.size} 种物品模板`);
// 设置数据已加载标记
this.isDataLoaded = true;
// 调用所有数据加载完成回调
this.loadCallbacks.forEach(callback => callback());
this.loadCallbacks = [];
} catch (error) {
console.error('加载物品数据失败:', error);
// 加载失败时,使用默认物品数据
this.loadDefaultItemData();
}
}
/**
* 加载默认物品数据(当JSON文件加载失败时使用)
*/
loadDefaultItemData() {
const defaultItems = [
{
id: "water_bottle",
name: "瓶装水",
description: "一瓶干净的饮用水,可以解渴。",
type: "food",
weight: 1.0,
volume: 1.0,
value: 5,
sprite: {
width: 16,
height: 16,
color: 0x0000ff
},
properties: {
drinkable: true,
nutrition: 0,
hydration: 50
}
},
{
id: "canned_food",
name: "罐头食品",
description: "一罐保质期很长的食品,可以提供营养。",
type: "food",
weight: 1.5,
volume: 1.0,
value: 10,
sprite: {
width: 16,
height: 16,
color: 0xff0000
},
properties: {
edible: true,
nutrition: 100,
hydration: 20
}
},
{
id: "crowbar",
name: "撬棍",
description: "多功能工具,可以撬开门锁或作为武器。",
type: "tool",
weight: 3.0,
volume: 2.0,
value: 20,
sprite: {
width: 16,
height: 16,
color: 0x808080
},
properties: {
tool_quality: 2,
weapon_damage: 15
}
},
{
id: "first_aid_kit",
name: "急救包",
description: "包含基本医疗用品,可以治疗伤口。",
type: "medical",
weight: 1.0,
volume: 1.5,
value: 30,
sprite: {
width: 16,
height: 16,
color: 0xffffff
},
properties: {
heal_amount: 50,
usable: true
}
},
{
id: "flashlight",
name: "手电筒",
description: "可以在黑暗中提供照明。",
type: "tool",
weight: 0.5,
volume: 0.5,
value: 15,
sprite: {
width: 16,
height: 16,
color: 0xffff00
},
properties: {
light_range: 10,
battery_life: 100
}
},
{
id: "knife",
name: "小刀",
description: "一把锋利的小刀,可以用作工具或武器。",
type: "weapon",
weight: 0.8,
volume: 1.0,
value: 12,
sprite: {
width: 16,
height: 16,
color: 0x404040
},
properties: {
weapon_damage: 10,
tool_quality: 1
}
}
];
defaultItems.forEach(itemData => {
this.itemTemplates.set(itemData.id, itemData);
});
console.log(`使用默认物品数据,加载了 ${this.itemTemplates.size} 种物品模板`);
// 设置数据已加载标记
this.isDataLoaded = true;
// 调用所有数据加载完成回调
this.loadCallbacks.forEach(callback => callback());
this.loadCallbacks = [];
}
/**
* 根据物品ID创建物品实例
* @param {string} itemId - 物品ID
* @param {number} x - 物品位置X坐标
* @param {number} y - 物品位置Y坐标
* @returns {Item|null} - 创建的物品实例,或null如果物品ID不存在
*/
createItem(itemId, x, y) {
const itemData = this.itemTemplates.get(itemId);
if (!itemData) {
console.error(`未知物品ID: ${itemId}`);
return null;
}
// 动态导入Item类
import('./Item.js').then(({ default: Item }) => {
const item = new Item(this.scene, x, y, itemData);
// 将物品添加到活动物品集合中
this.activeItems.add(item);
// 监听物品销毁事件,从集合中移除
item.on('destroy', () => {
this.activeItems.delete(item);
});
return item;
});
}
/**
* 在随机位置创建随机物品
* @returns {Item|null} - 创建的物品实例,或null如果没有物品模板
*/
createRandomItem() {
if (this.itemTemplates.size === 0) {
return null;
}
// 随机选择一个物品模板
const itemIds = Array.from(this.itemTemplates.keys());
const randomItemId = itemIds[Math.floor(Math.random() * itemIds.length)];
// 随机选择一个位置
const worldBounds = this.scene.physics.world.bounds;
const x = Phaser.Math.Between(worldBounds.x, worldBounds.right);
const y = Phaser.Math.Between(worldBounds.y, worldBounds.bottom);
// 创建物品
return this.createItem(randomItemId, x, y);
}
/**
* 根据物品类型创建物品
* @param {string} type - 物品类型
* @param {number} x - 物品位置X坐标
* @param {number} y - 物品位置Y坐标
* @returns {Item|null} - 创建的物品实例,或null如果没有对应类型的物品
*/
createItemByType(type, x, y) {
// 筛选出指定类型的物品
const typeItems = Array.from(this.itemTemplates.values()).filter(item => item.type === type);
if (typeItems.length === 0) {
console.error(`没有找到类型为 ${type} 的物品`);
return null;
}
// 随机选择一个该类型的物品
const randomItemData = typeItems[Math.floor(Math.random() * typeItems.length)];
// 动态导入Item类
import('./Item.js').then(({ default: Item }) => {
const item = new Item(this.scene, x, y, randomItemData);
// 将物品添加到活动物品集合中
this.activeItems.add(item);
// 监听物品销毁事件,从集合中移除
item.on('destroy', () => {
this.activeItems.delete(item);
});
return item;
});
}
/**
* 获取物品模板
* @param {string} itemId - 物品ID
* @returns {Object|null} - 物品模板数据,或null如果物品ID不存在
*/
getItemTemplate(itemId) {
return this.itemTemplates.get(itemId) || null;
}
/**
* 获取所有物品模板
* @returns {Map} - 所有物品模板的Map对象
*/
getAllItemTemplates() {
return this.itemTemplates;
}
/**
* 获取当前场景中的所有物品实例
* @returns {Set} - 当前场景中的所有物品实例的Set对象
*/
getActiveItems() {
return this.activeItems;
}
/**
* 清理所有物品实例
*/
clearAllItems() {
this.activeItems.forEach(item => {
item.destroy();
});
this.activeItems.clear();
}
/**
* 销毁物品管理器
*/
destroy() {
this.clearAllItems();
this.itemTemplates.clear();
}
}