Unity笔记:数据持久化的几种方式
正文
主要方法:
- ScriptableObject
- PlayerPrefs
- JSON
- XML
- 数据库(如Sqlite)
1. PlayerPerfs
PlayerPrefs 存储的数据是全局共享的,它们存储在用户设备的本地存储中,并且可以被应用程序的所有部分访问。这意味着,无论在哪个场景、哪个脚本中,只要是同一个应用程序中的代码,都可以读取和修改 PlayerPrefs 中的数据。
这意味着耦合性的增加、安全性的降低。它适合存储少量的基本数据(比如玩家的偏好设置、游戏设置、游戏进度等),但不适合存储大量或复杂的数据结构。
注意:
- 每次Set完数据要调用
PlayerPrefs.Save()
把数据写入磁盘。 - Get有两个参数,第一个是键名,第二个是没找到时传入的默认值。
int highScore = 1000;
PlayerPrefs.SetInt("HighScore", highScore);
// 记得保存
PlayerPrefs.Save();// 没找到就返回3
int score = PlayerPrefs.GetInt("Score", 3);
2. ScriptableObject
ScriptableObject的值在播放模式之后不会恢复原样,会保留修改
可以用于只读和读写两种数据,不过原则上还是只用于只读数据。
ScriptableObject 并不依赖于游戏对象(GameObject),也不受场景加载和卸载的影响。它的生命周期是由 Unity 引擎管理的。
使用流程:
- 派生自ScriptableObject创建基类
- 实例化后由编辑器对实例进行配置
- 其他C#脚本使用某个实例
- 合理使用
OnValidate()
方法(这是ScriptableObject中的值更改时触发的事件,但是仅限在编辑器使用)
下述代码在数值发生变化时触发定义的valueChanged事件。注意需要在其他脚本中向 valueChanged 事件添加侦听器才能响应
// 代码源自参考链接2
using UnityEngine;
using UnityEngine.Events;[CreateAssetMenu(fileName = "NewWeapon", menuName = "Game/Weapon")]
public class WeaponScriptableObject : ScriptableObject
{public string weaponName;public int damage;public Sprite icon;[SerializeField, Range(0, 100)]private int maxHealth;// Define a UnityEvent with no argumentspublic UnityEvent valueChanged;#if UNITY_EDITORprivate void OnValidate(){// This method is called in the Unity Editor whenever a value is changed.// Invoke the UnityEvent when values change.if (UnityEditor.EditorApplication.isPlaying){valueChanged.Invoke(); // Fire the UnityEvent}}
#endif
}
这里要注意的是类似于[CreateAssetMenu(fileName = "mySharedData", menuName = "SharedData/MySharedData", order = 1)]
这样的东西,意思是:
- fileName:新创建实例的默认文件名为"mySharedData"。
- menuName:在“Assets/Create”菜单中显示的类型条目名称为"SharedData"下的"MySharedData"。
- order:菜单项在“Assets/Create”菜单中的位置为1(显示靠前的优先级)
由于SO尽量存储运行时不更改的数据,所以要修改当前的生命值会考虑如下方法。此处PlayerXP实际上是个引用,在Unity中,当一个类的成员是另一个类的实例时,默认情况下它就是引用类型,不需要额外的标记(我是对比着[SerializeReference]
来看,见下文 )
public class PlayerXP {public int XP = 100;
}public class PlayerData : ScriptableObject {public PlayerXP PlayerXP;
}
PlayerData playerData = GetComponent<PlayerData>();
playerData.PlayerXP.XP = 200;
注:引用类型的序列化通常会占用更多的存储空间和加载时间(性能降低)
3. 序列化:Json、XML与二进制
using UnityEngine;[System.Serializable]
public class PlayerData
{public string playerName;public int playerScore;
}
关于序列化:
- 必须是
public
类,其中的字段或者属性必须是可序列化的 - 类中的方法不会被序列化
- 构造函数不可以被序列化
不可以序列化的数据类型:
- 静态类或静态成员(Static Members)
- 委托(Delegate)
- 事件(Event)
- 指针(Pointer)
- 索引器(Indexers)
静态成员是属于整个类的,但是序列化和反序列化是构造一个类的实例的
// 保存
string json = JsonUtility.ToJson(sourceObject);
System.IO.File.WriteAllText("playerData.json", json);
// 读取
string json = System.IO.File.ReadAllText("playerData.json");
SomeClass loadedPlayer = JsonUtility.FromJson<SomeClass>(json);
json是字符串文本,XML是标记语言(本身还是文本),二进制就是01序列。二进制在数据存储和传输的效率、紧凑性和速度上占有优势(但丧失了内容的可读性)
二进制有很多种方案:
- protobuf (Protocol Buffers,谷歌研发的一种二进制序列化格式)
- MessagePack
我们可以在官方仓库找到使用说明Github - MessagePack-CSharp
注:几个含Serialize的相关属性
[Serializable]
是一个 C# 中的特性,它告诉编译器这个类可以被序列化。[SerializeReference]
是 Unity 2019.3 引入的新特性,用于处理多态对象的序列化,使得可以在 Inspector 窗口中为该字段分配任意继承自同一父类的对象。[SerializeField]
(这个是把Private变量暴露到Inspector中的,跟上面那俩没关系)
注:标记为 [Serializable]
的类需要自行确保其内容是可序列化的。该标记的作用仅是告诉编译器这个类可以被序列化,但是不对内容做任何保证,如果存在不可被序列化的字段则会被忽略(不会引起报错)。此外还要避免类中存在循环引用(例如类 A 包含类 B 的实例,而类 B 又包含类 A 的实例)
// example
using UnityEngine;
using System;[Serializable]
public class Shape
{public float area;public virtual void Draw() { }
}[Serializable]
public class Circle : Shape
{public float radius;public override void Draw(){Debug.Log("Drawing a circle");}
}[Serializable]
public class Rectangle : Shape
{public float width;public float height;public override void Draw(){Debug.Log("Drawing a rectangle");}
}public class ShapeHolder : MonoBehaviour
{[SerializeReference]public Shape shape;
}
4. 数据库
数据库通常用于存储大量结构化数据,例如用户信息、游戏配置、游戏关卡数据、成就和统计信息等。相较于ScriptableObject、PlayerPrefs、JSON和XML文件,数据库的优势在于能够更灵活地管理和查询大量数据,并支持复杂的数据结构和关联查询。
数据库的优势包括:
- 数据结构灵活:数据库可以存储和管理复杂的数据结构,支持表与表之间的关联,适用于需要大量结构化数据的场景。
- 查询功能强大:数据库引擎提供强大的查询功能,可以进行复杂的数据筛选、排序和统计分析。
- 数据持久化:数据库中的数据可以持久保存,不受应用程序生命周期的影响,适用于长期存储和管理数据。
- 并发处理:数据库引擎通常支持并发处理,能够处理多个用户同时对数据进行读写的情况。
- 数据安全性:数据库可以提供数据加密、权限控制等安全功能,保护数据不被未授权访问。
参考与进一步阅读
PavCreations - Data persistence or how to save / load game data in Unity
Medium - A Beginner’s Guide to Storing and Retrieving Data in Unity
Medium - “How to Harness the Power of Scriptable Objects in Unity”
CSDN - 个人技术总结——Unity3D ScriptableObject实现多存档
CSDN - 【图文详解】Unity存储游戏数据的几种方法
相关文章:
Unity笔记:数据持久化的几种方式
正文 主要方法: ScriptableObjectPlayerPrefsJSONXML数据库(如Sqlite) 1. PlayerPerfs PlayerPrefs 存储的数据是全局共享的,它们存储在用户设备的本地存储中,并且可以被应用程序的所有部分访问。这意味着…...

MySQL 基础知识(八)之用户权限管理
目录 1 MySQL 权限管理概念 2 用户管理 2.1 创建用户 2.2 查看当前登录用户 2.3 修改用户名 2.4 删除用户 3 授予权限 3.1 授予用户管理员权限 3.2 授予用户数据库权限 3.3 授予用户表权限 3.4 授予用户列权限 4 查询权限 5 回收权限 1 MySQL 权限管理概念 关于 M…...

QT编写工具基本流程(自用)
以后有人让你写工具的时候,可以方便用这个模版及时提高工作效率,可以争取早点下班。包含库目录,头文件目录,输出目录以及翻译和部署,基本上都全了,也可以做收藏用用。 文章目录 1、创建项目Dialog Widget都…...

代码随想录算法训练营第三六天 | 无重叠区间、划分字母区间、合并区间
目录 无重叠区间划分字母区间合并区间 LeetCode 435. 无重叠区间 LeetCode 763.划分字母区间 LeetCode 56. 合并区间 无重叠区间 给定一个区间的集合 intervals ,其中 intervals[i] [starti, endi] 。返回 需要移除区间的最小数量,使剩余区间互不重叠…...

DP读书:《openEuler操作系统》(十)套接字 Socket 数据传输的基本模型
10min速通Socket 套接字简介数据传输基本模型1.TCP/IP模型2.UDP模型 套接字类型套接字(Socket)编程Socket 的连接1.连接概述(1)基本概念(2)连接状态(3)连接队列 2.建立连接3.关闭连接 socket 编程接口介绍数据的传输1. 阻塞与非阻塞2. I/O复用 数据的传输…...

抓住母亲节销售机会:Shopee 平台选品策略大揭秘
母亲节,作为一个重要的购物节日,为卖家带来了巨大的销售机会。在Shopee这样的电商平台上,如何通过有效的选品策略吸引消费者、提高销量呢?下面将介绍一些关键策略,帮助卖家在母亲节期间实现销售突破。 先给大家推荐一…...

Mysql如何优化数据查询方案
mysql做读写分离 读写分离是提高mysql并发的首选方案。 Mysql主从复制的原理 mysql的主从复制依赖于binlog,也就是记录mysql上的所有变化并以二进制的形式保存在磁盘上,复制的过程就是将binlog中的数据从主库传输到从库上。 主从复制过程详细分为3个阶段…...

SwiftUI 更自然地向自定义视图传递参数的“另类”方式
概览 在 SwiftUI 中,正是自定义视图让我们的 App 变得与众不同!然而,除了传统的视图接口定义方式以外,我们其实还可以有更“银杏化”的选择。 如上图所示:对于 SubView 子视图所需的参数我们一开始并没有操之过急&…...

Word第一课
文章目录 1. 文件格式1.1 如何显示文件扩展名1.2 Word文档格式的演变1.3 常见的Word文档格式 3. 文档属性理解文档属性查看文档属性 1. 文件格式 1.1 如何显示文件扩展名 文档格式指的是文件的扩展名,例如下图 对于该文件,.docx就是文件扩展名&#x…...

【Vue3】路由传参的几种方式
路由导航有两种方式,分别是:声明式导航 和 编程式导航 参数分为query参数和params参数两种 声明式导航 query参数 一、路径字符串拼接(不推荐) 1.传参 在路由路径后直接拼接?参数名:参数值 ,多组参数间使用&分隔。 <RouterLink …...
突破编程_C++_面试(高级特性(1))
面试题1:什么是线程以及它在并发编程中的作用是什么 线程( Thread )是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进…...

django请求生命周期流程图,路由匹配,路由有名无名反向解析,路由分发,名称空间
django请求生命周期流程图 浏览器发起请求。 先经过网关接口,Django自带的是wsgiref,请求来的时候解析封装,响应走的时候打包处理,这个wsgiref模块本身能够支持的并发量很少,最多1000左右,上线之后会换成u…...
@ 代码随想录算法训练营第8周(C语言)|Day54(动态规划)
代码随想录算法训练营第8周(C语言)|Day54(动态规划) Day53、动态规划(包含题目 ● 123.买卖股票的最佳时机III ● 188.买卖股票的最佳时机IV ) 123.买卖股票的最佳时机III 题目描述 给定一个数组&#…...
Flask 学习100-Flask-SocketIO 结合 xterm.js 实现网页版Xshell
前言 xterm.js 是一个使用 TypeScript 编写的前端终端组件,可以直接在浏览器中实现一个命令行终端应用。 可以实现 web-terminal 功能,类似于Xshell 操作服务器。 Flask-SocketIO 快速入门与使用基础参考前面这篇https://www.cnblogs.com/yoyoketang/p/18022139 前后端交互…...

Springboot AOP开发
Springboot AOP开发 一 AOP概述 AOP,即面向切面编程,简言之,面向方法编程。 针对方法,在方法的执行前或执行后使用,用于增强方法,或拓展。 二 AOP开发 1.引入 spring-boot-starter-aop 在SpringBoot项…...

office的excel中使用,告诉我详细的解决方案,如何变成转化为金额格式
在Office的Excel中,如果你想将名为"MEREFIELD"的公式结果转换为金额格式,你可以遵循以下详细步骤来实现: 书写MEREFIELD公式: 首先,在Excel中输入或确认你的MEREFIELD公式。例如,假设这个公式是用…...

灾后重建中GIS技术的关键作用与案例分析
地质灾害是指全球地壳自然地质演化过程中,由于地球内动力、外动力或者人为地质动力作用下导致的自然地质和人类的自然灾害突发事件。由于降水、地震等自然作用下,地质灾害在世界范围内频繁发生。我国除滑坡灾害外,还包括崩塌、泥石流、地面沉…...

java环境安装
java环境安装 一、官网下载: jdk,下载jdk,解压到D:\JAVA\Java\jdk目录下。 二、配置: 配置环境变量 鼠标右键我的电脑->属性->高级系统设置->环境变量->系统变量新建变量名JAVA_HOME,变量值为刚才解压的…...

如何在iStoreOS软路由系统中安装cpolar实现公网远程本地电脑桌面
文章目录 简介一、配置远程桌面公网地址二、家中使用永久固定地址 访问公司电脑**具体操作方法是:** 简介 软路由是PC的硬件加上路由系统来实现路由器的功能,也可以说是使用软件达成路由功能的路由器。 使用软路由控制局域网内计算机的好处:…...

appium实现自动化测试原理
目录 1、Appium原理 1.1、Android Appium原理图文解析 1.1.2、原理详解 1.1.2.1、脚本端 1.1.2.2、appium-server 1.1.2.3、中间件bootstrap.jar 1.1.2.4、驱动引擎uiautomator 1.2、 IOS Appium原理 1、Appium原理 1.1、Android Appium原理图文解析 执行测试脚本全过…...

MPNet:旋转机械轻量化故障诊断模型详解python代码复现
目录 一、问题背景与挑战 二、MPNet核心架构 2.1 多分支特征融合模块(MBFM) 2.2 残差注意力金字塔模块(RAPM) 2.2.1 空间金字塔注意力(SPA) 2.2.2 金字塔残差块(PRBlock) 2.3 分类器设计 三、关键技术突破 3.1 多尺度特征融合 3.2 轻量化设计策略 3.3 抗噪声…...

前端导出带有合并单元格的列表
// 导出async function exportExcel(fileName "共识调整.xlsx") {// 所有数据const exportData await getAllMainData();// 表头内容let fitstTitleList [];const secondTitleList [];allColumns.value.forEach(column > {if (!column.children) {fitstTitleL…...

Opencv中的addweighted函数
一.addweighted函数作用 addweighted()是OpenCV库中用于图像处理的函数,主要功能是将两个输入图像(尺寸和类型相同)按照指定的权重进行加权叠加(图像融合),并添加一个标量值&#x…...
在 Nginx Stream 层“改写”MQTT ngx_stream_mqtt_filter_module
1、为什么要修改 CONNECT 报文? 多租户隔离:自动为接入设备追加租户前缀,后端按 ClientID 拆分队列。零代码鉴权:将入站用户名替换为 OAuth Access-Token,后端 Broker 统一校验。灰度发布:根据 IP/地理位写…...
c++ 面试题(1)-----深度优先搜索(DFS)实现
操作系统:ubuntu22.04 IDE:Visual Studio Code 编程语言:C11 题目描述 地上有一个 m 行 n 列的方格,从坐标 [0,0] 起始。一个机器人可以从某一格移动到上下左右四个格子,但不能进入行坐标和列坐标的数位之和大于 k 的格子。 例…...
【AI学习】三、AI算法中的向量
在人工智能(AI)算法中,向量(Vector)是一种将现实世界中的数据(如图像、文本、音频等)转化为计算机可处理的数值型特征表示的工具。它是连接人类认知(如语义、视觉特征)与…...
Swagger和OpenApi的前世今生
Swagger与OpenAPI的关系演进是API标准化进程中的重要篇章,二者共同塑造了现代RESTful API的开发范式。 本期就扒一扒其技术演进的关键节点与核心逻辑: 🔄 一、起源与初创期:Swagger的诞生(2010-2014) 核心…...
Hive 存储格式深度解析:从 TextFile 到 ORC,如何选对数据存储方案?
在大数据处理领域,Hive 作为 Hadoop 生态中重要的数据仓库工具,其存储格式的选择直接影响数据存储成本、查询效率和计算资源消耗。面对 TextFile、SequenceFile、Parquet、RCFile、ORC 等多种存储格式,很多开发者常常陷入选择困境。本文将从底…...

HDFS分布式存储 zookeeper
hadoop介绍 狭义上hadoop是指apache的一款开源软件 用java语言实现开源框架,允许使用简单的变成模型跨计算机对大型集群进行分布式处理(1.海量的数据存储 2.海量数据的计算)Hadoop核心组件 hdfs(分布式文件存储系统)&a…...
Caliper 配置文件解析:fisco-bcos.json
config.yaml 文件 config.yaml 是 Caliper 的主配置文件,通常包含以下内容: test:name: fisco-bcos-test # 测试名称description: Performance test of FISCO-BCOS # 测试描述workers:type: local # 工作进程类型number: 5 # 工作进程数量monitor:type: - docker- pro…...