概要
Undo(Ctrl+Z), Redo(Ctrl+Y)を備えた、簡単なメモ帳を作ってみることにします。
手順
①Input Fieldを作る(図1)
Unity2020では、Hierarchyを右クリックして”Create>UI>Input Field”です。

②コードを書く
次のコードを”NotePad.cs”として保存。適当なオブジェクトにアタッチします(図2)。
using UnityEngine;
using UnityEngine.UI;
public class NotePad : MonoBehaviour
{
void Start()
{
// 配列を初期化
_Record_InputField = new string[limit];
// Input Fieldを初期化
if (!inputField) inputField = FindObjectOfType<InputField>();
}
void Update()
{
//キー入力(Escキーを割り当てるのは推奨しません。InputFieldのOnValueChangedが誤作動します。)
if (Input.GetKeyDown(KeyCode.RightControl) && Application.platform == RuntimePlatform.WindowsEditor) Undo();
if (Input.GetKey(KeyCode.LeftControl) && Input.GetKeyDown(KeyCode.Z)) Undo();
if (Input.GetKey(KeyCode.LeftControl) && Input.GetKeyDown(KeyCode.Y)) Redo();
}
//メモ帳機能
[SerializeField] private InputField inputField;
private static int limit = 20;//20回までUndo可能
[SerializeField] private string[] _Record_InputField; // 記録用の配列
[SerializeField] int _record_Index = 0; // 読み込む(書き込む)Indexの値
[SerializeField] int _normal_Limit = 0; // 順方向に戻せる残り回数
[SerializeField] int _reverse_Limit = 0; // 逆方向に戻せる残り回数
bool _StopRecording = false; //記録を停止する(InputFieldを戻した時の操作が記録されるとマズイ)
//Redo,Undo機能
/// <summary>
/// 値の変化を記録する
/// </summary>
public void RecordInput()
{
if (!_StopRecording) //Redo・Undo→記録しない
{
_record_Index++; _record_Index %= limit; //0~[limit]の間で配列をループさせて使い回す
_Record_InputField[_record_Index] = inputField.text;
if (_normal_Limit < limit - 1) _normal_Limit++; //[limit]回までUndoできる。
_reverse_Limit = 0;
Debug.Log("Record");
}
}
/// <summary>
/// 戻る
/// </summary>
private void Undo()
{
if (_normal_Limit > 0)
{
_normal_Limit--;
_record_Index += limit; _record_Index--; _record_Index %= limit;
_StopRecording = true;
inputField.text = _Record_InputField[_record_Index];
_StopRecording = false;
if (_reverse_Limit < limit - 1) _reverse_Limit++;
}
}
/// <summary>
/// 取り消し
/// </summary>
private void Redo()
{
if (_reverse_Limit > 0)
{
_reverse_Limit--;
_record_Index++; _record_Index %= limit;
_StopRecording = true;
inputField.text = _Record_InputField[_record_Index];
_StopRecording = false;
if (_normal_Limit < limit - 1) _normal_Limit++;
}
}
}

③InputField側の設定をする
さっき作ったInputFieldを選択。OnValueChangedの項目にいまアタッチしたスクリプトの”NotePad.RecordInput()”を設定します。

④動作確認
下の動画のように、入力文字に対し、”Ctrl+Z(エディタの場合は右Ctrlキー)”で戻る機能が、”CTRL+Y”でやり直す機能が動くはずです。
これで簡易メモ帳の完成です。(Playerprefs.SetString等を使えば保存機能も作れます。)
追記
・2022/02/07 (月)
コードを書き直しました。手順③を飛ばして手順①②だけで使えるようになりました。
using UnityEngine;
using UnityEngine.UI;
public class NotePad : MonoBehaviour
{
void Start()
{
// 配列を初期化
_Record_InputField = new string[limit];
// Input Fieldを初期化
if (!inputField) inputField = FindObjectOfType<InputField>();
inputField.onValueChanged.AddListener((_) => RecordInput());
}
void Update()
{
//キー入力(Escキーを割り当てるのは推奨しません。InputFieldのOnValueChangedが誤作動します。)
if (Input.GetKeyDown(KeyCode.RightControl) && Application.platform == RuntimePlatform.WindowsEditor) Undo();
if (Input.GetKey(KeyCode.LeftControl) && Input.GetKeyDown(KeyCode.Z)) Undo();
if (Input.GetKey(KeyCode.LeftControl) && Input.GetKeyDown(KeyCode.Y)) Redo();
}
//メモ帳機能
[SerializeField] private InputField inputField;
private static int limit = 20;//20回までUndo可能
[SerializeField] private string[] _Record_InputField; // 記録用の配列
[SerializeField] int _record_Index = 0; // 読み込む(書き込む)Indexの値
[SerializeField] int _normal_Limit = 0; // 順方向に戻せる(Redo)残り回数
[SerializeField] int _reverse_Limit = 0; // 逆方向に戻せる(Undo)残り回数
//Redo,Undo機能
/// <summary>
/// 値の変化を記録する
/// </summary>
public void RecordInput()
{
_record_Index++; _record_Index %= limit; //0~[limit]の間で配列をループさせて使い回す
_Record_InputField[_record_Index] = inputField.text;
if (_normal_Limit < limit - 1) _normal_Limit++; //[limit]回までUndoできる。
_reverse_Limit = 0;
Debug.Log("Record");
}
/// <summary>
/// 戻る
/// </summary>
private void Undo()
{
if (_normal_Limit > 0)
{
_normal_Limit--;
_record_Index += limit; _record_Index--; _record_Index %= limit;
inputField.SetTextWithoutNotify(_Record_InputField[_record_Index]);
if (_reverse_Limit < limit - 1) _reverse_Limit++;
}
}
/// <summary>
/// 取り消し
/// </summary>
private void Redo()
{
if (_reverse_Limit > 0)
{
_reverse_Limit--;
_record_Index++; _record_Index %= limit;
inputField.SetTextWithoutNotify(_Record_InputField[_record_Index]);
if (_normal_Limit < limit - 1) _normal_Limit++;
}
}
}