基于SqLite存储的背包系统+场景内拾取物体功能全流程
(从我的CSDN搬运而来,所以有水印)
零、功能分析 对于背包系统来说,我们首先需要实现几个关键的功能:
1.物品数据的持久化存储
2.对于物品的清晰显示
3.对于物品的便捷管理,能够增加删除物品
4.如何在场景中拾取物品
壹、基于SqLite的持久化存储 要实现物品数据的持久存储是背包系统所必须的,你也不想玩家再次打开游戏时发现自己的东西消失不见吧。
在UNITY中,想要持久存储数据有以下几种方式:
1.PlayerPrefs: Unity自带的一种简单的键值存储系统。
2.ScriptableObject: Unity中最灵活的数据管理工具。
3.JSON: 轻量级的数据交换格式。
4.XML:一种可扩展标记语言。
5.数据库:存储大量数据时使用的一种方法。
PlayerPrefs 不太适合存储大量数据,故不使用。
ScriptableObject 虽然能够持久化存储但是只能够在编辑模式当中,打包后其值在每次启动时都会还原成初始值,故不使用。
最终决定采用数据库来存储数据,学习下新内容。
1.下载SqLite查看软件 这里为了方便我选择了Navicat Premium,可以免费试用14天(后记:其实有免费开源的数据库查看软件),对于暂时的使用来说已经足够,我们只在开始的时候需要软件来查看数据
(1)新建db文件,在下载好软件之后,使用软件新建一个db文件,选择Sqlite3
(2)转移db文件到unity中,我将文件放置在了 Assets\StreamingAssets\SqLite\ 之中
2.导入DLL文件 在开始写代码之前我们需要向UNITY中导入以下三个dll文件 Mono.Data.Sqlite.dll 在Unity的Editor安装目录下“ Editor\Data\MonoBleedingEdge\lib\mono\unityjit-win32\Mono.Data.Sqlite.dll”
System.Data.dll 在Unity的Editor安装目录下“ Editor\Data\MonoBleedingEdge\lib\mono\2.0System.Data.dll”
Sqlite3.dll 在Sqlite的官网下载对应的版本即可“ https://www.sqlite.org/download.html ”
注意,如若Mono.Data.Sqlite.dll与UNITY版本不符合会报错Loading assembly failed “Assets/Plugins/Mono.Data.Sqlite.dll,此文件在mono文件夹中有很多个,请更换合适的版本。
3.编写SqLite代码 实现对于Sqlite的更新,插入,删除,查找
定义关键变量 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 private SqliteConnection SqlConnection;private SqliteCommand SqlCommand;private SqliteDataReader SqlDataReader;private string SqlitePath = "URI=file:" + Application.streamingAssetsPath + "/SqLite/GameDataSQLite.db" ; private Hashtable dataHashTable = new Hashtable();
要注意数据库路径需要加上文件的后缀名,笔者在这里耽搁了些时间。
初始化代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public void SqlDataLink () { try { SqlConnection = new SqliteConnection(SqlitePath); SqlConnection.Open(); SqlCommand = SqlConnection.CreateCommand(); } catch (System.Exception e) { #if UNITY_EDITOR Debug.Log(e.ToString()); #endif } }
创建一张表 1 2 3 4 5 6 7 public void SqlCreatTable (){ SqlCommand.CommandText = "CREATE TABLE IF NOT EXISTS ItemSys (id TEXT, number INTEGER)" ; SqlCommand.ExecuteNonQuery(); }
向表中插入数据 1 2 3 4 5 6 public void SqlInsertItemData (string id, int number ){ SqlCommand.CommandText = "INSERT INTO ItemSys (id, number) VALUES (@id, @number)" ; SqlCommand.Parameters.AddWithValue("@id" , id); SqlCommand.Parameters.AddWithValue("@number" , number); }
关闭数据库 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public void SqlClose (){ if (SqlCommand != null ) { SqlCommand.Dispose(); SqlCommand = null ; } if (SqlDataReader != null ) { SqlDataReader.Close(); SqlDataReader = null ; } if (SqlConnection != null ) { SqlConnection.Close(); SqlConnection = null ; } }
测试代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 using System.Text;using UnityEngine;using static Unity.VisualScripting.Dependencies.Sqlite.SQLite3; public class SqLiteManager : MonoBehaviour { private void Start () { SqLite sqlite = gameObject.GetComponent<SqLite>(); sqlite.SqlDataLink(); sqlite.SqlCreatTable(); sqlite.SqlInsertItemData("wp0001" ,12 ); sqlite.SqlClose(); } }
运行一下代码,我们打开Navicat Premium,再次选择新建连接,类型选择现有数据库文件,用户名密码留空。 注意不要直接打开之前未移动的文件,我们更改的是移动到UNITY中的文件。
打开之后我们会发现数据库中成功新建了一张表,同时也成功写入了指定数据。
趁热打铁,完成剩余的部分
删除表中数据 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public void SqlDelete (string tableName, string _KEY, string key ) { StringBuilder stringBuilder =new StringBuilder(); stringBuilder.Append("delete from " ); stringBuilder.Append(tableName); stringBuilder.Append(" where " ); stringBuilder.Append(_KEY); stringBuilder.Append(" = '" ); stringBuilder.Append(key); stringBuilder.Append("'" ); SqlCommand.CommandText= stringBuilder.ToString(); SqlCommand.ExecuteNonQuery(); }
查找表中数据 SQLite 的 SELECT 语句的基本语法如下:
1 SELECT column1, column2, columnN FROM table_name;
在这里,column1, column2…是表的字段,他们的值即是要获取的。如果想获取所有可用的字段,那么可以使用下面的语法:
1 SELECT * FROM table_name;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 public bool SQL_Select (string key, string tableName ){ StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append("select " ); stringBuilder.Append(key); stringBuilder.Append(" from " ); stringBuilder.Append(tableName); try { SqlCommand.CommandText = stringBuilder.ToString(); SqlDataReader = SqlCommand.ExecuteReader(); if (SqlDataReader != null ) { while (SqlDataReader.Read()) { var id = SqlDataReader.GetString(0 ); var number = SqlDataReader.GetInt32(1 ); Debug.LogFormat("id: {0}, number: {1}" , id, number); } return true ; } } catch (System.Exception e) { Debug.LogError(e.ToString()); } return false ; }
运行结果如图:
更新表中数据 UPDATE 查询的基本语法如下:
1 2 3 UPDATE table_name SET column1 = value1, column2 = value2...., columnN = valueN WHERE [condition];
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 public void SqlUpdata (string tableName, string _VALUE_STRING, int value , string _KEY, string key ) { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append("update " ); stringBuilder.Append(tableName); stringBuilder.Append(" set " ); stringBuilder.Append(_VALUE_STRING); stringBuilder.Append("=" ); stringBuilder.Append(value ); stringBuilder.Append(" where " ); stringBuilder.Append(_KEY); stringBuilder.Append("= '" ); stringBuilder.Append(key); stringBuilder.Append("'; " ); SqlCommand.CommandText = stringBuilder.ToString(); #if UNITY_EDITOR SqlDataReader = SqlCommand.ExecuteReader(); Debug.Log(SqlDataReader); #endif }
对此代码的使用范例: 现在建立有表如图,要将wp0034所在行的number值更变为5。
运行以下代码:
1 2 3 4 5 6 7 8 9 10 public class SqLiteManager : MonoBehaviour { private void Start () { SqLite sqlite = gameObject.GetComponent<SqLite>(); sqlite.SqlDataLink(); sqlite.SqlUpdata("ItemSys" , "number" , 5 , "id" , "wp0034" ); sqlite.SqlClose(); } }
再次打开软件查看,数据被成功更改,结果如下:
本段总结 至此,SqLite的基本代码书写完毕,但是使用过于繁琐,不能满足我们的实际需求。 接下来将基于这些代码进行再封装,正式开始背包系统的代码
贰、背包系统基本读写代码 在真正使用当中,我们并不直接对数据库进行读写,而是在游戏加载时将数据库数据搬移到HashTable当中,以加快数据的查找速度。
定义关键变量 在Scripts文件夹中新建一个脚本文件继承SqLite,定义一些变量
1 2 3 4 public class ItemSystem : SqLite { private Hashtable dataHashTable = new Hashtable(); }
读取数据库数据到HashTable 注意,每次读取数据之后要关闭DataReader,不然会报错
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 private void LoadItemData () { SqlDataLink(); if (SQL_Select("*" , "ItemSys" ) == false ) { return ; } SqlDataReader.Close(); SqlCommand.CommandText = "select * from ItemSys" ; SqlDataReader = SqlCommand.ExecuteReader(); while (SqlDataReader.Read()) { string key = SqlDataReader.GetString(0 ); dataHashTable[key] = SqlDataReader.GetInt32(1 ); } SqlDataReader.Close(); SqlClose(); Debug.Log(dataHashTable["wp0001" ]); }
运行之后能看见数据成功导入,数据正常显示了
向背包中加入或是删除物品 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 public void ItemSys_Add (string id,int num ){ dataHashTable[id] = (int )dataHashTable[id] + num; } public bool ItemSys_Del (string id, int num ){ int temp = (int )dataHashTable[id] - num; if (temp > 0 ) { dataHashTable[id] = temp; return true ; } else if (temp == 0 ) { dataHashTable.Remove(id); return true ; } return false ; }
同步数据到数据库 之前的增加删除都是基于哈希表的,我们还需要将变化同步到数据库当中。
由于我的数据量较少,采用完全备份,数据量大可以差异备份,这个我后面再写
1 2 3 4 5 6 7 8 9 10 11 12 void Synchronizedata (){ SqlDataLink(); foreach (string key in dataHashTable.Keys) { SqlInsertItemData(key, (int )dataHashTable[key]); } SqlClose(); }
显示物品信息 在显示信息之前我们先制作一下GUI,使用unity自带的ui组件可以制作一个简单的自适应滚动的物品表。
右键选择UI,新建一个滚动视图,命名为“ItemSysUI”
制作一个物品显示框的IyemSlot预制体(这里我从uintry商店找了个),放在ItemSysUI的Content下
为Content添加Vertical Layout Group组件
测试显示效果如下:
UI准备完毕后开始编写代码:
由于我在数据库中只存放了物品ID和数量,在生成界面时还需要根据ID调用对应的信息。这里部分代码阅读起来可能比较困难,但其实就是从数组中取数据,这样理解就简单很多了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 public void ItemSys_GUI (bool dis_status ){ GameObject gui =GameObject.Find("ItemSysUI" ); GameObject ItemContent = GameObject.Find("ItemContent" ); GameObject itemSlot = Resources.Load<GameObject>("UI/Prefabs/ItemSlot" ); ItemManager itemData = Resources.Load<ItemManager>("DataAssets/Item" ); CreatItemHashTable(); gui.SetActive(dis_status); if (!dis_status) return ; foreach (string key in dataHashTable.Keys) { GameObject slot = Instantiate(itemSlot, transform.position, Quaternion.identity); slot.transform.SetParent(ItemContent.transform); slot.transform.Find("Text/Name" ).GetComponent<Text>().text = itemData.dataArray[(int )array_id[key]].itemName; slot.transform.Find("Text/Description" ).GetComponent<Text>().text = itemData.dataArray[(int )array_id[key]].itemDescription; slot.transform.Find("Icon/Stack" ).GetComponent<Text>().text = dataHashTable[key].ToString(); } }
测试能够正常显示数据库中的数据及根据ID得到的数据,因为至少测试我并没有添加太多的信息,后续还可以指定图片,调用方法等等。
肆、额外功能 一键整理(自动化排布)
物品分类
拾取物品添加到背包中
转轮选取工具
待续…
参考文章: Unity存储游戏数据的几种方法_unity存储数据
Unity数据存储Sqlite的使用
Unity 报错Loading assembly failed “Assets/Plugins/Mono.Data.Sqlite.dll“
SQLite 教程
SQL SqlDataReader是否需要手动关闭和释放