SIKI_Unity_2_入门_通过实例学习游戏的存档和读档

任务5:创建设计目标

Animation的直接使用:

将Animation组件直接挂载在游戏物体上即可播放

脚本控制:

方法1:

直接在Inspector面板给Animation的Animations集合赋值动画a和b
通过animation.clip = a或animation.clip = b后进行animation.Play()即可播放相关动画
注意:这种情况下如果在Inspector面板的Animations集合中没有动画b,那么即使给animation.clip = b赋了b动画的值,也是无法播放b动画的

[SerializeField]
private AnimationClip m_IdleAnimationClip;
[SerializeField]
private AnimationClip  m_DieAnimationClip;

m_Animation.clip = m_DieAnimationClip;
m_Animation.Play();

方法2:

用代码给Animation的Animations赋值,AddClip(clip, name)可以给音效设定名称,之后animation.Play(name)传入name即可播放对应音效

[SerializeField]
private AnimationClip m_IdleAnimationClip;
[SerializeField]
private AnimationClip  m_IdleAnimationClip;

m_Animation.AddClip(m_IdleAnimationClip, "Idle");
m_Animation.AddClip(m_DieAnimationClip, "Die");
m_Animation.Play("Die");

任务9:设置手枪的旋转

效果:

思路:

设定旋转的最大与最小角度,并根据鼠标在屏幕上的位置,按比例对枪体进行x和y轴上的旋转

任务21:存档相关概念

unity中使用的存档方式

1. PlayerPrefs: 数据持久化方案
  存储形式: 键值对
  存储数据: Int、Float、String类型
    bool类型可以用int的0/1模拟
  接口:
    PlayerPrefs.SetInt("Key", value);
    PlayerPrefs.SetFloat("Key", value);
    PlayerPrefs.SetString("Key", value);
    PlayerPrefs.GetInt("Key");

2. Serialization/ Deserialization: 序列化和反序列化
  存储: 将对象序列化为字节流(Bytes),将字节流存储
  读取: 将存储的字节流数据反序列化为对象
  常见的数据序列化方法:二进制、XML、JSON
    简单的数据用二进制的方式存储就够了,但是二进制的方式有局限性
    在实际开发中最常用的是XML/ JSON方式

二进制方法:Binary Formatter

序列化:新建/打开一个二进制文件,通过二进制格式器将对象写入该二进制文件
反序列化:打开待反序列化的二进制文件,通过二进制格式器将文件解析成对象

XML:扩展标记语言(具有结构性的标记语言)

序列化/ 反序列化的方式与二进制方法类似

https://blog.csdn.net/y1196645376/article/details/52541882

JSON:数据的常用格式,可用来跨平台数据传输
  序列化: 对象 -- > JSON
  反序列化: JSON -> 对象

优缺点:

二进制: 简单 || 可读性差

XML: 可读性强 || 数据文件比较大、冗余信息多

JSON: 数据格式较简单、易读写 || 可读性比XML差(键值对--不直观)

任务23:存储背景音乐的开关状态

使用PlayerPrefs保存后,需要通过PlayerPrefs.Save()进行存储   

任务27~28:通过二进制方法存档读档

Save对象中包含相关数据作为属性,该类需要marked as Serializable

[Serializable]
public class SaveData
{
    public List<Enemy.EnemyType> enemyTypeList; // 对应位置的敌人种类信息

    public SaveData() {
        enemyTypeList = new List<Enemy.EnemyType>();
        for(int i = 0; i < GameManager.instance.enemyGeneratorList.Count; i++) {
            enemyTypeList.Add(Enemy.EnemyType.Null);
        }
    }

    public int shootCount;
    public int score;
}

存储:

void SaveByBinary() {
    BinaryFormatter bf = new BinaryFormatter();
    FileStream fileStream = File.Create(Application.dataPath + "/StreamingFile/ByBin.txt");
    bf.Serialize(fileStream, save);
    fileStream.Close();
}

读取:

void LoadByBinary() {
    BinaryFormatter bf = new BinaryFormatter();
    FileStream fileStream = File.Open(Application.dataPath + "StreamingFile/ByBin.txt");
    Save save = (Save) bf.Deserialize(fileStream);
    fileStream.Close();
} 

存储得到的二进制文件:

              FAssembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null   SaveData
enemyTypeList
shootCountscore  |System.Collections.Generic.List`1[[Enemy+EnemyType, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]]                    |System.Collections.Generic.List`1[[Enemy+EnemyType, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]]   _items_size_version  Enemy+EnemyType[]                              Enemy+EnemyType   ?Enemy+EnemyType   value__       ??   ??    ??    ??    ??   ??    ??    ??    ??    ??    ??    ??    ??    ??    ??    

任务29~30:通过Json方法存档读档

通过第三方插件LitJson来操作Json文件

存档:

void SaveByJson(SaveData save) {
    string jsonRepresentation = JsonUtility.ToJson(save);
    StreamWriter streamWriter = new StreamWriter(Application.dataPath + "...");
    streamWriter.Write(jsonRepresentation);
    streamWriter.Close();
}

读档:

SaveData LoadByJson() {
    StreamReader streamReader = new StreamReader(Application.dataPath + "");
    string jsonRepresentation = streamReader.ReadToEnd();
    streamReader.Close();
    return JsonUtility.FromJson<SaveData>(jsonRepresentation);
}

或者使用JsonMapper.ToObject<SaveData>(jsonRepresentation);

存储得到的Json文件:

{"enemyTypeList":[1,0,4,4,4,1,0,3,4],"shootCount":4,"score":0}

任务31~32:通过XML方法存档读档

通过System.Xml的接口进行Xml文件的操作

存档:

private void SaveByXML(SaveData save) {
    // 创建XML文档
    XmlDocument xmlDocument = new XmlDocument();
    // 创建根节点
    XmlElement root = xmlDocument.CreateElement("root");
    root.SetAttribute("name", "save1");  // 节点的属性值
    xmlDocument.AppendChild(root);

    // 怪物信息
    XmlElement target;
    for (int i = 0; i < save.enemyTypeList.Count; i++) {
        target = xmlDocument.CreateElement("target");
        target.InnerText = ((int)save.enemyTypeList[i]).ToString();
        root.AppendChild(target);
    }

    // 分数信息
    XmlElement scoreElement = xmlDocument.CreateElement("score");
    scoreElement.InnerText = save.score.ToString();
    root.AppendChild(scoreElement);

    XmlElement shootCountElement = xmlDocument.CreateElement("shootNumber");
    shootCountElement.InnerText = save.shootCount.ToString();
    root.AppendChild(shootCountElement);

    xmlDocument.Save(m_SavePath + BY_XML_FILENAME);
}

读档:

private SaveData LoadByXML() {
    XmlDocument xmlDocument = new XmlDocument();
    xmlDocument.Load(m_SavePath + BY_XML_FILENAME);

    SaveData save = new SaveData();
    XmlNodeList targetNodeList = xmlDocument.GetElementsByTagName("target");
    // 读取敌人信息
    for (int i = 0; i < targetNodeList.Count; i++) {
        save.enemyTypeList[i] = ((Enemy.EnemyType)int.Parse(targetNodeList[i].InnerText));
    }

    XmlNode scoreNode = xmlDocument.GetElementsByTagName("score")[0];
    save.score = int.Parse(scoreNode.InnerText);

    XmlNode shootCountNode = xmlDocument.GetElementsByTagName("shootCount")[0];
    save.shootCount = int.Parse(shootCountNode.InnerText);

    return save;
}

存储得到的Xml文件:

<root name="save1">
  <target>4</target>
  <target>4</target>
  <target>4</target>
  <target>4</target>
  <target>3</target>
  <target>3</target>
  <target>3</target>
  <target>4</target>
  <target>3</target>
  <score>0</score>
  <shootCount>3</shootCount>
</root>
02-12 12:47