UnrealEngine　SVGのベクターアニメーションを使ってワンランク上のUI表現を

環境：UE5.3、WebUI Plugin、Lottie 5.12.1

# はじめに
こういうアニメーションをUE上で使ってみようという内容のお話です。
<p class="codepen" data-height="410" data-default-tab="html,result" data-slug-hash="xxKdEjv" data-user="deliciousbrains" style="height: 410px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/deliciousbrains/pen/xxKdEjv">
  Lottie Animation Example</a> by deliciousbrains (<a href="https://codepen.io/deliciousbrains">@deliciousbrains</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

# ベクター画像とは？
ピクセルの集まりではなく、点や直線、曲線など数式の集まりで作られた画像の事です。
ベクター画像のメリットは
- 拡大しても画質が落ちない
- データサイズが小さい
- 変形が用意

などがあります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2778561/03dadfe4-4916-93a9-d314-29d369e3704c.png)
参照：http://www.persfreaks.jp/main/tool/conic/

# SVGとは？
SVG（Scalable Vector Graphics)は画像フォーマットの一種です。
XMLで記述でき、Webに適したベクターファイル形式です。

例：単純な矩形を描画するSVG
<p class="codepen" data-height="300" data-default-tab="html,result" data-slug-hash="rNodBWg" data-user="hrcvjbtz-the-bashful" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/hrcvjbtz-the-bashful/pen/rNodBWg">
  SVGBox</a> by 癒系 (<a href="https://codepen.io/hrcvjbtz-the-bashful">@hrcvjbtz-the-bashful</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

# Lottieとは？
さて、このSVGのHTMLを手でチクチク作っても良いのですがそんなの大変すぎるのでGUIのツールを使って作りたいですよね。そこで出てくるのがLottieファイルです。

Lottieは、JSONベースのアニメーション形式で、こんな感じのファイルです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2778561/406ce319-6a9f-29da-0d69-2a26c92fa904.png)

LottieはAfterEffectsというツールから出力でき、手軽にベクターアニメーションを作ることができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2778561/2a5c37f7-93a1-24d4-ad31-f07f16c55e03.png)
参照：https://zenn.dev/getgotgoto/articles/7db64306a93193


# 準備
### WebUIプラグインをUE5.3にインストール
https://github.com/tracerinteractive/UnrealEngine/releases

これを使うとHTMLの表示、Javascriptとのイベント通知などができるようになります。
ランチャーエンジンであればFreeTrial版で試せます。ソースコードがほしい場合でも15＄で購入できます。

インストール方法はドキュメントを見てください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2778561/bc9a7b56-1bc0-d916-6334-026b17d7fb41.png)

サンプルプロジェクトがついているのでこれで確認していきましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2778561/f8143888-465a-7605-fad0-80160f468daa.png)
プレイしてUEのアイコンが上下にふわふわしていたらHTMLを表示できています。
FPS表示とPlay、QUITボタン、音量調整のつまみがありますので
このサンプルでBPとのイベント通知の仕組みが確認することができます。

```javascript
jQuery(function()
{
    $("#playGame").click(function(e)
    {
        $("#logo").fadeOut(1000);
        $("#playGame").fadeOut(1000);
        $("#quitGame").fadeOut(1000);
        $("#volumeSlider").fadeOut(500);

        // executed in blueprints
        ue5("play");
    });

    $("#quitGame").click(function(e)
    {
        // executed in blueprints
        ue5("quit");
    });

    $("#volumeSlider").on("input", function(e)
    {
        // executed in blueprints
        ue5("volume", parseFloat($(this).val()));
    });

    // notify blueprint that browser is ready
    ue5("ready");

    // delay for 1 sec
    setTimeout(function()
    {
        // transmit data to the game
        ue5("print",
        {
            "browser": navigator.userAgent,
            "time": Date.now()
        });
    },
    1000);
});
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2778561/ef101765-ac96-ae40-fb75-185a5548e7c5.png)

### Lottie Playerをインストール
https://github.com/airbnb/lottie-web/tree/master

ここの
`build/player`フォルダを
`Contents/HTML`以下にコピーします。

playerじゃ分かりにくいのでlottieという名前に変更しました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2778561/a9a3a5f4-b356-8c22-910d-e1212f7c720d.png)

sample.htmlをちょちょっと改造します。

```html
<html>
<head>
<script>
// create the global ue5(...) helper function
"object"!=typeof ue&&(ue={}),uuidv4=function(){return"10000000-1000-4000-8000-100000000000".replace(/[018]/g,function(t){return(t^crypto.getRandomValues(new Uint8Array(1))[0]&15>>t/4).toString(16)})},ue5=function(r){return"object"!=typeof ue.interface||"function"!=typeof ue.interface.broadcast?(ue.interface={},function(t,e,n,o){var u,i;"string"==typeof t&&("function"==typeof e&&(o=n,n=e,e=null),u=[t,"",r(n,o)],void 0!==e&&(u[1]=e),i=encodeURIComponent(JSON.stringify(u)),"object"==typeof history&&"function"==typeof history.pushState?(history.pushState({},"","#"+i),history.pushState({},"","#"+encodeURIComponent("[]"))):(document.location.hash=i,document.location.hash=encodeURIComponent("[]")))}):(i=ue.interface,ue.interface={},function(t,e,n,o){var u;"string"==typeof t&&("function"==typeof e&&(o=n,n=e,e=null),u=r(n,o),void 0!==e?i.broadcast(t,JSON.stringify(e),u):i.broadcast(t,"",u))});var i}(function(t,e){if("function"!=typeof t)return"";var n=uuidv4();return ue.interface[n]=t,setTimeout(function(){delete ue.interface[n]},1e3*Math.max(1,parseInt(e)||0)),n});
</script>
<style>
#fpsMeter{font-family:'Open Sans',Arial,Helvetica,Verdana,sans-serif}
#fpsMeter{position:absolute}
#fpsMeter{top:3vh;left:5vh;color:#fff;font-size:18px;text-shadow:0 0 .2vh rgba(0,0,0,.5),0 0 1vh rgba(0,0,0,.5)}
</style>
<script src="https://code.jquery.com/jquery-2.2.4.min.js"></script>
<script src="lottie/lottie.min.js" type="text/javascript"></script>
<script>
// called in-game via blueprints
ue.interface.setFPS = function(fps)
{
    // set element text
    $("#fpsMeter").text(fps.toFixed(1) + " FPS");
};

// delay until browser is ready
jQuery(function()
{
    // notify blueprint that browser is ready
    ue5("ready");
});
</script>
</head>
<body style="background-color: transparent; -webkit-user-select: none;">
    <p id="fpsMeter">0 FPS</p>
<div style="width:100%;height:100%;background-color:#333;display:inline-block;" class="bodymovin" data-bm-path="data.json" data-bm-renderer="svg"></div>
</body>
</html>
```

ポイントは
```html
<script src="lottie/lottie.min.js" type="text/javascript"></script>
```
ここでプレイヤーのjsを指定して
```html
<div style="width:100%;height:100%;background-color:#333;display:inline-block;" class="bodymovin" data-bm-path="data.json" data-bm-renderer="svg"></div>
```
ここでjsonファイルを指定すると勝手に再生してくれます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2778561/4635d557-031c-0618-02f4-3922b002f505.png)

やったー！再生できました。

# 使い所
### NowLoading
<p class="codepen" data-height="300" data-default-tab="html,result" data-slug-hash="qBLobZQ" data-user="hrcvjbtz-the-bashful" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/hrcvjbtz-the-bashful/pen/qBLobZQ">
  NowLoading</a> by 癒系 (<a href="https://codepen.io/hrcvjbtz-the-bashful">@hrcvjbtz-the-bashful</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

https://lottiefiles.com/animations/nowloading-OaNd1Oftbn

### ボタン
https://lottiefiles.com/animations/start-button-CfmT34DYoX

### 画面遷移
https://lottiefiles.com/animations/transition-01-bKunH9juUm

画面遷移なんかをラスタ画像でやろうとするとテクスチャサイズがデカくなりがちですが
こういう全画面に表示する場合でも小さいサイズのデータできれいに表示できて良さそうです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2778561/3d9f95fa-e7ca-301e-41be-b364c26f19cc.png)
divタグのbackground-colorをtransparentにすれば背景透過もできました。

# 運用についての提案
UIを全部HTMLで作れるかもと検討してみたのですがHTMLでレイアウトを書くのは大変ですし、かといってホームページビルダーみたいなツールが吐く自動HTMLコードは保守が大変そうです。

ということで、基本はUMGで作ってボタンやカーソルなど部品として使うのがよさそうです。

# 処理負荷について
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2778561/f0702a69-d4a3-aa82-da84-e7e88cc35a34.png)

ちゃんと調べてないですがエディタで２１個再生してこれくらいなのでボタンとか数個程度なら心配する必要はなさそうです。

# まとめ
UMGのアニメーションやマテリアルで頑張ればやれるかもしれないですが
編集環境にAfterEffectsを使えるというのは強い気がします。
手軽にかっこいいアニメーションを作ってユーザーにいいね！
って思ってもらえるようなUIを作っていきたいですね。
自分はプログラマなのでデザインできないですが。

以上。