总结
1、二进制、json、xml存读档
2、处理二进制、json、xml 对 Vector3 等数据的读存
3、用Input.mousePosition.x/y和Screen.width/height的比例,来处理枪的旋转
界面
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红轴旋转;车轮旋转,好比枪口的上抬下压
运行前观察枪的方向极限
具体怎么观察、
以向上方向为例
1、向上旋转
2、看着觉得2中角度差不多
3、记录下3中的变化值
所以
public float minYDir=-60f;
解释那一堆又乘又加减的由来
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);
}
17 制作菜单UI
Grid Layout Group ==NGUI的Grid
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;
}
输出
28 保存游戏(二进制方法)
(问题) 不支持保存Transform,Vector3等
System.Runtime.Serialization.FormatterServices.InternalGetSerializableMember 视频是九宫格,位置不变,所以用索引值。我用飞扑的方式,需要记录Transform(起码需要position和rotation)
4个数据,类型,分数,击杀数都是整数,只有Transform不行,position行不行没试过,下面用结构体记载position。
网上采用结构体处理Vector3。所以把Transform拆分成3部分pos,rotatte,scale(我不会)
目前只记录位置Vector3,用结构体表示
流程
前面的保存类修改
[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();
}
结果
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也是一串数据在一起,等着被切割
所以放弃了前面的用结构体
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)
(问题) 可以打印出来,但文件中只有最后一个
把变量放循环外面,导致只有一个
保存类
虽然前面写了,但它是写读存档的基准,还是在写一下。建议一下代码全部做成代码片段
[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;
}
结果
继续游戏的画面操作,截图说明意义不大(动图才行)