【Unity】続×3・ノベルゲームの雛形①
前回の記事はこちら。
前回はスキップ機能の実装をしました。
今回実装するもの
・クイックセーブ・ロード機能
今回は簡易的なセーブ・ロード機能を作成するに留めておきます。
また、今回からスクリプトファイルを増やします。
PlayerPrefsUtility
まずは文章のセーブから実装していきたいと思います。
ただ、List型やDictionary型はそのままだとPlayerPrefsに保存できません。
なのでなんらかの形でデータをシリアライズする必要があります。
自分で実装してもいいのですが、既に作成してくださっている方がいらっしゃいましたので、それを使用させていただこうかと思います。
新しいスクリプトを作成し、名前を「PlayerPrefsUtility」にします。
そして上記の記事の二つ目のコードをコピーしてPlayerPrefsUtilityスクリプトに書き込みます。
最後にビルドして、エラーが出なければOKです。
文章のセーブ
準備が整ったところで、文章のセーブ機能の実装を行っていきます。
そろそろGameManagerクラスが重たくなってきたので、セーブ機能は別クラスにまとめようと思います。
その前にGameManagerクラスに追記します。
// パラメーターを追加
private int _subScenePos = 0;
public Dictionary<string, object> params_ { get { return _params; } }
public Dictionary<string, object> globalParams { get { return _globalparams; } }
public Dictionary<string, Dictionary<string, bool>> alreadyReadFlags { get { return _alreadyReadFlags; } }
public string readingSubSceneName { get { return _readingSubSceneName; } }
public int subScenePos { get { return _subScenePos; } }
// メソッドを変更
private void JumpTo(string parameter)
{
parameter = GetParameterForCommand(parameter);
CheckAlreadyRead(parameter);
ChangeMainTextColor();
_pageQueue = ListToQueue(_subScenes[parameter]);
_subScenePos = 0;
}
private void ReadLine(string text)
{
/* 省略 */
mainText.text = "";
_charQueue = SeparateString(main);
StartCoroutine(ShowChars(captionSpeed));
backLog.content.GetComponentInChildren<Text>().text +=
Environment.NewLine + Environment.NewLine;
_subScenePos++;
}
「SaveManager」スクリプトを作成してください。そこに以下のように書き込みます。
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class SaveManager : MonoBehaviour
{
private const string SAVEKEY_SCENE_NAME = "SaveKeySceneName";
private const string SAVEKEY_SUBSCENE_NAME = "SaveKeySubSceneName";
private const string SAVEKEY_SUBSCENE_POS = "SaveKeySubScenePos";
private const string SAVEKEY_PARAMS = "SaveKeyParams";
private const string SAVEKEY_GLOBAL_PARAMS = "SaveKeyGlobalParams";
private const string SAVEKEY_LOADED_SCENE = "SaveKeyLoadedScene";
private const string SAVEKEY_ALREADY_READ = "SaveKeyAlreadyRead_";
[SerializeField]
private GameManager gameManager;
[SerializeField]
private KeyCode saveKey = KeyCode.K;
private void Update()
{
PushSaveButton();
}
/**
* セーブ用のキー(ボタン)を押した時の処理
*/
private void PushSaveButton()
{
if (Input.GetKeyUp(saveKey))
{
Save();
}
}
/**
* セーブの処理
*/
private void Save()
{
string sceneName = SceneManager.GetActiveScene().name;
PlayerPrefsUtility.Save(SAVEKEY_SCENE_NAME, sceneName);
PlayerPrefsUtility.Save(SAVEKEY_SUBSCENE_NAME, gameManager.readingSubSceneName);
PlayerPrefsUtility.Save(SAVEKEY_SUBSCENE_POS, gameManager.subScenePos);
PlayerPrefsUtility.SaveDict(SAVEKEY_PARAMS, gameManager.params_);
PlayerPrefsUtility.SaveDict(SAVEKEY_GLOBAL_PARAMS, gameManager.globalParams);
List<string> loadedScenes = new List<string>();
foreach (KeyValuePair<string, Dictionary<string, bool>> pair in gameManager.alreadyReadFlags)
{
PlayerPrefsUtility.SaveDict(SAVEKEY_ALREADY_READ + pair.Key, pair.Value);
loadedScenes.Add(pair.Key);
}
PlayerPrefsUtility.SaveList(SAVEKEY_LOADED_SCENE, loadedScenes);
}
}
ただしこれだけだと実行しても本当にセーブできているかわかりません。
文章のロード
という訳で続けてロード機能も実装していきたいと思います。
SaveManagerスクリプトに以下を書き込んでください。
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class SaveManager : MonoBehaviour
{
private const string SAVEKEY_SCENE_NAME = "SaveKeySceneName";
private const string SAVEKEY_SUBSCENE_NAME = "SaveKeySubSceneName";
private const string SAVEKEY_SUBSCENE_POS = "SaveKeySubScenePos";
private const string SAVEKEY_PARAMS = "SaveKeyParams";
private const string SAVEKEY_GLOBAL_PARAMS = "SaveKeyGlobalParams";
private const string SAVEKEY_LOADED_SCENE = "SaveKeyLoadedScene";
private const string SAVEKEY_ALREADY_READ = "SaveKeyAlreadyRead_";
public const string SAVEKEY_ISLOAD = "SaveKeyIsLoad";
[SerializeField]
private GameManager gameManager;
[SerializeField]
private KeyCode saveKey = KeyCode.K;
[SerializeField]
private KeyCode loadKey = KeyCode.L;
private void Update()
{
PushLoadButton();
PushSaveButton();
}
/**
* セーブ用のキー(ボタン)を押した時の処理
*/
private void PushSaveButton()
{
if (Input.GetKeyUp(saveKey))
{
Save();
}
}
/**
* セーブの処理
*/
private void Save()
{
string sceneName = SceneManager.GetActiveScene().name;
PlayerPrefsUtility.Save(SAVEKEY_SCENE_NAME, sceneName);
PlayerPrefsUtility.Save(SAVEKEY_SUBSCENE_NAME, gameManager.readingSubSceneName);
PlayerPrefsUtility.Save(SAVEKEY_SUBSCENE_POS, gameManager.subScenePos);
PlayerPrefsUtility.SaveDict(SAVEKEY_PARAMS, gameManager.params_);
PlayerPrefsUtility.SaveDict(SAVEKEY_GLOBAL_PARAMS, gameManager.globalParams);
List<string> loadedScenes = new List<string>();
foreach (KeyValuePair<string, Dictionary<string, bool>> pair in gameManager.alreadyReadFlags)
{
PlayerPrefsUtility.SaveDict(SAVEKEY_ALREADY_READ + pair.Key, pair.Value);
loadedScenes.Add(pair.Key);
}
PlayerPrefsUtility.SaveList(SAVEKEY_LOADED_SCENE, loadedScenes);
}
/**
* ロード用のキー(ボタン)を押した時の処理
*/
private void PushLoadButton()
{
if (Input.GetKeyUp(loadKey))
{
Load();
}
}
/**
* ロードの処理
*/
private void Load()
{
string sceneName = PlayerPrefsUtility.Load(SAVEKEY_SCENE_NAME, "");
if (sceneName == "") return;
PlayerPrefsUtility.Save(SAVEKEY_ISLOAD, 1);
ChangeNextScene(sceneName);
}
/**
* データをロードして、返す
*/
public SaveData LoadData()
{
SaveData data = new SaveData();
PlayerPrefsUtility.Save(SAVEKEY_ISLOAD, 0);
data.readingSubSceneName = PlayerPrefsUtility.Load(SAVEKEY_SUBSCENE_NAME, "");
data.subScenePos = PlayerPrefsUtility.Load(SAVEKEY_SUBSCENE_POS, 0);
data.params_ = PlayerPrefsUtility.LoadDict<string, object>(SAVEKEY_PARAMS);
data.globalparams = PlayerPrefsUtility.LoadDict<string, object>(SAVEKEY_GLOBAL_PARAMS);
List<string> loadedScene = PlayerPrefsUtility.LoadList<string>(SAVEKEY_LOADED_SCENE);
data.alreadyReadFlag = new Dictionary<string, Dictionary<string, bool>>();
foreach (string scene in loadedScene)
{
data.alreadyReadFlag[scene] = PlayerPrefsUtility.LoadDict<string, bool>(SAVEKEY_ALREADY_READ + scene);
}
return data;
}
/**
* 対応するシーンに切り替える
*/
private void ChangeNextScene(string parameter)
{
SceneManager.LoadSceneAsync(parameter);
}
public class SaveData
{
public string readingSubSceneName;
public int subScenePos;
public Dictionary<string, object> params_;
public Dictionary<string, object> globalparams;
public Dictionary<string, Dictionary<string, bool>> alreadyReadFlag;
}
}
GameManagerクラスを以下のように変更してください。
// パラメーターを追加
[SerializeField]
private SaveManager saveManager;
// メソッドを追加
/**
* 初期化中にデータをロードする
*/
private void InitLoad()
{
SaveManager.SaveData data = saveManager.LoadData();
_readingSubSceneName = data.readingSubSceneName;
_subScenePos = data.subScenePos;
_pageQueue = ListToQueue(_subScenes[_readingSubSceneName]);
_params = data.params_;
_globalparams = data.globalparams;
_alreadyReadFlags = data.alreadyReadFlag;
for (int i = 0; i < _subScenePos; i++)
_pageQueue.Dequeue();
ShowNextPage();
ChangeMainTextColor();
}
// メソッドを変更
private void Init()
{
Dictionary<string, bool> alreadyReadFlag = new Dictionary<string, bool>();
_text = LoadTextFile(textFile);
Queue<string> subScenes = SeparateString(_text, SEPARATE_SUBSCENE);
foreach (string subScene in subScenes)
{
if (subScene.Equals("")) continue;
Queue<string> pages = SeparateString(subScene, SEPARATE_PAGE);
string subSceneName = pages.Dequeue();
_subScenes[subSceneName] = pages.ToList<string>();
alreadyReadFlag[subSceneName] = false;
}
bool isLoad = ES3.Load<bool>(SAVEKEY_ISLOAD, false);
if (isLoad)
{
InitLoad();
return;
}
if (_globalparams == null)
_globalparams = new Dictionary<string, object>();
if (_alreadyReadFlags == null)
_alreadyReadFlags = new Dictionary<string, Dictionary<string, bool>>();
string sceneName = SceneManager.GetActiveScene().name;
if (!_alreadyReadFlags.ContainsKey(sceneName))
_alreadyReadFlags[sceneName] = ES3.Load(SAVEKEY_ALREADY_READ + sceneName, defaultValue : alreadyReadFlag);
_readingSubSceneName = _subScenes.First().Key;
_pageQueue = ListToQueue(_subScenes.First().Value);
ChangeMainTextColor();
ShowNextPage();
}
SaveManagerスクリプトをプレハブのGameManagerにアタッチしてください。そしてそれぞれのコンポーネントにオブジェクトを設定しておきましょう。
それでは実際にセーブしたりロードしたりしてみます。
実際に上の画像を見ていただければわかると思いますが、ロード後、背景が消えてしまっていますね。場合によってはBGMや立ち絵なども消えてしまうかと思います。これを次回以降何とかしたいと思います。
(正確には背景・前景のデータ保存を次回行っていきたいと思います)
GameManagerスクリプト
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using UnityEditor;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
public class GameManager : MonoBehaviour
{
private const char SEPARATE_SUBSCENE = '#';
private const char SEPARATE_PAGE = '&';
private const char SEPARATE_COMMAND = '!';
private const char SEPARATE_MAIN_START = '「';
private const char SEPARATE_MAIN_END = '」';
private const char OUTPUT_PARAM = '"';
private const char OUTPUT_GLOBAL_PARAM = '$';
private const char COMMAND_SEPARATE_PARAM = '=';
private const char COMMAND_SEPARATE_ANIM = '%';
private const string COMMAND_BACKGROUND = "background";
private const string COMMAND_FOREGROUND = "foreground";
private const string COMMAND_CHARACTER_IMAGE = "charaimg";
private const string COMMAND_BGM = "bgm";
private const string COMMAND_SE = "se";
private const string COMMAND_JUMP = "jump_to";
private const string COMMAND_SELECT = "select";
private const string COMMAND_WAIT_TIME = "wait";
private const string COMMAND_CHANGE_SCENE = "scene";
private const string COMMAND_BACK_LOG = "backlog";
private const string COMMAND_PARAM = "param";
private const string COMMAND_GLOBAL_PARAM = "globalp";
private const string COMMAND_BRANCH = "branch";
private const string COMMAND_TEXT = "_text";
private const string COMMAND_SPRITE = "_sprite";
private const string COMMAND_COLOR = "_color";
private const string COMMAND_SIZE = "_size";
private const string COMMAND_POSITION = "_pos";
private const string COMMAND_ROTATION = "_rotate";
private const string COMMAND_ACTIVE = "_active";
private const string COMMAND_DELETE = "_delete";
private const string COMMAND_ANIM = "_anim";
private const string COMMAND_PLAY = "_play";
private const string COMMAND_MUTE = "_mute";
private const string COMMAND_SOUND = "_sound";
private const string COMMAND_VOLUME = "_volume";
private const string COMMAND_PRIORITY = "_priority";
private const string COMMAND_LOOP = "_loop";
private const string COMMAND_FADE = "_fade";
private const string COMMAND_WRITE = "_write";
private const string COMMAND_CALC = "_calc";
private const string CHARACTER_IMAGE_PREFAB = "CharacterImage";
private const string SELECT_BUTTON_PREFAB = "SelectButton";
private const string SE_AUDIOSOURCE_PREFAB = "SEAudioSource";
[SerializeField]
private Image backgroundImage;
[SerializeField]
private Image foregroundImage;
[SerializeField]
private GameObject characterImages;
[SerializeField]
private GameObject selectButtons;
[SerializeField]
private Text mainText;
[SerializeField]
private Text nameText;
[SerializeField]
private ScrollRect backLog;
[SerializeField]
private GameObject nextPageIcon;
[SerializeField]
private AudioSource bgmAudioSource;
[SerializeField]
private GameObject seAudioSources;
[SerializeField]
private string spritesDirectory = "Sprites/";
[SerializeField]
private string prefabsDirectory = "Prefabs/";
[SerializeField]
private string audioClipsDirectory = "AudioClips/";
[SerializeField]
private string textFile = "Texts/Scenario";
[SerializeField]
private string animationsDirectory = "Animations/";
[SerializeField]
private string overrideAnimationClipName = "Clip";
[SerializeField]
private float captionSpeed = 0.2f;
[SerializeField]
private KeyCode skipKey = KeyCode.S;
[SerializeField]
private KeyCode alreadyReadSkipKey = KeyCode.R;
[SerializeField]
private float skipSpeed = 0.05f;
[SerializeField]
private Color FirstReadCaptionColor;
[SerializeField]
private Color alreadyReadCaptionColor;
[SerializeField]
private SaveManager saveManager;
private float _waitTime = 0;
private string _text = "";
private bool _isSkip = false;
private bool isStop { get { return !mainText.transform.parent.gameObject.activeSelf || backLog.gameObject.activeSelf; } }
private Dictionary<string, List<string>> _subScenes = new Dictionary<string, List<string>>();
private Queue<string> _pageQueue;
private Queue<char> _charQueue;
private List<Image> _charaImageList = new List<Image>();
private List<Button> _selectButtonList = new List<Button>();
private List<AudioSource> _seList = new List<AudioSource>();
private Dictionary<string, object> _params = new Dictionary<string, object>();
private static Dictionary<string, object> _globalparams;
private static Dictionary<string, Dictionary<string, bool>> _alreadyReadFlags;
private static string _readingSubSceneName;
private DataTable _dt = new DataTable();
private int _subScenePos = 0;
public Dictionary<string, object> params_ { get { return _params; } }
public Dictionary<string, object> globalParams { get { return _globalparams; } }
public Dictionary<string, Dictionary<string, bool>> alreadyReadFlags { get { return _alreadyReadFlags; } }
public string readingSubSceneName { get { return _readingSubSceneName; } }
public int subScenePos { get { return _subScenePos; } }
private void Start()
{
Init();
}
private void Update()
{
PushSkipButton();
if (_isSkip) return;
if (Input.GetMouseButtonDown(0)) OnClick();
if (Input.GetMouseButtonDown(1)) OnClickRight();
MouseScroll();
}
/**
* 初期化する
*/
private void Init()
{
Dictionary<string, bool> alreadyReadFlag = new Dictionary<string, bool>();
_text = LoadTextFile(textFile);
Queue<string> subScenes = SeparateString(_text, SEPARATE_SUBSCENE);
foreach (string subScene in subScenes)
{
if (subScene.Equals("")) continue;
Queue<string> pages = SeparateString(subScene, SEPARATE_PAGE);
string subSceneName = pages.Dequeue();
_subScenes[subSceneName] = pages.ToList<string>();
alreadyReadFlag[subSceneName] = false;
}
int isLoad = PlayerPrefsUtility.Load(SaveManager.SAVEKEY_ISLOAD, 0);
if (isLoad > 0)
{
InitLoad();
return;
}
if (_globalparams == null)
_globalparams = new Dictionary<string, object>();
if (_alreadyReadFlags == null)
_alreadyReadFlags = new Dictionary<string, Dictionary<string, bool>>();
string sceneName = SceneManager.GetActiveScene().name;
if (!_alreadyReadFlags.ContainsKey(sceneName))
_alreadyReadFlags[sceneName] = alreadyReadFlag;
_readingSubSceneName = _subScenes.First().Key;
_pageQueue = ListToQueue(_subScenes.First().Value);
ChangeMainTextColor();
ShowNextPage();
}
/**
* 終了時の処理
*/
private void Exit()
{
CheckAlreadyRead("");
#if UNITY_EDITOR
EditorApplication.isPlaying = false;
#endif
}
/**
* 文字列のリストをキューに変更する
*/
private Queue<string> ListToQueue(List<string> strs)
{
Queue<string> queue = new Queue<string>();
foreach (string l in strs) queue.Enqueue(l);
return queue;
}
/**
* クリックしたときの処理
*/
private void OnClick()
{
if (isStop) return;
if (_charQueue.Count > 0)
OutputAllChar();
else
{
if (_selectButtonList.Count > 0)
return;
if (!ShowNextPage())
{
Exit();
}
}
}
/**
* 右クリックした時の処理
*/
private void OnClickRight()
{
GameObject mainWindow = mainText.transform.parent.gameObject;
GameObject nameWindow = nameText.transform.parent.gameObject;
mainWindow.SetActive(!mainWindow.activeSelf);
if (nameText.text.Length > 0)
nameWindow.SetActive(mainWindow.activeSelf);
if (_charQueue.Count <= 0)
nextPageIcon.SetActive(mainWindow.activeSelf);
}
/**
* スクロールした時の処理
*/
private void MouseScroll()
{
if (backLog.gameObject.activeSelf && Input.mouseScrollDelta.y > 0 && backLog.verticalNormalizedPosition <= 0)
{
StopAllAnimation(false);
backLog.gameObject.SetActive(false);
}
if (!backLog.gameObject.activeSelf && Input.mouseScrollDelta.y < 0)
{
StopAllAnimation(true);
backLog.verticalNormalizedPosition = 0;
backLog.gameObject.SetActive(true);
EventSystem.current.SetSelectedGameObject(backLog.verticalScrollbar.gameObject);
}
}
/**
* スキップ用のキー(ボタン)を押した時の処理
*/
private void PushSkipButton()
{
if (!_isSkip && Input.GetKey(skipKey))
{
_isSkip = true;
StartCoroutine(Skip(skipSpeed));
}
if (!_isSkip && Input.GetKey(alreadyReadSkipKey))
{
if (IsAlreadyRead(_readingSubSceneName))
{
_isSkip = true;
StartCoroutine(Skip(skipSpeed));
}
}
if (Input.GetKeyUp(skipKey) || Input.GetKeyUp(alreadyReadSkipKey))
_isSkip = false;
}
/**
* スキップの処理
*/
private IEnumerator Skip(float wait)
{
while (true)
{
if (isStop || !_isSkip || _waitTime > 0 || _selectButtonList.Count > 0)
break;
OutputAllChar();
yield return new WaitForSeconds(wait);
if (!ShowNextPage())
{
Exit();
}
}
_isSkip = false;
yield break;
}
/**
* 文字送りするコルーチン
*/
private IEnumerator ShowChars(float wait)
{
while (true)
{
if (!isStop)
{
if (!OutputChar()) break;
}
yield return new WaitForSeconds(wait);
}
yield break;
}
/**
* 次の読み込みを待機するコルーチン
*/
private IEnumerator WaitForCommand()
{
float time = 0;
while (time < _waitTime)
{
if (!isStop) time += Time.deltaTime;
yield return null;
}
_waitTime = 0;
ShowNextPage();
yield break;
}
/**
* 音のフェードを行うコルーチン
*/
private IEnumerator FadeSound(AudioSource audio, float time, float volume)
{
float vo = (volume - audio.volume) / (time / Time.deltaTime);
bool isOut = audio.volume > volume;
while ((!isOut && audio.volume < volume) || (isOut && audio.volume > volume))
{
audio.volume += vo;
yield return null;
}
audio.volume = volume;
}
/**
* 1文字を出力する
*/
private bool OutputChar()
{
if (_charQueue.Count <= 0)
{
nextPageIcon.SetActive(true);
return false;
}
mainText.text += _charQueue.Dequeue();
return true;
}
/**
* 全文を表示する
*/
private void OutputAllChar()
{
StopCoroutine(ShowChars(captionSpeed));
while (OutputChar()) ;
_waitTime = 0;
nextPageIcon.SetActive(true);
}
/**
* 既読かどうかを返す
*/
private bool IsAlreadyRead(string subSceneName)
{
return _alreadyReadFlags[SceneManager.GetActiveScene().name][subSceneName];
}
/**
* 既読の時は色を変える
*/
private void ChangeMainTextColor()
{
if (IsAlreadyRead(_readingSubSceneName))
mainText.color = alreadyReadCaptionColor;
else
mainText.color = FirstReadCaptionColor;
}
/**
* 既読チェックと次に読み込むラベル名の保存
*/
private void CheckAlreadyRead(string nextSubSceneName)
{
_alreadyReadFlags[SceneManager.GetActiveScene().name][_readingSubSceneName] = true;
_readingSubSceneName = nextSubSceneName;
}
/**
* 次のページを表示する
*/
private bool ShowNextPage()
{
if (_pageQueue.Count <= 0) return false;
nextPageIcon.SetActive(false);
ReadLine(_pageQueue.Dequeue());
return true;
}
/**
* 文字列を指定した区切り文字ごとに区切り、キューに格納したものを返す
*/
private Queue<string> SeparateString(string str, char sep)
{
string[] strs = str.Split(sep);
Queue<string> queue = new Queue<string>();
foreach (string l in strs) queue.Enqueue(l);
return queue;
}
/**
* 文を1文字ごとに区切り、キューに格納したものを返す
*/
private Queue<char> SeparateString(string str)
{
char[] chars = str.ToCharArray();
Queue<char> charQueue = new Queue<char>();
foreach (char c in chars) charQueue.Enqueue(c);
return charQueue;
}
/**
* 1行を読み出す
*/
private void ReadLine(string text)
{
if (text[0].Equals(SEPARATE_COMMAND))
{
ReadCommand(text);
if (_selectButtonList.Count > 0) return;
if (_waitTime > 0)
{
StartCoroutine(WaitForCommand());
return;
}
ShowNextPage();
return;
}
text = ReplaceParameterForGame(text);
string[] ts = text.Split(SEPARATE_MAIN_START);
string name = ts[0];
string main = ts[1].Remove(ts[1].LastIndexOf(SEPARATE_MAIN_END));
nameText.text = name;
if (name.Equals(""))
{
nameText.transform.parent.gameObject.SetActive(false);
backLog.content.GetComponentInChildren<Text>().text += main;
}
else
{
nameText.transform.parent.gameObject.SetActive(true);
backLog.content.GetComponentInChildren<Text>().text += text;
}
mainText.text = "";
_charQueue = SeparateString(main);
StartCoroutine(ShowChars(captionSpeed));
backLog.content.GetComponentInChildren<Text>().text +=
Environment.NewLine + Environment.NewLine;
_subScenePos++;
}
/**
* ゲーム中パラメーターの置き換え
*/
private string ReplaceParameterForGame(string line)
{
string[] lines = line.Split(OUTPUT_PARAM);
for (int i = 1; i < lines.Length; i += 2)
{
if (lines[i][0] == OUTPUT_GLOBAL_PARAM)
lines[i] = _globalparams[lines[i].Remove(0, 1)].ToString();
else
lines[i] = _params[lines[i]].ToString();
}
return String.Join("", lines);
}
/**
* コマンドの読み出し
*/
private void ReadCommand(string cmdLine)
{
cmdLine = cmdLine.Remove(0, 1);
Queue<string> cmdQueue = SeparateString(cmdLine, SEPARATE_COMMAND);
foreach (string cmd in cmdQueue)
{
string[] cmds = cmd.Split((COMMAND_SEPARATE_PARAM + "").ToCharArray(), count: 3);
if (cmds[0].Contains(COMMAND_BACKGROUND))
SetBackgroundImage(cmds[0], cmds[1]);
if (cmds[0].Contains(COMMAND_FOREGROUND))
SetForegroundImage(cmds[0], cmds[1]);
if (cmds[0].Contains(COMMAND_CHARACTER_IMAGE))
SetCharacterImage(cmds[1], cmds[0], cmds[2]);
if (cmds[0].Contains(COMMAND_JUMP))
JumpTo(cmds[1]);
if (cmds[0].Contains(COMMAND_SELECT))
SetSelectButton(cmds[1], cmds[0], cmds[2]);
if (cmds[0].Contains(COMMAND_WAIT_TIME))
SetWaitTime(cmds[1]);
if (cmds[0].Contains(COMMAND_BGM))
SetBackgroundMusic(cmds[0], cmds[1]);
if (cmds[0].Contains(COMMAND_SE))
SetSoundEffect(cmds[1], cmds[0], cmds[2]);
if (cmds[0].Contains(COMMAND_CHANGE_SCENE))
ChangeNextScene(cmds[1]);
if (cmds[0].Contains(COMMAND_BACK_LOG))
WriteBackLog(cmds[1]);
if (cmds[0].Contains(COMMAND_PARAM))
SetParameterForGame(cmds[1], cmds[0].Replace(COMMAND_PARAM, ""), cmds[2], _params);
if (cmds[0].Contains(COMMAND_GLOBAL_PARAM))
SetParameterForGame(cmds[1], cmds[0].Replace(COMMAND_GLOBAL_PARAM, ""), cmds[2], _globalparams);
if (cmds[0].Contains(COMMAND_BRANCH))
CompareJumpTo(cmds[1], cmds[2]);
}
}
/**
* 対応するシーンに切り替える
*/
private void ChangeNextScene(string parameter)
{
parameter = GetParameterForCommand(parameter);
SceneManager.LoadSceneAsync(parameter);
}
/**
* 対応するラベルまでジャンプする
*/
private void JumpTo(string parameter)
{
parameter = GetParameterForCommand(parameter);
CheckAlreadyRead(parameter);
ChangeMainTextColor();
_pageQueue = ListToQueue(_subScenes[parameter]);
_subScenePos = 0;
}
/**
* 比較式がtrueだったら対応するラベルまでジャンプする
*/
private void CompareJumpTo(string method, string parameter)
{
if ((bool)CalcParameterForGame(method)) JumpTo(parameter);
}
/**
* 待機時間を設定する
*/
private void SetWaitTime(string parameter)
{
parameter = GetParameterForCommand(parameter);
_waitTime = float.Parse(parameter);
}
/**
* バックログに記述する
*/
private void WriteBackLog(string parameter)
{
parameter = GetParameterForCommand(parameter);
backLog.content.GetComponentInChildren<Text>().text += parameter + Environment.NewLine + Environment.NewLine;
}
/**
* 背景の設定
*/
private void SetBackgroundImage(string cmd, string parameter)
{
cmd = cmd.Replace(COMMAND_BACKGROUND, "");
SetImage(cmd, parameter, backgroundImage);
}
/**
* 前景の設定
*/
private void SetForegroundImage(string cmd, string parameter)
{
cmd = cmd.Replace(COMMAND_FOREGROUND, "");
SetImage(cmd, parameter, foregroundImage);
}
/**
* 立ち絵の設定
*/
private void SetCharacterImage(string name, string cmd, string parameter)
{
cmd = cmd.Replace(COMMAND_CHARACTER_IMAGE, "");
name = GetParameterForCommand(name);
Image image = _charaImageList.Find(n => n.name == name);
if (image == null)
{
image = Instantiate(Resources.Load<Image>(prefabsDirectory + CHARACTER_IMAGE_PREFAB), characterImages.transform);
image.name = name;
_charaImageList.Add(image);
}
SetImage(cmd, parameter, image);
}
/**
* 選択肢の設定
*/
private void SetSelectButton(string name, string cmd, string parameter)
{
cmd = cmd.Replace(COMMAND_SELECT, "");
name = GetParameterForCommand(name);
Button button = _selectButtonList.Find(n => n.name == name);
if (button == null)
{
button = Instantiate(Resources.Load<Button>(prefabsDirectory + SELECT_BUTTON_PREFAB), selectButtons.transform);
button.name = name;
button.onClick.AddListener(() => SelectButtonOnClick(name));
_selectButtonList.Add(button);
}
SetImage(cmd, parameter, button.image);
}
/**
* 選択肢がクリックされた
*/
private void SelectButtonOnClick(string label)
{
foreach (Button button in _selectButtonList) Destroy(button.gameObject);
_selectButtonList.Clear();
JumpTo('"' + label + '"');
ShowNextPage();
}
/**
* 画像の設定
*/
private void SetImage(string cmd, string parameter, Image image)
{
cmd = cmd.Replace(" ", "");
parameter = GetParameterForCommand(parameter);
switch (cmd)
{
case COMMAND_TEXT:
image.GetComponentInChildren<Text>().text = parameter;
break;
case COMMAND_SPRITE:
image.sprite = LoadSprite(parameter);
break;
case COMMAND_COLOR:
image.color = ParameterToColor(parameter);
break;
case COMMAND_SIZE:
image.GetComponent<RectTransform>().sizeDelta = ParameterToVector3(parameter);
break;
case COMMAND_POSITION:
image.GetComponent<RectTransform>().anchoredPosition = ParameterToVector3(parameter);
break;
case COMMAND_ROTATION:
image.GetComponent<RectTransform>().eulerAngles = ParameterToVector3(parameter);
break;
case COMMAND_ACTIVE:
image.gameObject.SetActive(ParameterToBool(parameter));
break;
case COMMAND_DELETE:
_charaImageList.Remove(image);
Destroy(image.gameObject);
break;
case COMMAND_ANIM:
ImageSetAnimation(image, parameter);
break;
}
}
/**
* スプライトをファイルから読み出し、インスタンス化する
*/
private Sprite LoadSprite(string name)
{
return Instantiate(Resources.Load<Sprite>(spritesDirectory + name));
}
/**
* パラメーターから色を作成する
*/
private Color ParameterToColor(string parameter)
{
string[] ps = parameter.Replace(" ", "").Split(',');
if (ps.Length > 3)
return new Color32(byte.Parse(ps[0]), byte.Parse(ps[1]),
byte.Parse(ps[2]), byte.Parse(ps[3]));
else
return new Color32(byte.Parse(ps[0]), byte.Parse(ps[1]),
byte.Parse(ps[2]), 255);
}
/**
* パラメーターからベクトルを取得する
*/
private Vector3 ParameterToVector3(string parameter)
{
string[] ps = parameter.Replace(" ", "").Split(',');
return new Vector3(float.Parse(ps[0]), float.Parse(ps[1]), float.Parse(ps[2]));
}
/**
* パラメーターからboolを取得する
*/
private bool ParameterToBool(string parameter)
{
string p = parameter.Replace(" ", "");
return p.Equals("true") || p.Equals("TRUE");
}
/**
* アニメーションを画像に設定する
*/
private void ImageSetAnimation(Image image, string parameter)
{
Animator animator = image.GetComponent<Animator>();
AnimationClip clip = ParameterToAnimationClip(image, parameter.Split(COMMAND_SEPARATE_ANIM));
AnimatorOverrideController overrideController;
if (animator.runtimeAnimatorController is AnimatorOverrideController)
overrideController = (AnimatorOverrideController)animator.runtimeAnimatorController;
else
{
overrideController = new AnimatorOverrideController();
overrideController.runtimeAnimatorController = animator.runtimeAnimatorController;
animator.runtimeAnimatorController = overrideController;
}
overrideController[overrideAnimationClipName] = clip;
animator.Update(0.0f);
animator.Play(overrideAnimationClipName, 0);
}
/**
* パラメーターからアニメーションクリップを生成する
*/
private AnimationClip ParameterToAnimationClip(Image image, string[] parameters)
{
string[] ps = parameters[0].Replace(" ", "").Split(',');
string path = animationsDirectory + SceneManager.GetActiveScene().name + "/" + image.name;
AnimationClip prevAnimation = Resources.Load<AnimationClip>(path + "/" + ps[0]);
AnimationClip animation;
#if UNITY_EDITOR
if (ps[3].Equals("Replay") && prevAnimation != null)
return Instantiate(prevAnimation);
animation = new AnimationClip();
Color startcolor = image.color;
Vector3[] start = new Vector3[3];
start[0] = image.GetComponent<RectTransform>().sizeDelta;
start[1] = image.GetComponent<RectTransform>().anchoredPosition;
Color endcolor = startcolor;
if (parameters[1] != "") endcolor = ParameterToColor(parameters[1]);
Vector3[] end = new Vector3[3];
for (int i = 0; i < 2; i++)
{
if (parameters[i + 2] != "")
end[i] = ParameterToVector3(parameters[i + 2]);
else end[i] = start[i];
}
AnimationCurve[,] curves = new AnimationCurve[4, 4];
if (ps[3].Equals("EaseInOut"))
{
curves[0, 0] = AnimationCurve.EaseInOut(float.Parse(ps[1]), startcolor.r, float.Parse(ps[2]), endcolor.r);
curves[0, 1] = AnimationCurve.EaseInOut(float.Parse(ps[1]), startcolor.g, float.Parse(ps[2]), endcolor.g);
curves[0, 2] = AnimationCurve.EaseInOut(float.Parse(ps[1]), startcolor.b, float.Parse(ps[2]), endcolor.b);
curves[0, 3] = AnimationCurve.EaseInOut(float.Parse(ps[1]), startcolor.a, float.Parse(ps[2]), endcolor.a);
for (int i = 0; i < 2; i++)
{
curves[i + 1, 0] = AnimationCurve.EaseInOut(float.Parse(ps[1]), start[i].x, float.Parse(ps[2]), end[i].x);
curves[i + 1, 1] = AnimationCurve.EaseInOut(float.Parse(ps[1]), start[i].y, float.Parse(ps[2]), end[i].y);
curves[i + 1, 2] = AnimationCurve.EaseInOut(float.Parse(ps[1]), start[i].z, float.Parse(ps[2]), end[i].z);
}
}
else
{
curves[0, 0] = AnimationCurve.Linear(float.Parse(ps[1]), startcolor.r, float.Parse(ps[2]), endcolor.r);
curves[0, 1] = AnimationCurve.Linear(float.Parse(ps[1]), startcolor.g, float.Parse(ps[2]), endcolor.g);
curves[0, 2] = AnimationCurve.Linear(float.Parse(ps[1]), startcolor.b, float.Parse(ps[2]), endcolor.b);
curves[0, 3] = AnimationCurve.Linear(float.Parse(ps[1]), startcolor.a, float.Parse(ps[2]), endcolor.a);
for (int i = 0; i < 2; i++)
{
curves[i + 1, 0] = AnimationCurve.Linear(float.Parse(ps[1]), start[i].x, float.Parse(ps[2]), end[i].x);
curves[i + 1, 1] = AnimationCurve.Linear(float.Parse(ps[1]), start[i].y, float.Parse(ps[2]), end[i].y);
curves[i + 1, 2] = AnimationCurve.Linear(float.Parse(ps[1]), start[i].z, float.Parse(ps[2]), end[i].z);
}
}
string[] b1 = { "r", "g", "b", "a" };
for (int i = 0; i < 4; i++)
{
AnimationUtility.SetEditorCurve(
animation,
EditorCurveBinding.FloatCurve("", typeof(Image), "m_Color." + b1[i]),
curves[0, i]
);
}
string[] a = { "m_SizeDelta", "m_AnchoredPosition" };
string[] b2 = { "x", "y", "z" };
for (int i = 0; i < 2; i++)
{
for (int j = 0; j < 3; j++)
{
AnimationUtility.SetEditorCurve(
animation,
EditorCurveBinding.FloatCurve("", typeof(RectTransform), a[i] + "." + b2[j]),
curves[i + 1, j]
);
}
}
if (!Directory.Exists("Assets/Resources/" + path))
Directory.CreateDirectory("Assets/Resources/" + path);
AssetDatabase.CreateAsset(animation, "Assets/Resources/" + path + "/" + ps[0] + ".anim");
AssetDatabase.ImportAsset("Assets/Resources/" + path + "/" + ps[0] + ".anim");
#elif UNITY_STANDALONE
animation = prevAnimation;
#endif
return Instantiate(animation);
}
/**
* 全てのアニメーションを一時停止・再開する
*/
private void StopAllAnimation(bool isStop)
{
StopAnimation(isStop, foregroundImage);
foreach (Image image in _charaImageList)
StopAnimation(isStop, image);
foreach (Button button in _selectButtonList)
StopAnimation(isStop, button.image);
}
/**
* アニメーションを一時停止・再開する
*/
private void StopAnimation(bool isStop, Image image)
{
Animator animator = image.GetComponent<Animator>();
if (isStop) animator.speed = 0;
else animator.speed = 1;
}
/**
* BGMの設定
*/
private void SetBackgroundMusic(string cmd, string parameter)
{
cmd = cmd.Replace(COMMAND_BGM, "");
SetAudioSource(cmd, parameter, bgmAudioSource);
}
/**
* 効果音の設定
*/
private void SetSoundEffect(string name, string cmd, string parameter)
{
cmd = cmd.Replace(COMMAND_SE, "");
name = GetParameterForCommand(name);
AudioSource audio = _seList.Find(n => n.name == name);
if (audio == null)
{
audio = Instantiate(Resources.Load<AudioSource>(prefabsDirectory + SE_AUDIOSOURCE_PREFAB), seAudioSources.transform);
audio.name = name;
_seList.Add(audio);
}
SetAudioSource(cmd, parameter, audio);
}
/**
* 音声の設定
*/
private void SetAudioSource(string cmd, string parameter, AudioSource audio)
{
cmd = cmd.Replace(" ", "");
parameter = GetParameterForCommand(parameter);
switch (cmd)
{
case COMMAND_PLAY:
audio.Play();
break;
case COMMAND_MUTE:
audio.mute = ParameterToBool(parameter);
break;
case COMMAND_SOUND:
audio.clip = LoadAudioClip(parameter);
break;
case COMMAND_VOLUME:
audio.volume = float.Parse(parameter);
break;
case COMMAND_PRIORITY:
audio.priority = int.Parse(parameter);
break;
case COMMAND_LOOP:
audio.loop = ParameterToBool(parameter);
break;
case COMMAND_FADE:
FadeSound(audio, parameter);
break;
case COMMAND_ACTIVE:
audio.gameObject.SetActive(ParameterToBool(parameter));
break;
case COMMAND_DELETE:
_seList.Remove(audio);
Destroy(audio.gameObject);
break;
}
}
/**
* 音声ファイルを読み出し、インスタンス化する
*/
private AudioClip LoadAudioClip(string name)
{
return Instantiate(Resources.Load<AudioClip>(audioClipsDirectory + name));
}
/**
* 音声にフェードをかける
*/
private void FadeSound(AudioSource audio, string parameter)
{
string[] ps = parameter.Replace(" ", "").Split(',');
StartCoroutine(FadeSound(audio, int.Parse(ps[0]), int.Parse(ps[1])));
}
/**
* ゲーム中パラメーターの設定
*/
private void SetParameterForGame(string name, string cmd, string parameter, Dictionary<string, object> _params)
{
cmd = cmd.Replace(" ", "");
name = GetParameterForCommand(name);
switch (cmd)
{
case COMMAND_WRITE:
_params[name] = GetParameterForCommand(parameter);
break;
case COMMAND_CALC:
_params[name] = CalcParameterForGame(parameter);
break;
}
}
/**
* ゲーム中パラメーターの演算処理
*/
private object CalcParameterForGame(string parameter)
{
parameter = GetParameterForCommand(parameter);
object result = _dt.Compute(parameter, "");
return result;
}
/**
* コマンド中の「""」に挟まれた値を取り出す
*/
private string GetParameterForCommand(string parameter)
{
parameter = parameter.Substring(parameter.IndexOf('"') + 1, parameter.LastIndexOf('"') - parameter.IndexOf('"') - 1);
return ReplaceParameterForGame(parameter.Replace(" ", ""));
}
/**
* テキストファイルを読み込む
*/
private string LoadTextFile(string fname)
{
TextAsset textasset = Resources.Load<TextAsset>(fname);
return textasset.text.Replace("\n", "").Replace("\r", "");
}
/**
* 初期化中にデータをロードする
*/
private void InitLoad()
{
SaveManager.SaveData data = saveManager.LoadData();
_readingSubSceneName = data.readingSubSceneName;
_subScenePos = data.subScenePos;
_pageQueue = ListToQueue(_subScenes[_readingSubSceneName]);
_params = data.params_;
_globalparams = data.globalparams;
_alreadyReadFlags = data.alreadyReadFlag;
for (int i = 0; i < _subScenePos; i++)
_pageQueue.Dequeue();
ShowNextPage();
ChangeMainTextColor();
}
}
今回記事を上げるのが大分遅くなってしまってすみませんでした。
次回もいつになるかわかりませんが、ゆっくりやって行こうと思いますので引き続きよろしくお願いいたします。
この記事が気に入ったらサポートをしてみませんか?