Novices use C#'s own methods to create a unity archive system (no plug-ins)

Purely original, homemade

I am still a high school student, so if I am not good at it, please point it out if there are any loopholes.

Let’s first show what our demonstration project looks like.

Demo project engineering file download

A detailed video tutorial will be posted on station B later (so the article will be revised later)

If you find it troublesome, you can copy it directly into the project and use it.

(All SaveSystem source codes will be released at the end for everyone to copy)

Can store objects with fields of common value types and string types

The usage method is SaveSystem.Save <stored object type> (object name of stored data);

Loading is SaveSystem.Load <object type read> (object name of data read);

It's that simple, as long as the fields in your class are common value types and string types

For uncommon value types, please see the comments on the Load function in my article and just add them in the switch statement.

Use this to save

(Note: The using system, UnityEngine, System.Reflection, System.IO namespaces are required for use)

(This archiving method can also be used in other places. You can delete all Debug statements in the code without using UnityEngine)

(Only classes with the [CanSave] attribute added can be accepted by the archive system to prevent errors caused by incompatible classes being saved)

(This feature was added just for everyone to check)

(More detailed usage instructions are in the tutorial)

Use the UI to display changes in our data, and then save and load buttons to implement the functions of reading and archiving files

Create a new SaveSystem script, delete the Update function and Start function automatically generated by the editor, let's write the first Sava function, I will write comments to explain each object that beginners may not know (it is considerate enough)

public class SaveSysteam
{//泛型以保存各种类型的数据
   public static void Save<T>(T obj)
    {
        int N=0;//创建一个int变量来控制我要保存的数据所在数组位置
        string[]Data=new string[10];
        Type type = typeof(T);//利用反射获得类的信息
        if (type.IsDefined(typeof(CanSaveAttribute), false))
        {//这个if先不看可以,就是判断这个类是否存在我们后面的自定义特性,不存在是不可以存的哦
         //因为存的数据要求是值类型或字符串类型,为了防止其他人使用不会注意,我们要写一个特性,起提示作用
            CanSaveAttribute can=type.GetCustomAttribute<CanSaveAttribute>();//获得该类下的特性
            FieldInfo[] fields = type.GetFields();//利用反射获得该类的所有字段
            Data[N] = can.author;//保存特性中的信息
            N++;
            Data[N] = can.versionNumber;
            N++;
         //从上个注释到这都是存特性的
            foreach (FieldInfo field in fields)
            {
        //遍历字段的数组获得每个字段的值,并转换成字符串类型
                Data[N] = Convert.ToString(field.GetValue(obj));
                N++;

                // Debug.Log(field.GetValue(obj));
            }
            File.WriteAllLines(@"D:\\c#\\存档系统\\GameData.txt", Data);
                 //前面的参数是存的路径,请您自己改哦,后面的参数是存完数据的string数组
            Debug.Log("IsSave");//控制台输出保存的消息
        }
        else//如果不包含特性的警告
        {
            Debug.LogError("你保存了一个不包含特性CanSaveAttribute的类,请检查保存的类是否符合规定");
        }
    }
    
    
}

This is our Save function. Isn’t it relatively simple? Then let’s add the Load function to the SaveSystem class.

//泛型老样子(不会有人没注意听讲吧)
 public static void Load<T>(T obj)
    {
        int N = 0;//N的作用和上面的一样
        Type type = typeof(T);
        if (type.IsDefined(typeof(CanSaveAttribute), false))
        {
            CanSaveAttribute can = type.GetCustomAttribute<CanSaveAttribute>();
            FieldInfo[] fields = type.GetFields();
            string[] Data = new string[10];
            Data = File.ReadAllLines(@"D:\\c#\\存档系统\\GameData.txt");//按行读取文件里的数据
               //上面用到的保存方法也是按行读取
            if (Data[N] == can.author && Data[N+1]==can.versionNumber)
            {//判断要读取的类的特性里的信息和当时存储的信息是否一致
                N = N + 2;
                
                foreach (FieldInfo field in fields)
                {//遍历要读取的类的字段的类型,好把数据按类型传回去,因为存的都是string类型的
                    switch (Convert.ToString(field.FieldType))
                    {//用switch语句,现在字段的类型是啥就把string转换成什么
                       //为什么变量类型都是这些名字呢
                     //因为反射获得的字段的类型都是对应的.NET Framework 中的类型
                    // 下张图片我会放一个表,C#类型对应的.NET Framework 类型
                        case "System.Int32":
                            field.SetValue(obj, Convert.ToInt32(Data[N]));
                             //设置字段值的方法SetValue,前面要求的参数是你要设置字段的对象,可以这么理解
                             //但是我建议你自己查一下微软的文档里面有详细解释
                            break;
                        case "System.Double":
                            field.SetValue(obj, Convert.ToDouble(Data[N]));
                            break;
                        case "System.String":
                            field.SetValue(obj, Data[N]);
                            break;
                        case "System.Single":
                            field.SetValue(obj, Convert.ToSingle(Data[N]));
                            break;
                        case "System.Decimal":
                            field.SetValue(obj, Convert.ToDecimal(Data[N]));
                            break;



                    }
                   
                    N++;
                }
            }
            else
            {
                Debug.LogError("现在的类与存储时的特性有所差异,可以查看文件中的原作者名字取得联系并修改");
            }
        }
        else
        {
            Debug.LogError("你加载了一个不包含特性CanSaveAttribute的类,请检查加载的类是否符合规定或存在");
        }
    }

Below is the table I agreed to (I found it in another blog, but I can’t find the original author, so I can’t post the link. Thanks to the author for organizing it)

(If there is an error, please check the latest Microsoft documentation)

C# 类型       .NET Framework 类型

bool            System.Boolean               

4Byte 32bit ,true或者false,默认值为false

byte            System.Byte                

1Byte 8bit 无符号整数 无符号的字节,所存储的值的范围是0~255,默认值为0

sbyte         System.SByte 1Byte

8bit 有符号整数 带符号的字节,所存储的值的范围是-128~127,默认值为0

char           System.Char 

2Byte 16bit 无符号Unicode字符,默认值为’\0’


decimal      System.Decimal

16Byte 128bit十进制数不遵守四舍五入规则的十进制数,28个有效数字,通常用于财务方面的计算,默认值为0.0m



double       System.Double

8Byte 64bit双精度的浮点类型,默认值为0.0d

float          System.Single

4Byte 32bit单精度的浮点类型,默认值为0.0f



int             System.Int32

4Byte 32bit有符号整数,默认值为0

uint           System.UInt32

4Byte 32bit无符号整数,默认值为0

long          System.Int64

8Byte 64bit有符号整数,默认值为0

Then we have to write about the pit we dug before. Yes, it’s the features.

//哈哈哈,自定义特性上还要加特性,这个特性标明它是给类用的
[AttributeUsage(AttributeTargets.Class)]
//创造特性需要继承Attribute,且名字后必须以Attribute结尾,使用时不需要Attribute结尾
public class CanSaveAttribute : System.Attribute
{
   //下面都是希望存在特性里的信息作者,版本号,和是否检查过
    public string author="";
    public string versionNumber="";
    public bool isCheck;
}

那我们该如何使用这个存档系统呢,拿我的模板当个实例,我需要存储玩家的位置信息和名字信息,我把模板项目的玩家控制代码发出来,挂到玩家上即可,看好注释哦,后面放游戏内容截图

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class Player : MonoBehaviour
{
    public Rigidbody2D Rigidbody2D;
    public float speed = 10f;
    public SaveDataObject SObject;//先声明我们要存的对象
    public Text X;
    public Text Y;
    public Text Z;
    public Text NameUI;

    // Start is called before the first frame update
    void Start()
    {
        Rigidbody2D=GetComponent<Rigidbody2D>();

    }

    // Update is called once per frame
    void Update()
    {
        Rigidbody2D.velocity = new Vector2(Input.GetAxis("Horizontal") * speed, Input.GetAxis("Vertical") *speed);
        X.text = Convert.ToString(transform.position.x);
        Y.text = Convert.ToString(transform.position.y);
        Z.text = Convert.ToString(transform.position.z);
    }
   //要用button的事件链接这两个函数哦
    public void ClickSave()
    {
        Player player=GetComponent<Player>();//获得玩家对象
        SObject=new SaveDataObject(player,"LiLei");//嘿嘿偷懒,把名字写这里传了
         SaveSysteam.Save<SaveDataObject>(SObject);//保存实际就这一行,传入我们要存的对象即可
        
    }
    public void ClickLoad()
    {
        Player player = GetComponent<Player>();
        SObject = new SaveDataObject(player);
        SaveSysteam.Load<SaveDataObject>(SObject);//和保存的使用方法一样
       //下面就是把获得的数据传回到游戏中了
        transform.position = new Vector3(SObject.X,SObject.Y,SObject.Z);
        NameUI.text = SObject.playerName;
        Debug.Log("IsLoad");
    }

}
//这是我要存储的类型获得玩家名称和位置信息
//想存别的东西就现创建一个类就行了,不用动存档系统的代码哦,方便吧
[CanSave(author = "小羊宝正", versionNumber = "版本1.1",isCheck =true)]
public class SaveDataObject
{
    
    public float X;
    public float Y;
    public float Z;
    public string playerName;

    public  SaveDataObject(Player player,string name)
    {
        X = player.transform.position.x;
        Y = player.transform.position.y;
        Z = player.transform.position.z;
        playerName = name;
    }
    public SaveDataObject(Player player)
    {
        X = player.transform.position.x;
        Y = player.transform.position.y;
        Z = player.transform.position.z;
        
    }
}

该放截图了,先让小球走几步(走到load按钮上)

然后点击save按钮 控制台发出消息

打开我们存的文件,看数据被存好了

然后我们关掉游戏,让小球回到原点

然后再次运行,再点击load按钮,看小球回到存档的位置了,并且被起名LiLei(李雷)

这就是使用方法了,我写的存档系统还不错吧,非常简便且灵活,其他功能大家可以自己探索(比如存档位,在此基础上更改都不难,虽然我设计了其他完整的功能,但是临近期末没精力写出来了,以后要是反响好我就补)

下面是答应大家的SaveSystem完整源代码,被注释的是之前写错懒得删的不影响使用

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Reflection;
using System.IO;

public class SaveSysteam
{
   public static void Save<T>(T obj)
    {
        int N=0;
        string[]Data=new string[10];
        Type type = typeof(T);
        if (type.IsDefined(typeof(CanSaveAttribute), false))
        {
            CanSaveAttribute can=type.GetCustomAttribute<CanSaveAttribute>();
            FieldInfo[] fields = type.GetFields();
            Data[N] = can.author;
            N++;
            Data[N] = can.versionNumber;
            N++;
            foreach (FieldInfo field in fields)
            {
                //field.GetValue(obiect);
                Data[N] = Convert.ToString(field.GetValue(obj));
                N++;

                // Debug.Log(field.GetValue(obj));
            }
            File.WriteAllLines(@"D:\\c#\\存档系统\\GameData.txt", Data);
            Debug.Log("IsSave");
        }
        else
        {
            Debug.LogError("你保存了一个不包含特性CanSaveAttribute的类,请检查保存的类是否符合规定");
        }
    }
    public static void Load<T>(T obj)
    {
        int N = 0;
        Type type = typeof(T);
        if (type.IsDefined(typeof(CanSaveAttribute), false))
        {
            CanSaveAttribute can = type.GetCustomAttribute<CanSaveAttribute>();
            FieldInfo[] fields = type.GetFields();
            string[] Data = new string[10];
            Data = File.ReadAllLines(@"D:\\c#\\存档系统\\GameData.txt");
            if (Data[N] == can.author && Data[N+1]==can.versionNumber)
            {
                N = N + 2;
                //PropertyInfo[] propertyInfos = type.GetProperties();
                foreach (FieldInfo field in fields)
                {
                    switch (Convert.ToString(field.FieldType))
                    {
                        case "System.Int32":
                            field.SetValue(obj, Convert.ToInt32(Data[N]));
                            break;
                        case "System.Double":
                            field.SetValue(obj, Convert.ToDouble(Data[N]));
                            break;
                        case "System.String":
                            field.SetValue(obj, Data[N]);
                            break;
                        case "System.Single":
                            field.SetValue(obj, Convert.ToSingle(Data[N]));
                            break;
                        case "System.Decimal":
                            field.SetValue(obj, Convert.ToDecimal(Data[N]));
                            break;



                    }
                    Debug.Log(Convert.ToString(field.FieldType));
                    N++;
                }
            }
            else
            {
                Debug.LogError("现在的类与存储时的特性有所差异,可以查看文件中的原作者名字取得联系并修改");
            }
        }
        else
        {
            Debug.LogError("你加载了一个不包含特性CanSaveAttribute的类,请检查加载的类是否符合规定或存在");
        }
    }
    
}
[AttributeUsage(AttributeTargets.Class)]
public class CanSaveAttribute : System.Attribute
{
    public string author="";
    public string versionNumber="";
    public bool isCheck;
}

自己设计的代码无论多烂,自己都觉得像诗一样美好,自己总会去回味。自己不是专业的程序,一开始只是一个叛逆的爱打游戏的少年。可能是为了做一个游戏,又或者是儿时的科普读物让我向往计算机,我走上了游戏制作人的道路,开始自学unity和C#,我走过好多弯路,无论人生还是学习编程的过程中,我失败无数次。也长大了对游戏不再那么痴迷,但是我对编程的热爱却没有减退,希望这篇文章能帮助向我一样的自学者。希望每位读者能从我这读到你需要的知识。感谢阅读,感谢您的认真观看。

Guess you like

Origin blog.csdn.net/wsxybz/article/details/128486717