/// <summary> /// ノベル処理開始 /// </summary> public void Execute(NovelCommand.SharedData data = null, NovelCommand.SharedVariable variable = null) { if (variable == null) { variable = new NovelCommand.SharedVariable() { index = 0, handles = new Dictionary <string, GameObject>(), values = new Dictionary <string, string>(), groupValues = new Dictionary <string, string>(), groupHistory = new Queue <NovelHistory.GroupData>(), }; } if (data == null) { data = new NovelCommand.SharedData() { data = novelData, system = this, view = GetComponent <NovelView>(), }; } if (executer == null) { executer = new NovelExecuter(novelData, commandTypeDic); } StartCoroutine(RunCoroutine(data, variable)); }
/// <summary> /// ノベルコマンドDo処理コルーチン /// </summary> private IEnumerator RunCoroutine(NovelCommand.SharedData data, NovelCommand.SharedVariable variable) { yield return(executer.SetupCoroutine(data)); yield return(executer.RunCoroutine(variable, data.data.commands.Count)); yield break; }
/// <summary> /// 履歴追加 /// </summary> public void Add(NovelCommand.SharedData data, NovelCommand.SharedVariable variable, NovelCommand.NovelCommandInterface commandInstance) { // 履歴追加 histories.Insert(0, new HistoryData() { historyID = GetCurrentHistoryID() + 1, index = variable.index, command = commandInstance, }); // 初回データを用意(TextClear時に履歴追加したい為) if (logs.Count <= 0) { logs.Insert(0, new LogData()); logs[0].historyID = 0; } // コマンドID毎に処理 var commandData = data.info_.NovelInfoList[variable.index]; switch (commandData.id) { case (int)NovelCommandType.NameWrite: { logs[0].name += variable.FindValue(commandData.parameters[0]); } break; case (int)NovelCommandType.NameClear: { logs[0].name = ""; } break; case (int)NovelCommandType.TextWrite: { logs[0].text += variable.FindValue(commandData.parameters[0]); } break; case (int)NovelCommandType.TextClear: { if (MaxHistoryCount <= logs.Count) { // 古い順で履歴の削除 var log = logs[MaxHistoryCount - 1]; logs.RemoveAt(MaxHistoryCount - 1); histories.RemoveAll(d => d.historyID <= log.historyID); //groups.RemoveAll(d => d.historyID <= log.historyID); // groupsはGroup/GrouopEndコマンドの巻き戻しがある為徐々に増えていきます。 // 古いデータを消す為の処理をコメントアウトしていますが、この処理は正しく動作しません。 // GroupコマンドはUndoの際に開始位置を把握する為、単純に古いhistoryIDを消すとGroupのPush/Popが壊れます。 // GroupEndコマンドを消さないとGroupコマンドは消せません。 // この辺りは都合により未実装なので、執筆者githubより最新版をご確認ください。 } // 履歴を残すべきデータがあるかどうか確認 if (!string.IsNullOrEmpty(logs[0].name) || !string.IsNullOrEmpty(logs[0].text)) { logs.Insert(0, new LogData() { // historyIDはTextClear時の履歴リストの番号 historyID = histories[0].historyID, // 名前は前回のものを引き継ぐ name = logs[0].name, text = "", }); } } break; } }
/// <summary> /// セットアップコルーチン /// Include, Import, Groupコマンドの必要情報を集めます /// </summary> public IEnumerator SetupCoroutine(NovelCommand.SharedData data) { if (data == null) { yield break; } sharedData = data; sharedData.meta = new NovelMetaData(); sharedData.meta.groupDic = new Dictionary <string, NovelMetaData.GroupData>(); // セットアップ用に専用の変数を用意 // indexはRunCoroutine時とは別物である為(開始位置に関わらずSetupは全部見る) var variable = new NovelCommand.SharedVariable() { values = new Dictionary <string, string>(), handles = new Dictionary <string, UnityEngine.GameObject>(), index = 0, }; // セットアップが必要なコマンド var setupIDs = new int[] { (int)NovelCommandType.Include, (int)NovelCommandType.Import, (int)NovelCommandType.Group, }; while (variable.index < novelData.NovelInfoList.Count) { // 次に処理するコマンドデータ // 各コマンドはここから引数を取得します sharedData.model_ = novelData.NovelInfoList[variable.index]; // セットアップが必要なコマンドのみ実行 if (sharedData.model_.id.IsAny(setupIDs)) { // コマンドデータのIDから型を取得 Type commandType; if (commandTypeDic.TryGetValue(sharedData.model_.id, out commandType)) { // 型からインスタンスを生成 // このインスタンスはUndoを簡易的に行う為のものです command = Activator.CreateInstance(commandType) as NovelCommand.NovelCommandInterface; // 必ず1フレーム消費するので、これだとシビアなタイミングがまずい //yield return command.Do(sharedData, sharedVariable); // 同フレームで処理できるように自分でコルーチンを回す var coroutine = command.Do(sharedData, variable); while (coroutine.MoveNext()) { if (coroutine.Current != null) { // 何かしらが返ってきたら同フレームを保証する必要はなさそう var nestCoroutine = coroutine.Current as IEnumerator; if (nestCoroutine != null) { yield return(nestCoroutine); } } yield return(null); } } } variable.index++; } yield break; }
/// <summary> /// 実行コルーチン /// </summary> public IEnumerator RunCoroutine(NovelCommand.SharedVariable variable, int endIndex) { if (variable == null) { yield break; } if (sharedData == null) { yield break; } sharedVariable = variable; // handlesが存在する場合は引き継いでいる想定 // (最初には存在しなかった余計なオブジェクトも取得してしまうので処理しない) if (sharedVariable.handles.Count() <= 0) { // Scene上のGameObjectをハンドルに登録 foreach (var pair in sharedData.view.Canvases.GetComponentsInChildren <UnityEngine.Transform>(true)) { sharedVariable.handles[pair.name] = pair.gameObject; } } // 履歴登録が不要なID var noHistoryID = new int[] { (int)NovelCommandType.Import, (int)NovelCommandType.GroupRun, }; while (sharedVariable.index < novelData.NovelInfoList.Count && sharedVariable.index <= endIndex) { // 次に処理するコマンドデータ // 各コマンドはここから引数を取得します sharedData.model_ = novelData.NovelInfoList[sharedVariable.index]; // コマンドデータのIDから型を取得 Type commandType; if (commandTypeDic.TryGetValue(sharedData.model_.id, out commandType)) { // 型からインスタンスを生成 // このインスタンスはUndoを簡易的に行う為のものです command = Activator.CreateInstance(commandType) as NovelCommand.NovelCommandInterface; // 履歴保存 if (!sharedData.model_.id.IsAny(noHistoryID)) { sharedData.system_.history.Add( sharedData, sharedVariable, command); } // 必ず1フレーム消費するので、これだとシビアなタイミングがまずい //yield return command.Do(sharedData, sharedVariable); // 同フレームで処理できるように自分でコルーチンを回す var coroutine = command.Do(sharedData, sharedVariable); while (coroutine.MoveNext()) { if (coroutine.Current != null) { // 何かしらが返ってきたら同フレームを保証する必要はなさそう var nestCoroutine = coroutine.Current as IEnumerator; if (nestCoroutine != null) { yield return(nestCoroutine); } } yield return(null); } } sharedVariable.index++; } yield break; }