1. 引言
- 问题: 如何在 Unity 中存储数据?
- 解决方案: 使用 ScriptableObjects 和 Resources 系统创建一个易于使用和管理的数据库。
- 优势:
- 自动读写数据
- 自动处理资源实例化和更改
- 无需额外插件
- 使用用户定义键进行访问
- 可扩展性强
2. 实现
2.1 项目结构
四个主要的脚本:
- ItemData.cs: 定义了一个 ScriptableObject 类,用于存储物品数据,例如 ID、名称、等级和图标。
- DataManager.cs: 定义了一个抽象类,用于管理数据,包括读取、添加和删除数据项。
- ItemDataManager.cs: 继承自 DataManager 类,并实现了具体的数据管理逻辑,例如从 Resources 文件夹加载物品数据。
- InventoryTestContext.cs: 用于测试数据库功能,打印所有物品信息。
2.2 ScriptableObjects
- ScriptableObjects 用于在 Unity 中存储数据到资源文件夹中,可以通过代码进行读写和修改。
- 使用 [CreateAssetMenu] 属性可以创建自定义菜单项,用于在编辑器中创建 ScriptableObject 对象。
- 警告:不建议在游戏运行时通过代码修改 ScriptableObject 中的字段,可能会导致编辑器中出现奇怪的行为。
2.3 创建对象数据:Scriptable Object Item.cs 脚本
- ItemData 类继承自 ScriptableObject,并定义了四个属性:id、displayName、levelRequired 和 icon。
- 使用 [SerializeField] 属性使属性在编辑器中可见并可编辑。
- 提供了相应的 getter 和 setter 方法。
ItemData.cs:
using System.Globalization;
using UnityEngine;
namespace DatabaseSystem.ScriptableObjects
{
[CreateAssetMenu(menuName = "Demo/Items/Item Data")]
public class ItemData : ScriptableObject
{
#region 变量
[SerializeField] private int id = default;
[SerializeField] private string displayName = default;
[SerializeField] private int levelRequired = default;
[SerializeField] private Sprite icon = default;
public int Id
{
get { return id; }
set { id = value; }
}
public string DisplayName
{
set { displayName = value; }
get { return displayName; }
}
public int LevelRequired
{
get { return levelRequired; }
set { levelRequired = value; }
}
public Sprite Icon
{
get { return icon; }
set { icon = value; }
}
#endregion
}
}
2.4 编写 ItemDataManager 代码
- DataManager 类是一个抽象类,定义了数据管理的基本方法,例如 GetAllDataObjects、GetDataObject、TryPutDataItem 和 TryRemoveItem。
- ItemDataManager 类继承自 DataManager 类,并实现了具体的数据管理逻辑。
- LoadFromResources 方法从 Resources 文件夹加载所有物品数据,并将其存储在字典中。
- Initialize 方法用于初始化数据管理器。
DataManager.cs:
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
namespace DatabaseSystem.Managers
{
// T 表示key类型,D 表示ItemDat的类型
public abstract class DataManager<T, D> : MonoBehaviour
{
#region Variables
protected Dictionary<T, D> dataDictionary = default;
#endregion
#region Public Methods
public Dictionary<T, D> GetAllDataObjects()
{
return dataDictionary.ToDictionary(k => k.Key, v => v.Value);
}
public D GetDataObject(T id)
{
if(dataDictionary.ContainsKey(id))
{
return dataDictionary[id];
}
return default;
}
#endregion
#region Protected Methods
protected virtual bool TryPutDataItem(T id, D data)
{
if (dataDictionary.ContainsKey(id))
{
return false;
}
dataDictionary.Add(id, data);
return true;
}
protected virtual bool TryRemoveItem(T id, D data)
{
if (!dataDictionary.ContainsKey(id))
{
return false;
}
dataDictionary.Remove(id);
return true;
}
#endregion
}
}
ItemDataManager:
using DatabaseSystem.ScriptableObjects;
using System.Collections.Generic;
using UnityEngine;
namespace DatabaseSystem.Managers
{
public class ItemsDataManager : DataManager<int, ItemData>
{
#region Variables
[SerializeField] private string resourcesItemsFolder = default;
#endregion
#region Private Methods
private void LoadFromResources()
{
dataDictionary = new Dictionary<int, ItemData>();
ItemData[] itemsFromResources = Resources.LoadAll<ItemData>(resourcesItemsFolder);
foreach (var itemData in itemsFromResources)
{
TryPutDataItem(itemData.Id, itemData);
}
}
#endregion
#region Public Methods
public void Initialize()
{
LoadFromResources();
}
#endregion
}
}
2.5 在数据清单中使用数据库
- 创建两个空物体,分别为其添加 ItemDataManager.cs 和 InventoryTestContext.cs 组件。
- InventoryTestContext 组件负责初始化 ItemDataManager,并打印所有物品信息。
3. 总结
InventoryTestContext.cs:
- 使用 ScriptableObjects 和 Resources 系统可以轻松创建一个可扩展的数据库系统。
- 这种方法可以用于存储各种类型的数据,简化游戏开发流程。
其他要点
- 警告:序列化成 json 格式时,需要注意字段是否可序列化。
- DataManager 类可以进行扩展,以支持更多类型的数据。