[UE4]Editor Utility Widgetでツール・エディタ拡張を作る際のUndo/Redoの実装方法について

# はじめに
UE4.22で追加されたEditor Utility Widget機能を使って、エディタ上で動作するツールやエディタ拡張を作る方法について説明しました。ツール・エディタ拡張を作ることで、面倒な作業を半自動化することができるため作業効率が格段に向上します。
[[UE4]エディタ上で動作するツール・エディタ拡張をUMGで簡単に作れる Editor Utility Widget について](https://qiita.com/EGJ-Kaz_Okada/items/9f530db3b53d0fde3f20)

ただし、GUIツールではよくある話なのですが、**Undo（元に戻す Ctrl+Z）/Redo（やり直し Ctrl+Y）が用意されていないツールはあまり好まれません。**人間は間違いを犯す生き物なので、操作ミスなどで意図せぬ処理をしてしまうことは稀によくあります。そのため、ツールの影響範囲や機能性が大きくなればなるほど、Undo/Redoの実装要望が強くなります。例えば、[「ボタンを押すと全てのStaticMeshアセットのLODを作成・変更するツール」](http://api.unrealengine.com/JPN/Engine/Editor/ScriptingAndAutomation/HowTos/CreatingLODs/index.html)がUndo/Redoを持っていないと作業者はとても不安になります。そして、最終的には「安心して作業ができない！」と言われ、誰にも使われないツールになってしまいます。悲しい。

そこで、Editor Utility Widgetでツールを作る際にどのようにしてUndo/Redoを実装するのかについて説明します。と言っても、Undo/Redo実装用のBPノードが既に用意されているのでとても簡単です。

なお、**今回紹介するUndo/Redo用の機能はエディタ限定の機能です。実際のゲーム中では使用できません。**ご注意ください。

# Undo/Redoの実装方法
今回の説明用に「ボタンを押すとカウントが増える」「ボタンを押すとテキストの色が変わる」ボタンを持つ謎ツール「EUW_UndoRedo」をEditor Utility Widgetで作りました。どのようにしてこのツールにUndo/Redo機能を実装するのかを説明します。
![cgi9k-9mkxj.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/363889/e85ec68a-7f64-99d5-e50d-0e07559543f5.gif)

## Undo/Redoの実装例
細かい説明の前に、実装したBPノードをまずお見せします。まずはテキストの色を変えるボタンの実装です。見慣れないノードがありますが、とてもシンプルな形になっています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/363889/b466096f-770f-2fa3-bc72-01df87149b8b.png)
次に、カウントが増えるボタンの実装です。先程の見慣れないノードが見慣れないマクロに変わっています。更にシンプルになりました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/363889/840ca5d7-1de5-3aad-fb28-7f40232847e9.png)
これだけでUndo/Redoの対応が完了です。ね、簡単でしょ？

## Undo/Redoの実装例の解説
Undo/Redoを実装するためには、以下の対応が必要になります。

1. **Begin Transactionノード**をUndo/Redoの対象にしたい処理の前に実行
2. その後、**Transact Objectノード**でUndo/Redoの対象にしたいオブジェクトを登録
3. **End Transactionノード**をUndo/Redoの対象にしたい処理の後に実行

または、標準で用意されてる**Create Transactionマクロ**と**Transact Objectノード**を使用する必要があります
(下画像はCreate Transactionマクロの中身)。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/363889/58948d1d-ec41-a99b-bbd6-9fd927b7d832.png" width=100%>

### Begin Transactionノード
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/363889/41a5c7d6-730f-9bfd-3673-7621f81b5a7e.png)
このノードが実行されると、Undo/Redo実装用の監視機能( UTransactor )が開始されます。そのため、このノードをまず始めに呼ぶ必要があります。

■ Context：
指定する文字列は特に制限はありません。ただドキュメントではUndo/Redo処理を行うツール名を指定することが推奨されていますので、今回の場合はEditor Utility Widgetアセットの名前を指定することになります。
■ Description：
指定する文字列は特に制限はありません。ただ後述のUndo Historyに表示される文字列はここで指定したものになるため、DescriptionにはUndo/Redo対象の処理がどのような処理内容なのかを記述することが推奨されています。
■ Primary Object：
「Undo/Redo対象の処理が作用するオブジェクト」と説明にはありますが、設定せずに空のままでも全く問題ありません。ただし、オブジェクトを指定した場合は後述のUndo Historyに表示される文字列にそのオブジェクト名が追加されます。

参考： https://api.unrealengine.com/INT/BlueprintAPI/Transactions/BeginTransaction/index.html

### Transact Objectノード
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/363889/48402d2b-b9fd-9f72-2cd4-374bb392516e.png)
このノードを使って、Undo/Redoによってパラメータを戻したいオブジェクト( UObject )を登録する必要があります。そして、Transact Objectノードに登録したオブジェクトに対して行われた変更処理は全て記録され、Undo/Redo処理時にその内容が用いれられます。

テキストの色を変える処理の場合は、Undo/Redoの対象にしたいオブジェクトはTextBlockなので、Transact ObjectノードにTextBlockの参照を渡しています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/363889/729f4aba-1ff4-166d-3ae8-4ed7e754d4cc.png)
カウントが増える処理の場合は、Transact ObjectノードにTextBlockとSelf( Editor Utility Widgetアセット自身 )の参照を渡しています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/363889/d11e7b6b-6580-1a22-328f-58924bb91fcc.png)
ここで「あれ？Counter変数をTransact Objectノードに渡すのではダメなの？」と思った方がいるかと思います。何故Selfを渡しているかというと、intは[ UObject ](http://api.unrealengine.com/JPN/Programming/UnrealArchitecture/Objects/index.html)継承のクラスではないからです。そのため、Counter変数自身ではなくCounter変数を管理しているEditor Utility Widgetアセットを渡しています。…よく分からない場合はそういうものだと思うのが良いかと思います。

なお、このノードはBegin TransactionとEnd Transactionの間に実行する必要があるのでご注意ください。

参考：https://api.unrealengine.com/INT/BlueprintAPI/Transactions/TransactObject/index.html

### End Transaction / Cancel Transactinノード
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/363889/c48da960-786d-9a22-cac8-c1d6d02953d7.png)
Undo/Redoの対象となる処理が完了したらEnd Transactionノードを使用します。これでBegin Transactionノードから始まった監視機能が終了します。もし呼び忘れた場合、Undo/Redoが正常に働かなくなり大きな不具合の原因になる可能性があります。十分注意することを推奨します。

なお、もしBegin Transactionノード実行後にUndo/Redoの対象にすることをキャンセルしたい場合は**Cancel Transactionノード**を使用することになります。引数のIndexには、Begin TransactionノードのReturn Valueの値を入れる必要があります。


これでUndo/Redoの実装する上で必要なノードの説明は以上です。ここで改めて実装例の画像を確認したり、実際に手を動かしてみると理解がスムーズに進むかと思います！

# おまけ：Undo History
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/363889/0c3bca4d-2b06-c3b8-d762-05446b65eedc.png) ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/363889/7c272233-d3ad-c38c-d878-71e897332c5b.png)

エディタ左上にあるツールバーから、Undo/Redo用に記録された各処理のリストを確認できるUndo Historyを開くことができます。このツールを使うことで、Undo/Redoの対象の処理がどのような順序で並んでいるか、その処理で変更された要素・内容はどんなものなのかなどを確認することが可能です。

Begin Transactionの説明で触れた通り、Begin TransactionノードのDescriptionで指定した文字列がUndo Historyで表示されるリストに使われます。例えば、Begin TransactionノードのPrimary ObjectにSelfを渡した場合はこのように表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/363889/3c5e5ed9-569b-75fe-43af-bb692e2a964c.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/363889/cef098a5-5db0-8be8-a48b-4e03386b7cf0.png)

Undo HistoryにはUE4エディタにおける各編集作業のUndo/Redo記録結果も並びます。例えばBPのノードを動かすとどのように記録されるのかを確認したりすると面白いかと思います。

また、Undo Historyの左下には記録された項目の数とメモリ使用量が表示されています。もし諸事情でPCに積まれているメモリ容量が少なくてカツカツな場合はここに表示される値に注意した方が良いのかもしれません。

# 最後に
標準で用意されているTransaction系のノードを使用することで、Editor Utility Widgetでツールやエディタ拡張を作る際にUndo/Redoを実装することが可能です。たしかに少し手間は掛かってしまいますが、これだけで作業者が安心して使える良いツールにすることができますので積極的に使っていきましょう！