当前位置:   article > 正文

新手向 使用C#自带方法制作unity存档系统(无插件)_c#单机游戏存档功能怎么做

c#单机游戏存档功能怎么做

纯原创,自制

本人还是个在校的高中生,能力不精如果有漏洞欢迎指出

先放出我们示范项目的样子

演示项目工程文件下载

后面会在B站发详细的视频教程(所以文章之后肯会改)

嫌麻烦的直接复制到项目里就可以用哦

(全部的SaveSystem源码最后放出方便大家复制)

可以存储含有常见值类型和字符串类型字段的对象

使用方法就是SaveSystem.Save<存储的对象类型>(存储数据的对象名);

加载就是SaveSystem.Load<读取的对象类型>(读取数据的对象名);

就是这么简单,只要你的类里的字段是常见值类型和字符串类型就可以

不常见的值类型可看我文章中Load函数的注释,在switch语句中添加即可

使用这个就可以存了

(注:使用时需要using system,UnityEngine,System.Reflection,System.IO这几个命名空间)

(其他地方也可以使用这个存档方法,把代码中的所有Debug语句删除就可以不使用UnityEngine)

(只有加了[CanSave]特性的类才可以被存档系统接受,防止不兼容的类被保存造成的错误)

(就是为了让大家检查一下才加了这个特性)

(更详尽的使用方法在教程中哦)

使用UI来显示我们数据的变化,然后save和Load按钮实现读档和存档的功能

新建SaveSystem脚本,删除掉编辑器自动生成的Update函数和Start函数,让我们写下第一个Sava函数,我会写好注释讲解每个初学者可能不知道的对象(够贴心吧)

  1. public class SaveSysteam
  2. {//泛型以保存各种类型的数据
  3. public static void Save<T>(T obj)
  4. {
  5. int N=0;//创建一个int变量来控制我要保存的数据所在数组位置
  6. string[]Data=new string[10];
  7. Type type = typeof(T);//利用反射获得类的信息
  8. if (type.IsDefined(typeof(CanSaveAttribute), false))
  9. {//这个if先不看可以,就是判断这个类是否存在我们后面的自定义特性,不存在是不可以存的哦
  10. //因为存的数据要求是值类型或字符串类型,为了防止其他人使用不会注意,我们要写一个特性,起提示作用
  11. CanSaveAttribute can=type.GetCustomAttribute<CanSaveAttribute>();//获得该类下的特性
  12. FieldInfo[] fields = type.GetFields();//利用反射获得该类的所有字段
  13. Data[N] = can.author;//保存特性中的信息
  14. N++;
  15. Data[N] = can.versionNumber;
  16. N++;
  17. //从上个注释到这都是存特性的
  18. foreach (FieldInfo field in fields)
  19. {
  20. //遍历字段的数组获得每个字段的值,并转换成字符串类型
  21. Data[N] = Convert.ToString(field.GetValue(obj));
  22. N++;
  23. // Debug.Log(field.GetValue(obj));
  24. }
  25. File.WriteAllLines(@"D:\\c#\\存档系统\\GameData.txt", Data);
  26. //前面的参数是存的路径,请您自己改哦,后面的参数是存完数据的string数组
  27. Debug.Log("IsSave");//控制台输出保存的消息
  28. }
  29. else//如果不包含特性的警告
  30. {
  31. Debug.LogError("你保存了一个不包含特性CanSaveAttribute的类,请检查保存的类是否符合规定");
  32. }
  33. }
  34. }

这就是我们的Save函数了,是不是比较简单呢,那么接下来让我们在SaveSystem类里加入Load函数

  1. //泛型老样子(不会有人没注意听讲吧)
  2. public static void Load<T>(T obj)
  3. {
  4. int N = 0;//N的作用和上面的一样
  5. Type type = typeof(T);
  6. if (type.IsDefined(typeof(CanSaveAttribute), false))
  7. {
  8. CanSaveAttribute can = type.GetCustomAttribute<CanSaveAttribute>();
  9. FieldInfo[] fields = type.GetFields();
  10. string[] Data = new string[10];
  11. Data = File.ReadAllLines(@"D:\\c#\\存档系统\\GameData.txt");//按行读取文件里的数据
  12. //上面用到的保存方法也是按行读取
  13. if (Data[N] == can.author && Data[N+1]==can.versionNumber)
  14. {//判断要读取的类的特性里的信息和当时存储的信息是否一致
  15. N = N + 2;
  16. foreach (FieldInfo field in fields)
  17. {//遍历要读取的类的字段的类型,好把数据按类型传回去,因为存的都是string类型的
  18. switch (Convert.ToString(field.FieldType))
  19. {//用switch语句,现在字段的类型是啥就把string转换成什么
  20. //为什么变量类型都是这些名字呢
  21. //因为反射获得的字段的类型都是对应的.NET Framework 中的类型
  22. // 下张图片我会放一个表,C#类型对应的.NET Framework 类型
  23. case "System.Int32":
  24. field.SetValue(obj, Convert.ToInt32(Data[N]));
  25. //设置字段值的方法SetValue,前面要求的参数是你要设置字段的对象,可以这么理解
  26. //但是我建议你自己查一下微软的文档里面有详细解释
  27. break;
  28. case "System.Double":
  29. field.SetValue(obj, Convert.ToDouble(Data[N]));
  30. break;
  31. case "System.String":
  32. field.SetValue(obj, Data[N]);
  33. break;
  34. case "System.Single":
  35. field.SetValue(obj, Convert.ToSingle(Data[N]));
  36. break;
  37. case "System.Decimal":
  38. field.SetValue(obj, Convert.ToDecimal(Data[N]));
  39. break;
  40. }
  41. N++;
  42. }
  43. }
  44. else
  45. {
  46. Debug.LogError("现在的类与存储时的特性有所差异,可以查看文件中的原作者名字取得联系并修改");
  47. }
  48. }
  49. else
  50. {
  51. Debug.LogError("你加载了一个不包含特性CanSaveAttribute的类,请检查加载的类是否符合规定或存在");
  52. }
  53. }

下面是我答应的表(是在别的博客里翻到的,但是实在找不到原作者了,无法发链接,感谢作者整理)

(如果错误就去微软文档查最新的)

  1. C# 类型 .NET Framework 类型
  2. bool System.Boolean
  3. 4Byte 32bit ,true或者false,默认值为false
  4. byte System.Byte
  5. 1Byte 8bit 无符号整数 无符号的字节,所存储的值的范围是0~255,默认值为0
  6. sbyte System.SByte 1Byte
  7. 8bit 有符号整数 带符号的字节,所存储的值的范围是-128~127,默认值为0
  8. char System.Char
  9. 2Byte 16bit 无符号Unicode字符,默认值为’\0’
  10. decimal System.Decimal
  11. 16Byte 128bit十进制数不遵守四舍五入规则的十进制数,28个有效数字,通常用于财务方面的计算,默认值为0.0m
  12. double System.Double
  13. 8Byte 64bit双精度的浮点类型,默认值为0.0d
  14. float System.Single
  15. 4Byte 32bit单精度的浮点类型,默认值为0.0f
  16. int System.Int32
  17. 4Byte 32bit有符号整数,默认值为0
  18. uint System.UInt32
  19. 4Byte 32bit无符号整数,默认值为0
  20. long System.Int64
  21. 8Byte 64bit有符号整数,默认值为0

然后我们要写之前挖的坑了,没错就是特性

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

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

  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using UnityEngine;
  5. using UnityEngine.UI;
  6. public class Player : MonoBehaviour
  7. {
  8. public Rigidbody2D Rigidbody2D;
  9. public float speed = 10f;
  10. public SaveDataObject SObject;//先声明我们要存的对象
  11. public Text X;
  12. public Text Y;
  13. public Text Z;
  14. public Text NameUI;
  15. // Start is called before the first frame update
  16. void Start()
  17. {
  18. Rigidbody2D=GetComponent<Rigidbody2D>();
  19. }
  20. // Update is called once per frame
  21. void Update()
  22. {
  23. Rigidbody2D.velocity = new Vector2(Input.GetAxis("Horizontal") * speed, Input.GetAxis("Vertical") *speed);
  24. X.text = Convert.ToString(transform.position.x);
  25. Y.text = Convert.ToString(transform.position.y);
  26. Z.text = Convert.ToString(transform.position.z);
  27. }
  28. //要用button的事件链接这两个函数哦
  29. public void ClickSave()
  30. {
  31. Player player=GetComponent<Player>();//获得玩家对象
  32. SObject=new SaveDataObject(player,"LiLei");//嘿嘿偷懒,把名字写这里传了
  33. SaveSysteam.Save<SaveDataObject>(SObject);//保存实际就这一行,传入我们要存的对象即可
  34. }
  35. public void ClickLoad()
  36. {
  37. Player player = GetComponent<Player>();
  38. SObject = new SaveDataObject(player);
  39. SaveSysteam.Load<SaveDataObject>(SObject);//和保存的使用方法一样
  40. //下面就是把获得的数据传回到游戏中了
  41. transform.position = new Vector3(SObject.X,SObject.Y,SObject.Z);
  42. NameUI.text = SObject.playerName;
  43. Debug.Log("IsLoad");
  44. }
  45. }
  46. //这是我要存储的类型获得玩家名称和位置信息
  47. //想存别的东西就现创建一个类就行了,不用动存档系统的代码哦,方便吧
  48. [CanSave(author = "小羊宝正", versionNumber = "版本1.1",isCheck =true)]
  49. public class SaveDataObject
  50. {
  51. public float X;
  52. public float Y;
  53. public float Z;
  54. public string playerName;
  55. public SaveDataObject(Player player,string name)
  56. {
  57. X = player.transform.position.x;
  58. Y = player.transform.position.y;
  59. Z = player.transform.position.z;
  60. playerName = name;
  61. }
  62. public SaveDataObject(Player player)
  63. {
  64. X = player.transform.position.x;
  65. Y = player.transform.position.y;
  66. Z = player.transform.position.z;
  67. }
  68. }

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

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

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

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

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

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

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

  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using UnityEngine;
  5. using System.Reflection;
  6. using System.IO;
  7. public class SaveSysteam
  8. {
  9. public static void Save<T>(T obj)
  10. {
  11. int N=0;
  12. string[]Data=new string[10];
  13. Type type = typeof(T);
  14. if (type.IsDefined(typeof(CanSaveAttribute), false))
  15. {
  16. CanSaveAttribute can=type.GetCustomAttribute<CanSaveAttribute>();
  17. FieldInfo[] fields = type.GetFields();
  18. Data[N] = can.author;
  19. N++;
  20. Data[N] = can.versionNumber;
  21. N++;
  22. foreach (FieldInfo field in fields)
  23. {
  24. //field.GetValue(obiect);
  25. Data[N] = Convert.ToString(field.GetValue(obj));
  26. N++;
  27. // Debug.Log(field.GetValue(obj));
  28. }
  29. File.WriteAllLines(@"D:\\c#\\存档系统\\GameData.txt", Data);
  30. Debug.Log("IsSave");
  31. }
  32. else
  33. {
  34. Debug.LogError("你保存了一个不包含特性CanSaveAttribute的类,请检查保存的类是否符合规定");
  35. }
  36. }
  37. public static void Load<T>(T obj)
  38. {
  39. int N = 0;
  40. Type type = typeof(T);
  41. if (type.IsDefined(typeof(CanSaveAttribute), false))
  42. {
  43. CanSaveAttribute can = type.GetCustomAttribute<CanSaveAttribute>();
  44. FieldInfo[] fields = type.GetFields();
  45. string[] Data = new string[10];
  46. Data = File.ReadAllLines(@"D:\\c#\\存档系统\\GameData.txt");
  47. if (Data[N] == can.author && Data[N+1]==can.versionNumber)
  48. {
  49. N = N + 2;
  50. //PropertyInfo[] propertyInfos = type.GetProperties();
  51. foreach (FieldInfo field in fields)
  52. {
  53. switch (Convert.ToString(field.FieldType))
  54. {
  55. case "System.Int32":
  56. field.SetValue(obj, Convert.ToInt32(Data[N]));
  57. break;
  58. case "System.Double":
  59. field.SetValue(obj, Convert.ToDouble(Data[N]));
  60. break;
  61. case "System.String":
  62. field.SetValue(obj, Data[N]);
  63. break;
  64. case "System.Single":
  65. field.SetValue(obj, Convert.ToSingle(Data[N]));
  66. break;
  67. case "System.Decimal":
  68. field.SetValue(obj, Convert.ToDecimal(Data[N]));
  69. break;
  70. }
  71. Debug.Log(Convert.ToString(field.FieldType));
  72. N++;
  73. }
  74. }
  75. else
  76. {
  77. Debug.LogError("现在的类与存储时的特性有所差异,可以查看文件中的原作者名字取得联系并修改");
  78. }
  79. }
  80. else
  81. {
  82. Debug.LogError("你加载了一个不包含特性CanSaveAttribute的类,请检查加载的类是否符合规定或存在");
  83. }
  84. }
  85. }
  86. [AttributeUsage(AttributeTargets.Class)]
  87. public class CanSaveAttribute : System.Attribute
  88. {
  89. public string author="";
  90. public string versionNumber="";
  91. public bool isCheck;
  92. }

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

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/盐析白兔/article/detail/140707
推荐阅读
相关标签
  

闽ICP备14008679号