我如何用Unity3D实现一个Galgame框架(存档读档与设置面板的思路)

开学了开学了,虽然后期很浪,但总的来说过了一个收获颇多的暑假。这里讲一讲最后实现设置面板和存档读档的思路


设置面板

可以采用之前类神庙逃亡项目的思路来写:
在主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来控制,如图:
我如何用Unity3D实现一个Galgame框架(存档读档与设置面板的思路)_第1张图片
在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));
    }

我如何用Unity3D实现一个Galgame框架(存档读档与设置面板的思路)_第2张图片
然后是读档,存档和读档用的是一个面板,所以写一个状态量,点击不同的按钮进入不同的(存档,读挡)状态,写一个协程,隔个零点几秒检测一次状态,直接上代码了:

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;

基本就完成了,暂时就到这里吧,开学了,有时间再更新

你可能感兴趣的:(Unity)