总结

1、二进制、json、xml存读档
2、处理二进制、json、xml 对 Vector3 等数据的读存
3、用Input.mousePosition.x/y和Screen.width/height的比例,来处理枪的旋转

界面

unity游戏存档备份 unity存档和读档_FileStream

8 使用协程控制怪物的生命周期

协程

计时,劳动法按时下班
开协程,各有对策

void Update()
    { 
        timer += Time.deltaTime;
        if (timer > spawnTime)
        {
            timer = 0;
           //01 SpawnEnemy();
            StartCoroutine(RandomSpawnTime());
        }

    }

    IEnumerator RandomSpawnTime()
    {
        yield return new WaitForSeconds(Random.Range(1f,5f));
        SpawnEnemy();
    }

9 控制怪物的死亡

协程控制生成死亡时间,min死亡时间>max生成事件

但目前我是以敌人从格子里面是生成,走出来,离开屋子后,加速冲向玩家,碰到玩家,玩家就掉血,敌人就自毁。
所以只用了协程生成

10 设置手枪的旋转

旋转轴与设置轴数字的对应

旋转那个位置,相当于“开摩托”,把手转转好比x红轴旋转;车轮旋转,好比枪口的上抬下压

unity游戏存档备份 unity存档和读档_unity游戏存档备份_02

运行前观察枪的方向极限

unity游戏存档备份 unity存档和读档_json_03

具体怎么观察、

以向上方向为例
1、向上旋转
2、看着觉得2中角度差不多
3、记录下3中的变化值
所以

public float minYDir=-60f;

unity游戏存档备份 unity存档和读档_json_04

解释那一堆又乘又加减的由来

public float minYDir=-60f;
    public float maxYDir=20f;
    public float minXDir=-70f;
    public float maxXDir=70f;
    //
    void RotateMuzzle()//旋转枪口
    {
        //第一句,当旋转X轴的值,等于,鼠标坐标在屏幕坐标系上的比例 乘以 观察到的eulerAngles.y最值。(为什么是y,因为只有y在变化)
        float rotateXAxis = Input.mousePosition.y / Screen.height * maxYDir;//上下,20
        float rotateYAxis = Input.mousePosition.x / Screen.width * maxXDir;//左右,70
        //(0,20)转(-60,20);(0,70)转(-70,70)。屏幕坐标转世界坐标。去除数字太麻烦了,做过一次,放弃了
        rotateXAxis = -((Mathf.Clamp(rotateXAxis, minYDir, maxYDir)) * 4 - 20);
        rotateYAxis = (Mathf.Clamp(rotateYAxis, minXDir, maxXDir) - 35) * 2;
        //赋值
        transform.eulerAngles = new Vector3(rotateXAxis, rotateYAxis, 0f);
    }

unity游戏存档备份 unity存档和读档_FileStream_05

17 制作菜单UI

Grid Layout Group ==NGUI的Grid

unity游戏存档备份 unity存档和读档_List_06

27 创建Save保存类

小结下目前碰到的标签

[System.Serializable]//类的详情
[SerializeField]  //对象的详情
[Tooltip("")]//提示,编辑器,代码都有提示
[HideInInspector]//编辑器隐藏

保存类

[System.Serializable]
public class SaveGame : MonoBehaviour
{
    public List<int> enemyPosList=new List<int>();
    public List<int> enemyTypeList=new List<int>();
    public int score;
    public int killCount;

调用

public SaveGame SaveGame()//保存敌人的类型,位置,得分,击杀数
    {
         SaveGame saveGame = new SaveGame();
        foreach(GameObject go in enemyObjectList)
        {
            saveGame.enemyTypeList.Add(go.GetComponent<Enemy>().type);
            saveGame.enemyPosList.Add(enemyObjectList.IndexOf(go));
        }

        list1 = saveGame.enemyTypeList;//打印
        list2 = saveGame.enemyPosList;
        a1 =saveGame.score = Player._instance.score;
        a2=saveGame.killCount = Player._instance.killCount;

        return saveGame;
    }

输出

unity游戏存档备份 unity存档和读档_FileStream_07

28 保存游戏(二进制方法)

(问题) 不支持保存Transform,Vector3等

System.Runtime.Serialization.FormatterServices.InternalGetSerializableMember 视频是九宫格,位置不变,所以用索引值。我用飞扑的方式,需要记录Transform(起码需要position和rotation)
4个数据,类型,分数,击杀数都是整数,只有Transform不行,position行不行没试过,下面用结构体记载position。

网上采用结构体处理Vector3。所以把Transform拆分成3部分pos,rotatte,scale(我不会)
目前只记录位置Vector3,用结构体表示

流程

unity游戏存档备份 unity存档和读档_List_08

前面的保存类修改

[Serializable]
public class SaveGame
{
    public List<int> enemyTypeList = new List<int>();
    public List<Vector3Serializer> enemyPosList = new List<Vector3Serializer>();
    public int score;
    public int killCount;
}

Enemy

[Serializable]public struct Vector3Serializer
{
    public float x;
    public float y;
    public float z;

    public void Fill(Vector3 v3)
    {
        x = v3.x;
        y = v3.y;
        z = v3.z;
    }

    public Vector3 V3
    { get { return new Vector3(x, y, z); } }
}
public class Enemy : MonoBehaviour
{
 ...
    public Vector3Serializer pos;

EnemyController

public SaveGame SaveGame()//保存敌人的类型,位置,得分,击杀数
    {
         SaveGame save = new SaveGame();
        foreach(GameObject go in enemyObjectList)
        {
            Enemy enemy = go.GetComponent<Enemy>();
            //类型
            save.enemyTypeList.Add(enemy.type);           
            //位置赋值,以结构体的方式
            go.GetComponent<Enemy>().pos.Fill(go.transform.position);
            save.enemyPosList.Add(enemy.pos);

            save.score = enemy.score;
            save.killCount = enemy.score;                           
        }
        return save;
    }

UI的代码,文件流

StreamingFile是在Unity根目录下新建的文件夹,不新建不知道会不会自动帮我们创建,毕竟ByBin1.txt一开始也是没有的
再次保存则覆盖

感觉FileStream像插好的“漏斗”,SaveGame是油,BinaryFormatter是来“倒油的”

void SaveByBin()
    {
        BinaryFormatter binaryFormatter = new BinaryFormatter();
        FileStream fileStream = File.Create(Application.dataPath + "/StreamingFile" + "/ByBin1.txt");
        SaveGame save = EnemyController._instance.SaveGame();

        binaryFormatter.Serialize(fileStream, save);

        fileStream.Close();
    }

结果

unity游戏存档备份 unity存档和读档_unity游戏存档备份_09

29 读取游戏(二进制方法)

UIController

BinaryFormatter将数据从FileStream“倒向”SaveGame

SaveGame LoadByBin() {
        BinaryFormatter binaryFormatter = new BinaryFormatter();
        FileStream fileStream = File.Open(Application.dataPath + "/StreamingFile" + "/ByBin1.txt", FileMode.Open);

        SaveGame save = (SaveGame)binaryFormatter.Deserialize(fileStream);

        fileStream.Close();
        return save;
    }
    public void LoadGame()
    {
        ResetGame();//清空置0

        SaveGame save= LoadByBin();//读档

        EnemyController._instance.LoadGame(save);//敌人
        Player._instance.LoadGame(save);//击杀数,得分
        scoreText.text = save.score.ToString();
        killCountText.text = save.killCount.ToString();
    }

EnemyController

public void LoadGame(SaveGame save)
    {
        for (int i = 0; i < save.enemyTypeList.Count; i++)
        {
            GameObject go = Instantiate( enemyPrefabList[save.enemyTypeList[i]] );
            go.transform.position = save.enemyPosList[i].V3;

            enemyObjectList.Add(go);
        }
    }

做成代码片段

不能复用就没意义,样式用的是xml格式

<?xml version="1.0" encoding="utf-8"?>
<CodeSnippets xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
	<CodeSnippet Format="1.0.0">
		<Header>
			<Title>SaveLoadByBin</Title>
			<Shortcut>SaveLoadByBin</Shortcut>
			<Description>二进制读档存档</Description>
			<Author>用户名</Author>
			<SnippetTypes>
				<SnippetType>Expansion</SnippetType>
				<SnippetType>SurroundsWith</SnippetType>
			</SnippetTypes>
		</Header>
		<Snippet>
	<Code Language="csharp"><![CDATA[
	[Serializable]
public class SaveGame
{
    public List<int> enemyTypeList = new List<int>();
    public List<Vector3Serializer> enemyPosList = new List<Vector3Serializer>();
    public int score;
    public int killCount;
}
    void SaveByBin()
    {
        BinaryFormatter binaryFormatter = new BinaryFormatter();
        FileStream fileStream = File.Create(Application.dataPath + "/StreamingFile" + "/ByBin1.txt");
        SaveGame save = EnemyController._instance.SaveGame();

        binaryFormatter.Serialize(fileStream, save);

        fileStream.Close();
    } 
    SaveGame LoadByBin() {
        BinaryFormatter binaryFormatter = new BinaryFormatter();
        FileStream fileStream = File.Open(Application.dataPath + "/StreamingFile" + "/ByBin1.txt", FileMode.Open);

        SaveGame save = (SaveGame)binaryFormatter.Deserialize(fileStream);

        fileStream.Close();
        return save;
    }
]]>
	</Code>
		</Snippet>
	</CodeSnippet>
</CodeSnippets>

30-31 保存游戏(JSON)

现象

存档后,unity不会及时自动刷新,需要手动刷新,一开始以为没成功,或者速度太慢

二进制不支持vector3,所以用结构体,现在json不支持Vector3,结构体也不支持

01 把List< Vector3 >拆成List< int >、List< int >、List< int >分别存储x,y,z
02 就一个列表每三个位置存储x,y,z,以此类推下去

采用2,因为像json也是一串数据在一起,等着被切割

所以放弃了前面的用结构体

unity游戏存档备份 unity存档和读档_unity游戏存档备份_10

JsonException: Max allowed object depth reached while trying to export from type System.Single

jsonData不支持float,所以用double
JsonException: Max allowed object depth reached while trying to export from type System.Single

重构

之前数据读存的部分放UICOntrollter,把它拎出来放新建的GameController

保存类

[Serializable]
public class SaveGame
{
    public List<int> enemyTypeList = new List<int>();
    public List<double> enemyXYZList = new List<double>();
    public int score;
    public int killCount;
}

GameController

void SaveByJson(SaveGame save, string path)
    {
        string str = JsonMapper.ToJson(save);

        StreamWriter streamWriter = new StreamWriter(Application.dataPath + path);

        streamWriter.Write(str);
        streamWriter.Close();
    }
    SaveGame LoadByJson(string path)
    {
        StreamReader streamreader = new StreamReader(Application.dataPath + path);//读取数据,转换成数据流
        JsonReader json = new JsonReader(streamreader);//再转换成json数据
        SaveGame save = JsonMapper.ToObject<SaveGame>(json);//读取

        streamreader.Close();

        return save;
    }

    public void SaveGame()
    {
        print("保存游戏!");
        SaveGame save = EnemyController._instance.SaveGame();

        SaveByJson(save, "/StreamingFile" + "/ByJson.json");
        if (File.Exists(Application.dataPath + "/StreamingFile" + "/ByJson.json"))
        {
            UIController._instance.ShowMessage("保存成功!");
        }
    }
    public void LoadGame()
    {
        ResetGame();//清空置0

        //SaveGame save= LoadByBin("/StreamingFile" + "/ByBin.txt");//读档
        SaveGame save = LoadByJson("/StreamingFile" + "/ByJson.json");//读档
        //SaveGame save= LoadByXml();//读档

        EnemyController._instance.LoadGame(save);//敌人
        Player._instance.LoadGame(save);//击杀数,得分
        UIController._instance.SetRecordUI(save.score, save.killCount);
    }

EnemyController

被UI的LoadGame调用

public SaveGame SaveGame()//保存敌人的类型,位置,得分,击杀数
    {
        SaveGame save = new SaveGame();
        foreach(GameObject go in enemyObjectList)
        {
            Enemy enemy = go.GetComponent<Enemy>();
            //类型
            save.enemyTypeList.Add(enemy.type);
            //位置赋值
            Vector3 pos= go.transform.position;
            save.enemyXYZList.Add(pos.x);
            save.enemyXYZList.Add(pos.y);
            save.enemyXYZList.Add(pos.z);

            enemyObjectXYZList = save.enemyXYZList;//打印看看
            this.enemyTypeList = save.enemyTypeList;//打印看看
            save.score = Player._instance.score;
            save.killCount = Player._instance.score;                           
        }

        UIController._instance.ShowMessage("存储完毕");
        return save;
    }

    public void LoadGame(SaveGame save)
    {
        for (int i = 0; i < save.enemyTypeList.Count; i++)
        {
            GameObject go = Instantiate( enemyPrefabList[save.enemyTypeList[i]] );

            int j = i * 3;//012 345 678 ......
            go.transform.position = new Vector3((float)save.enemyXYZList[j], (float)save.enemyXYZList[j+1], (float)save.enemyXYZList[j+2]);

            enemyObjectList.Add(go);
        }
    }

结果

存档有3个敌人了,分别是2,3,2种,位置是Vector3(-12.6999998092651, 14.9000015258789, 8.44331550598145),一次类推

{
	"enemyTypeList": [2, 3, 2],
	"enemyXYZList": [-12.6999998092651, 14.9000015258789, 8.44331550598145, -0.200000002980232, 29.0, 15.3022012710571, -0.400000005960464, 1.40000081062317, 22.9358730316162],
	"score": 10,
	"killCount": 10
}

32-33 保存游戏(XML)

(问题) 可以打印出来,但文件中只有最后一个

unity游戏存档备份 unity存档和读档_json_11


把变量放循环外面,导致只有一个

unity游戏存档备份 unity存档和读档_FileStream_12

保存类

虽然前面写了,但它是写读存档的基准,还是在写一下。建议一下代码全部做成代码片段

[Serializable]
public class SaveGame
{
    public List<int> enemyTypeList = new List<int>();
    public List<double> enemyXYZList = new List<double>();
    public int score;
    public int killCount;
}

保存,GameController

public void SaveGame()
    {
        print("保存游戏!");
        SaveGame save = EnemyController._instance.SaveGame();
        
        SaveByXml(save, "/StreamingFile" + "/ByXml.txt");
        if (File.Exists(Application.dataPath + "/StreamingFile" + "/ByXml.txt"))
        {
            UIController._instance.ShowMessage("保存成功!");
        }
    }


    void SaveByXml(SaveGame save, string path) {

        XmlDocument xmlDoc = new XmlDocument();
        XmlElement root = xmlDoc.CreateElement("save");
        root.SetAttribute("name","SaveFile1");


        for (int i = 0; i < save.enemyTypeList.Count; i++)
        {
            XmlElement target = xmlDoc.CreateElement("target");

            //类型和位置
            XmlElement type = xmlDoc.CreateElement("type");
            XmlElement x = xmlDoc.CreateElement("x");
            XmlElement y = xmlDoc.CreateElement("y");
            XmlElement z = xmlDoc.CreateElement("z");
            type.InnerText = save.enemyTypeList[i].ToString();
            int j = i * 3;
            x.InnerText = save.enemyXYZList[j].ToString();
            y.InnerText = save.enemyXYZList[j+1].ToString();
            z.InnerText = save.enemyXYZList[j+2].ToString();

            print(type.InnerText+","+x.InnerText+ "," + y.InnerText+ "," + z.InnerText);//打印看看

            target.AppendChild(type);
            target.AppendChild(x);
            target.AppendChild(y);
            target.AppendChild(z);

            root.AppendChild(target);
        }
        //分数
        XmlElement score = xmlDoc.CreateElement("score");
        score.InnerText = save.score.ToString();
        root.AppendChild(score);
        //击杀数
        XmlElement killCount = xmlDoc.CreateElement("killCount");
        killCount.InnerText = save.killCount.ToString();
        root.AppendChild(killCount);

        //
        xmlDoc.AppendChild(root);
        xmlDoc.Save(Application.dataPath+path);

加载 GameController

public void LoadGame()
    {
        SaveGame save= LoadByXml("/StreamingFile" + "/ByXml.txt");//读档

        EnemyController._instance.LoadGame(save);//敌人
        Player._instance.LoadGame(save);//击杀数,得分
        UIController._instance.SetRecordUI(save.score, save.killCount);
    }
    SaveGame LoadByXml(string path) 
    {
        XmlDocument xmlDoc = new XmlDocument();
        xmlDoc.Load(Application.dataPath+path);
        SaveGame save = new SaveGame();
        //
        XmlNodeList targetList = xmlDoc.GetElementsByTagName("target");
        for (int i = 0; i < targetList.Count; i++)
        {
            int type = int.Parse(targetList[i].ChildNodes[0].InnerText);
            double x = double.Parse(targetList[i].ChildNodes[1].InnerText);
            double y = double.Parse(targetList[i].ChildNodes[2].InnerText);
            double z = double.Parse(targetList[i].ChildNodes[3].InnerText);

            save.enemyTypeList.Add(type);
            save.enemyXYZList.Add(x);
            save.enemyXYZList.Add(y);
            save.enemyXYZList.Add(z);
        }
        //
        int score = int.Parse(xmlDoc.GetElementsByTagName("score")[0].InnerText);
        int killCount = int.Parse(xmlDoc.GetElementsByTagName("killCount")[0].InnerText);
        save.score = score;
        save.killCount = killCount;

        return save;
    }

结果

继续游戏的画面操作,截图说明意义不大(动图才行)

unity游戏存档备份 unity存档和读档_json_13