开学了开学了,虽然后期很浪,但总的来说过了一个收获颇多的暑假。这里讲一讲最后实现设置面板和存档读档的思路
可以采用之前类神庙逃亡项目的思路来写:
在主Canvas上创建一个Panel,当作设置面板,先禁用
如何控制BGM音量和文字显示的快慢?很简单,创建一个SettingManager脚本来管理设置面板,先获取BGM的脚本和带显示文字协程的脚本(好吧,后面看来其实不用,因为我的所有脚本都写有一个供全局调用的实例)
//static:
public static Setting_Controller Setting_Instance;
//public:
public GameObject BGM;
public GameObject TextSpeed;
// Use this for initialization
private void Awake()
{
Setting_Instance = this;//initialize
BGM = GameObject.Find("BGM_Source");
TextSpeed = GameObject.Find("UI_Control");
}
然后在Panel上创建两个Slider来控制,如图:
在SettingManager中写两个函数来修改对应参数,用滑动条来改变值即可:
public void SetBGMVolume()
{
BGM.GetComponent<AudioSource>().volume = gameObject.GetComponent<Slider>().value;
}
public void SetTextSpeed()
{
UI_Manager.UIManager_Instance.SetTextSpeed(gameObject.GetComponent<Slider>().value);
}
// Update is called once per frame
比较严谨的可以用PlayerPrefeb或者JSON来处理,这里就说说最最简单的思路,用文件读写来实现。可以分析一下可以用什么来当储存的数据,之前定义过一个LineIndex变量,代表当前脚本的行数,刚好是我们需要的,就它了。然而这是肯定不够的,因为我们在游戏中储存的脚本行必然是一行对话脚本,当前的人物,背景,bgm是没有被储存的(它们是在之前的命令脚本被改变的)
于是,必须将所有信息先存起来,建立一个SaveData类:
//public:
public int LineIndex;
public string BGMName;
public string BackgroundImgName;
public string LCharacter, MCharacter, RCharacter;
public float BGMVolume;
public float TextSpeed;
//private:
//private Color compare_color;
// Use this for initialization
private void Awake()
{
LineIndex = TextParser.TextParser_Instance.LineIndex;
BGMName = BGM_Management.BGM_Manager_Instance.Current_BGM;
BackgroundImgName = UI_Manager.UIManager_Instance.Cor_bgname;
//compare_color = new Color(1, 1, 1, 1);
TextSpeed = UI_Manager.UIManager_Instance.Cor_DisplayingSpeed;
BGMVolume = GameObject.Find("BGM_Source").GetComponent<AudioSource>().volume;
}
在初始化时获取当前场景的bgm,背景,角色,以及设置界面。角色立绘储存还需要处理一下,把所有位置变为字符串储存起来
public void GetCharacters()
{
LCharacter = TextParser.TextParser_Instance.CurrentCharas[0];
MCharacter = TextParser.TextParser_Instance.CurrentCharas[1];
RCharacter = TextParser.TextParser_Instance.CurrentCharas[2];
}
这样需要储存的东西都到位了,创建一个SavingManager脚本来把数据写入文件中,把savedata类挂到一个空物体上,让SavingManager获取它,将数据打包存进一个string数组中:
初始化:
//public
public string DirectPath;
public StreamWriter SW1;
public SaveData CurrentData;
public FileStream fs;
// Use this for initialization
void Start () {
DirectPath = Application.streamingAssetsPath;
}
packing&writing
public string[] PackingData(SaveData sd1)
{
string[] DataArray = new string[8];
DataArray[0] = sd1.LineIndex.ToString();
DataArray[1] = sd1.BGMName;
DataArray[2] = sd1.BackgroundImgName;
DataArray[3] = sd1.LCharacter;
DataArray[4] = sd1.MCharacter;
DataArray[5] = sd1.RCharacter;
DataArray[6] = sd1.BGMVolume.ToString();
DataArray[7] = sd1.TextSpeed.ToString();
return DataArray;
}
//
public void WriteData(StreamWriter temp)
{
string[] DataStream=PackingData(CurrentData);
foreach(string str in DataStream)
{
temp.WriteLine(str);
temp.Flush();
}
temp.Close();
}
这里用的StreamWriter来写入,有点麻瓜,毫无安全性可言(但很简单省时),需要先创建好几个存档槽位,然后通过点击slot的按钮来储存。用switch语句来判断是哪个槽位,写入,然后更新存档槽的图片就ok了
switch (gameObject.name)
{
case "Slot_1":
UpdateImage(gameObject);
fs = new FileStream(DirectPath + "/Slot1.txt", FileMode.Create);
SW1 = new StreamWriter(fs);
WriteData(SW1);
fs.Close();
break;
case "Slot_2":
UpdateImage(gameObject);
fs = new FileStream(DirectPath + "/Slot2.txt", FileMode.Create);
SW1 = new StreamWriter(fs);
WriteData(SW1);
fs.Close();
break;
case "Slot_3":
UpdateImage(gameObject);
fs = new FileStream(DirectPath + "/Slot3.txt", FileMode.Create);
SW1 = new StreamWriter(fs);
fs.Close();
WriteData(SW1);
break;
case "Slot_4":
UpdateImage(gameObject);
fs = new FileStream(DirectPath + "/Slot4.txt", FileMode.Create);
SW1 = new StreamWriter(fs);
fs.Close();
WriteData(SW1);
break;
default:
break;
}
public void UpdateImage(GameObject gb1)
{
CurrentData = GameObject.Find("SaveDataObject").GetComponent<SaveData>();
gb1.GetComponent<Image>().sprite = (Sprite)Resources.Load("Background/" + CurrentData.BackgroundImgName, typeof(Sprite));
}
然后是读档,存档和读档用的是一个面板,所以写一个状态量,点击不同的按钮进入不同的(存档,读挡)状态,写一个协程,隔个零点几秒检测一次状态,直接上代码了:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public enum SLState {none,Saving,Loading}
public class State_Manager : MonoBehaviour {
public static SLState SaveLoadState;
public GameObject Manager_Save;
public GameObject Manager_Load;
// Use this for initialization
private void Awake()
{
SaveLoadState = SLState.none;
}
void Start () {
StartCoroutine("SaveStateCheck");
}
IEnumerator SaveStateCheck()
{
yield return new WaitForEndOfFrame();
while(true)
{
yield return new WaitForSeconds(0.2f);
if(SaveLoadState==SLState.Saving)
{
Manager_Save.SetActive(true);
Manager_Load.SetActive(false);
}
else if(SaveLoadState==SLState.Loading)
{
Manager_Save.SetActive(false);
Manager_Load.SetActive(true);
}
else
{
Manager_Save.SetActive(false);
Manager_Load.SetActive(false);
}
}
}
// Update is called once per frame
void Update () {
//
}
}
通过状态来指示当前储存面板点击槽位是储存还是读取,接着来写读档的LoadingManager脚本,很简单,把文件中的string按行读取,再改变对应UI即可,定义初始化:
public static Loading_Manager Loading_Manager_Instance;
public string DirectPath;
public string[] CurArray;
// Use this for initialization
void Start () {
DirectPath = Application.streamingAssetsPath;
Loading_Manager_Instance = this;
}
然后是解析和改变UI的函数:
public void Slot_Clicked()
{
if (State_Manager.SaveLoadState == SLState.Loading)
{
switch (gameObject.name)
{
case "Slot_1":
CurArray = LoadData("Slot1.txt");
SetUI();
break;
case "Slot_2":
CurArray = LoadData("Slot2.txt");
SetUI();
break;
case "Slot_3":
CurArray = LoadData("Slot3.txt");
SetUI();
break;
case "Slot_4":
CurArray = LoadData("Slot4.txt");
SetUI();
break;
default:
break;
}
}
}
public string[] LoadData(string filename)
{
string path = DirectPath + "/" + filename;
Debug.Log("readfile:" + path);
string[] AllText = File.ReadAllLines(path);//split by lines
return AllText;
}
public void SetUI()
{
string[] pack = CurArray;
TextParser.TextParser_Instance.LineIndex = int.Parse(pack[0]);
BGM_Management.BGM_Manager_Instance.SetBGM(pack[1]);
UI_Manager.UIManager_Instance.SetBackground(pack[2], 1f);
UI_Manager.UIManager_Instance.CharacterDisplay("all", "off");
if(pack[3]!="null")
{
UI_Manager.UIManager_Instance.SetCharacter(pack[3], "left");
}
if (pack[3] != "null")
{
UI_Manager.UIManager_Instance.SetCharacter(pack[3], "center");
}
if (pack[3] != "null")
{
UI_Manager.UIManager_Instance.SetCharacter(pack[3], "right");
}
Setting_Controller.Setting_Instance.SetBGMVolume();
Setting_Controller.Setting_Instance.SetTextSpeed();
}
之前的savingmanager也要加入状态的判断,这里就不写了。把ButtonManager更新一下,添加对应的按钮事件,基本完成了,可以运行看看效果
case "Save_BTN":
State_Manager.SaveLoadState = SLState.Saving;
UI_Manager.UIManager_Instance.SavingPanel.SetActive(true);
GameObject.Find("SaveDataObject").AddComponent<SaveData>();
break;
case "Load_BTN":
State_Manager.SaveLoadState = SLState.Loading;
UI_Manager.UIManager_Instance.SavingPanel.SetActive(true);
break;
case "Setting_BTN":
UI_Manager.UIManager_Instance.SettingPanel.SetActive(true);
UI_Manager.UIManager_Instance.InitSettingWindow();
break;
基本就完成了,暂时就到这里吧,开学了,有时间再更新