【unity实战】制作unity数据保存和加载系统——小型游戏存储的最优解(包含数据安全处理方案的加密解密)

前言

如何在 Unity 中正确制作一个保存和加载系统,该系统使用JSON 文件来处理保存配置文件,可以保存和加载任何类型对象!标题为什么叫小型游戏存储功能呢?因为该存储功能可能只适合存储数据比较单一的情况,它非常的方便快捷,且易于使用和理解。但是如果你的游戏要存储的内容有很多,比如很多怪物的状态,很多宝箱的状态,很多物品的状态,那么它可能就不适用了。但是不用担心,后续我还会针对大型游戏,出更加复杂和全面的存储系统,解决存储数据比较多的情况。

存储

新增SaveProfile保存配置文件的泛型类和SaveProfileData抽象基类

[System.Serializable]
// 保存配置文件的泛型类
public sealed class SaveProfile<T> where T : SaveProfileData
{
    // 保存配置文件的名称
    public string name;
    // 实际保存的数据
    public T saveData;

    // 私有的默认构造函数,防止无参数实例化
    private SaveProfile() { }

    // 公共构造函数,用于初始化名称和保存数据
    public SaveProfile(string name, T saveData)
    {
        this.name = name;
        this.saveData = saveData;
    }
}

// 抽象基类,用于保存数据
public abstract record SaveProfileData {}

新增SaveManager,定义读取和存储 删除存档文件方法

using System;
using System.IO;
using UnityEngine;
using Newtonsoft.Json;

public static class SaveManager
{
    // 文件保存的根目录路径
    private static readonly string saveFolder = Application.persistentDataPath + "/GameData";

    // 删除指定存档文件
    public static void Delete(string profileName)
    {
        if (!File.Exists($"{saveFolder}/{profileName}"))
            throw new Exception($"保存配置文件 {profileName} 未找到!");

        Debug.Log($"已成功删除 {saveFolder}/{profileName}");
        File.Delete($"{saveFolder}/{profileName}");
    }

    // 加载指定类型的存档文件
    public static SaveProfile<T> Load<T>(string profileName) where T : SaveProfileData
    {
        if (!File.Exists($"{saveFolder}/{profileName}"))
            throw new Exception($"保存配置文件 {profileName} 未找到!");

        // 读取文件内容为字符串
        var fileContents = File.ReadAllText($"{saveFolder}/{profileName}");
        // TODO:解密

        Debug.Log($"已成功加载 {saveFolder}/{profileName}");

        // 反序列化为指定类型的SaveProfile<T>对象并返回
        return JsonConvert.DeserializeObject<SaveProfile<T>>(fileContents);
    }

    // 保存指定类型的存档数据
    public static void Save<T>(SaveProfile<T> save) where T : SaveProfileData
    {
        if (File.Exists($"{saveFolder}/{save.name}")){
            // throw new Exception($"保存配置文件 {save.name} 未找到!");
            Delete(save.name);
        }

        // 将SaveProfile<T>对象序列化为JSON格式的字符串
        var jsonString = JsonConvert.SerializeObject(save, Formatting.Indented,
            new JsonSerializerSettings { ReferenceLoopHandling = ReferenceLoopHandling.Ignore });
        // TODO:加密

        if (!Directory.Exists(saveFolder))
            Directory.CreateDirectory(saveFolder);

        // 将加密后的jsonString写入文件
        File.WriteAllText($"{saveFolder}/{save.name}", jsonString);
    }
}

使用

新增SaveData.cs,定义需要存储的数据,如果需要其他数据需要再添加

using UnityEngine;

// 玩家保存数据
public record PlayerSaveData : SaveProfileData
{
    // 玩家位置
    public Vector2 position;
    // 成就数组
    public int[] achievements;
}

// 世界保存数据
public record WorldSaveData : SaveProfileData
{
    // 方块二维数组
    public int[,] blocks;
}

新增Player ,保存测试数据和读取

public class Player : MonoBehaviour
{
    void Start()
    {
        Debug.Log(Application.persistentDataPath);

        // 保存玩家数据
        var playerSave = new PlayerSaveData
        {
            position = new Vector2(1f, 1.5f),
            achievements = new[] { 1, 2, 3, 4, 5 }
        };
        var saveProfile = new SaveProfile<PlayerSaveData>("playerSaveData", playerSave);
        SaveManager.Save(saveProfile);

        //保存世界数据
        var worldSave = new WorldSaveData
        {
            blocks =new[,] { {1,1}, {1,2}, {1,3}}        
        };
        var saveProfile2 = new SaveProfile<WorldSaveData>("WorldSaveData", worldSave);
        SaveManager.Save(saveProfile2);
    }

    void Update()
    {
        //读取数据
        if (Input.GetKeyDown(KeyCode.E))
        {
            Vector2 position = SaveManager.Load<PlayerSaveData>("playerSaveData").saveData.position;
            Debug.Log(position);
            transform.position = position;
        }
    }
}

运行之后,可以去查看保存的文件数据
在这里插入图片描述
在这里插入图片描述

运行按E成功加载数据
在这里插入图片描述
看到这里你应该就明白为什么叫小型游戏存储功能了吧!

数据加密

修改SaveManager,添加数据加密内容

using System;
using System.IO;
using UnityEngine;
using Newtonsoft.Json;

public static class SaveManager
{
    // 文件保存的根目录路径
    private static readonly string saveFolder = Application.persistentDataPath + "/GameData";

     // 加密:选择一些用于亦或操作的字符(注意保密)
    public static char[] keyChars = { 'a', 'b', 'c', 'd', 'e' };

    // 删除指定存档文件
    public static void Delete(string profileName)
    {
        if (!File.Exists($"{saveFolder}/{profileName}"))
            throw new Exception($"保存配置文件 {profileName} 未找到!");

        Debug.Log($"已成功删除 {saveFolder}/{profileName}");
        File.Delete($"{saveFolder}/{profileName}");
    }

    // 加载指定类型的存档文件
    public static SaveProfile<T> Load<T>(string profileName) where T : SaveProfileData
    {
        if (!File.Exists($"{saveFolder}/{profileName}"))
            throw new Exception($"保存配置文件 {profileName} 未找到!");

        // 读取文件内容为字符串
        var fileContents = File.ReadAllText($"{saveFolder}/{profileName}");
        // TODO:解密
        fileContents = Decrypt(fileContents);

        Debug.Log($"已成功加载 {saveFolder}/{profileName}");

        // 反序列化为指定类型的SaveProfile<T>对象并返回
        return JsonConvert.DeserializeObject<SaveProfile<T>>(fileContents);
    }

    // 保存指定类型的存档数据
    public static void Save<T>(SaveProfile<T> save) where T : SaveProfileData
    {
        if (File.Exists($"{saveFolder}/{save.name}")){
            // throw new Exception($"保存配置文件 {save.name} 未找到!");
            Delete(save.name);
        }

        // 将SaveProfile<T>对象序列化为JSON格式的字符串
        var jsonString = JsonConvert.SerializeObject(save, Formatting.Indented,
            new JsonSerializerSettings { ReferenceLoopHandling = ReferenceLoopHandling.Ignore });
        // TODO:加密
        jsonString = Encrypt(jsonString);

        if (!Directory.Exists(saveFolder))
            Directory.CreateDirectory(saveFolder);

        // 将加密后的jsonString写入文件
        File.WriteAllText($"{saveFolder}/{save.name}", jsonString);
    }

    // 加密方法
    public static string Encrypt(string data)
    {
        char[] dataChars = data.ToCharArray();
        for (int i = 0; i < dataChars.Length; i++)
        {
            char dataChar = dataChars[i];
            char keyChar = keyChars[i % keyChars.Length];
            // 重点: 通过亦或得到新的字符
            char newChar = (char)(dataChar ^ keyChar);
            dataChars[i] = newChar;
        }
        return new string(dataChars);
    }

    // 解密方法
    public static string Decrypt(string data)
    {
        return Encrypt(data);
    }
}

存储加密内容
在这里插入图片描述
按E读取数据正常
在这里插入图片描述

完结

赠人玫瑰,手有余香!如果文章内容对你有所帮助,请不要吝啬你的点赞评论和关注,以便我第一时间收到反馈,你的每一次支持都是我不断创作的最大动力。当然如果你发现了文章中存在错误或者有更好的解决方法,也欢迎评论私信告诉我哦!

好了,我是向宇,https://xiangyu.blog.csdn.net

一位在小公司默默奋斗的开发者,出于兴趣爱好,最近开始自学unity,闲暇之余,边学习边记录分享,站在巨人的肩膀上,通过学习前辈们的经验总是会给我很多帮助和启发!php是工作,unity是生活!如果你遇到任何问题,也欢迎你评论私信找我, 虽然有些问题我也不一定会,但是我会查阅各方资料,争取给出最好的建议,希望可以帮助更多想学编程的人,共勉~

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/747449.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

udp udpClient 聊天室

简介 1、UDP&#xff08;User Data Protocol&#xff0c;用户数据报协议&#xff09; &#xff08;1&#xff09; UDP是一个非连接的协议&#xff0c;传输数据之前源端和终端不建立连接&#xff0c;当它想传送时就简单地去抓取来自应用程序的数据&#xff0c;并尽可能快地把它…

三大关键技术看RAG如何提升LLM的能力

大语言模型表现出色&#xff0c;但是在处理幻觉、使用过时的知识、进行不透明推理等方面存在挑战。检索增强生成&#xff08;RAG&#xff09;作为一个新兴的解决方案&#xff0c;通过整合外部知识库的数据&#xff0c;提高了模型在知识密集型任务中的准确性和可信度&#xff0c…

数据质量管理-一致性管理

前情提要 根据GB/T 36344-2018《信息技术 数据质量评价指标》的标准文档&#xff0c;当前数据质量评价指标框架中包含6评价指标&#xff0c;在实际的数据治理过程中&#xff0c;存在一个关联性指标。7个指标中存在4个定性指标&#xff0c;3个定量指标&#xff1b; 定性指标&am…

星坤Type-A连接器:创新快充技术,引领电子连接!

快速发展的电子时代&#xff0c;消费者对电子设备的性能和便利性有着更高的要求。特别是在充电和数据传输方面&#xff0c;快充技术和高速传输已成为市场的新宠。中国星坤公司推出的Type-A连接器系列&#xff0c;以其卓越的性能和创新的设计&#xff0c;满足了市场对高效、稳定…

Linux-笔记 全志平台休眠功能初探

前言 全志平台支持的休眠功能主要包括两种模式&#xff1a;休眠模式和待机模式。这两种模式用于降低设备的功耗&#xff0c;并在需要时快速恢复工作状态。由于平台为T113&#xff0c;所以可以很方便的使用RTC来做唤醒源。唤醒源指的是能够让系统从休眠状态恢复到工作状态的信号…

《人人都是产品经理》:项目的坎坷一生

《人人都是产品经理》&#xff1a;项目的坎坷一生 产品VS项目产品经理和项目经理 一切项目从kick off 开始工作量预估Kick Off的大致也就15分钟 写文档咯UML图用例文档UCdemo也得做 需求活在项目中bug等级有多高bug流转过程 以终为始 产品VS项目 项目定义&#xff1a;是只会进…

Python 挖坑式填充Excel模板内容(包括页眉/SheetName/logo)

纵览 Python处理Excel的方式--解压缩方式1、导包2、对模板文件进行解压缩3、对解压缩后文件层级进行介绍4、准备需要载入的数据5、模板挖坑6、运行替换代码7、压缩文件8、生成文件9、完成代码10、可能遇到的问题 结语 Python处理Excel的方式–解压缩方式 在处理Excel中过程中&…

2024-6-26 石群电路-30

2024-6-26&#xff0c;星期三&#xff0c;10:38&#xff0c;天气&#xff1a;雨&#xff0c;心情&#xff1a;晴。今天没有什么事情发生&#xff0c;继续学习&#xff0c;加油&#xff01;&#xff01;&#xff01;&#xff01;&#xff01; 今日观看了石群老师电路课程的视频…

SpringBoot中使用多线程调用异步方法,异步方法有无返回值例子。

快速了解Async注解的用法&#xff0c;包括异步方法无返回值、有返回值&#xff0c;最后总结Async注解失效的几个坑。 在我们的 SpringBoot 应用中&#xff0c;经常会遇到在一个接口中&#xff0c;同时做事情1&#xff0c;事情2&#xff0c;事情3&#xff0c;如果同步执行的话&a…

2024期权交易佣金手续费最低是多少?期权交易有哪些成本?

显性成本 期权交易的显性成本包含期权交易的佣金和交易所费用&#xff0c;分别支付给券商和交易所&#xff0c;统一由券商代收。 佣金 期权佣金是期权交易时支付给券商的费用&#xff0c;佣金通常以交易金额的一定比例计算&#xff0c;可以是固定费用&#xff0c;也可以是滑…

深入理解计算机系统 CSAPP 家庭作业7.12

A:refptr (unsigned)(ADDR(r.symbol) r.addend - refaddr) 0x4004f8 (-4) - 0x4004ea 0xa B:refaddr 0x4004d0 0xa 0x4004da refptr 0x400500 (-4) - 0x4004da 0x22 ​​​​​​​

docker安装与入门使用(适用于小白)

总结&#xff1a;Docker 是一个开源的容器化平台&#xff0c;旨在使开发、部署和运行应用程序的过程更加简单和高效。Docker 使用操作系统级虚拟化在单个主机上运行多个独立的容器。每个容器包含应用程序及其所有依赖项&#xff0c;确保在不同环境中具有一致的运行表现。 下面…

【招聘贴】JAVA后端·唯品会·BASE新加坡

作者|老夏&#xff08;题图&#xff1a;公司业务介绍页&#xff09; “ 请注意&#xff0c;这两个岗是BASE新加坡的&#xff0c;欢迎推荐给身边需要的朋友&#xff08;特别是在新加坡的&#xff09;。” VIP海外业务-产品技术团队&#xff0c;这两个岗位属于后端工程组的岗&…

【ocean】ocnPrin结合getData导出数据

核心就是这一句ocnPrint(?output fout leafValue( getData(“/output” ?result “dc”))) r_list list(4000, 4100, 4200) multi_list list(20,21,22) fout outfile("/home/yourpath/results.txt" "w") foreach(r_value r_listforeach(multi_value …

第11周 多线程接口并行对数据字典的查询优化

第11周 多线程接口并行对数据字典的查询优化 本章概述1. 多线程的初始化方式1.1 简单实现多线程的启动方式(3种)1. 继承Thread实现2. 实现Runnable接口3. 实现callable接口(返回值)1.2 基于线程池实现多线程的启动方式❤❤❤2. 多线程编排工具CompletableFuture2.1 Completable…

onlyoffice实现在单页面加载文档的功能

草图 实现案例的基本原型 这里我们的样式库使用的是Tailwindcss&#xff0c;我们的前端UI组件库使用的是Ant Design Vue。 基本原型是&#xff0c;有个按钮&#xff0c;没有点击按钮的时候&#xff0c;页面显示的时普通的内容。当点击这个按钮的时候&#xff0c;页面加载文档…

UNIAPP编译到微信小程序时,会多一层以组件命名的标签

UNIAPP编译到微信小程序时&#xff0c;会多一层以组件命名的标签 解决方案 可以配置virtualHost来配置 export default {options: {virtualHost: true} }

化茧成蝶 | 继HuggingFace首家落地大模型具身智能场景

关于具身智能的起源 近年来&#xff0c;大语言模型&#xff08;LLMs&#xff09;的兴起给机器人领域带来了革命性的改变&#xff0c;大模型赋予了传统机器人理解和推理的能力&#xff0c;让具身智能这一概念再度出现在大众的视角中。OpenCSG 作为国内 AI 开源社区的先锋&#…

Flask之数据库

前言&#xff1a;本博客仅作记录学习使用&#xff0c;部分图片出自网络&#xff0c;如有侵犯您的权益&#xff0c;请联系删除 目录 一、数据库的分类 1.1、SQL 1.2、NoSQL 1.3、如何选择&#xff1f; 二、ORM魔法 三、使用Flask-SQLALchemy管理数据库 3.1、连接数据库服…

美创携手浙江长征职业技术学院,共建智云数据安全大师工作室

6月24日&#xff0c;“美创科技浙江长征职业技术学院智云数据安全大师工作室揭牌暨中国特色学徒制第四期云数据安全和智能运维人才选拔培养启动仪式”在长征职业技术学院隆重举行。 浙江长征职业技术学院计算机与信息技术学院院长梅灿华、计算机与信息技术学院学工办副主任华春…