【Unity】UniRxを使って簡単にUIの画像をフワフワさせてみる

最近、仕事でもプライベートでもUniRxブームが自分の中で来ています。

今回はUniRxを使用してUIの画像をフワフワと上下に揺れるアニメーションを作成しました。
こちらはスプライトや３Dモデルでも、使う変数が違うくらいで考え方はほぼ一緒です

#前提条件

[UniRx](https://assetstore.unity.com/packages/tools/integration/unirx-reactive-extensions-for-unity-17276?aid=1101lIAt&utm_source=aff)をプロジェクトにインストール済み
Unity2018.2.21fでプロジェクトを作成

#今回記載すること
・UniRxを用いた簡単な上下に繰り返し移動するアニメーション

#記載しない（説明を省く）こと
・UniRxの基礎的な使い方
=>知りたい方はとりすーぷさんの[UniRx入門シリーズ 目次](https://qiita.com/toRisouP/items/00b8a5bb8e7b68e0686c)を見てください。これ以上UniRx初心者で役立つ資料を僕は見たことがないです。

・Unityのスクリプト基礎
最低限、UpdateとかStartが何なのかをわかっている前提で記載しています。
わからなければ検索をかけてください。

・uGUIの基礎
最低限、ImageとCanvasぐらいは見たことがある前提で記載しています。

#上下に揺らすために

スクリプトで上下に揺らすために調査したところ、

・Sin波(Mathf.Sin)を利用して毎フレーム計算する
・Mathf.PingPongを利用

で簡単に実現できそうです。

今回はSinを利用して上下に揺らしてみます。

#単純にふわふわさせてみる

以下のようにスクリプトを組んでみます。

```CharaImage.cs
using System;
using UnityEngine;
using UnityEngine.UI;
using UniRx;
using UniRx.Triggers;

/// <summary>
/// Chara image.
/// </summary>
public class CharaImage : MonoBehaviour
{
        /// <summary>
        /// Image
        /// </summary>
        [SerializeField]
        private Image image = null;

        /// <summary>
        /// Start
        /// </summary>
        private void Start()
        {
            float imageY = image.rectTransform.anchoredPosition.y; // Yの初期値
            float speed = 0.7f; //　移動スピード
            float moveY = 15; // 移動値

            // ふわふわ浮いてるようなアニメーション
            this.UpdateAsObservable()
                .Select(_ => image.rectTransform.anchoredPosition) // 画像のanchoredPositionを取得
                .Subscribe(p =>
                {
                    image.rectTransform.anchoredPosition = new Vector2(p.x, imageY + Mathf.Sin(Time.time * speed) * moveY);
                });
        }
}
    
```

上記スクリプトを揺らしたい画像があるコンポーネントにアタッチしてください。
※InspectorでSerializeFieldを指定したImageに、動かしたいImageをセットするのを忘れずに。

![fuwafuwa.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104377/c540f67a-51b9-7065-17a4-22e2f34b7649.gif)

※Gifがアップロード容量の問題でカットしていますが、ちゃんと上下に動きます

※購読停止処理をあえて抜いていますので、実際に使用する場合は以下のようにTakeUntilDestroy、Addto等でなにかしらの購読停止処理を必ず実装してください。

```cs:
this.UpdateAsObservable()
    .Select(_ => image.rectTransform.anchoredPosition)
    .Subscribe(p =>
    {
        image.rectTransform.anchoredPosition = new Vector2(p.x, imageY + Mathf.Sin(Time.time * speed) * moveY);
    })
    .AddTo(this.gameObject);
```

# クリックしたときに、ぴょこっとキャラを動かしてみる。

Imageをクリックしたときにぴょこっと動かしてみたいので、スクリプトに少し修正を加えます。

※EventTrigerをアタッチしてください
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104377/11123b4d-d36f-6312-b4a0-78959cc03e67.png)

```CharaImage.cs
using System;
using UnityEngine;
using UnityEngine.UI;
using UniRx;
using UniRx.Triggers;

/// <summary>
/// Chara image.
/// </summary>
public class CharaImage : MonoBehaviour
{
        /// <summary>
        /// Image
        /// </summary>
        [SerializeField]
        private Image image = null;

        /// <summary>
        /// Start
        /// </summary>
        private void Start()
        {
            float imageY = image.rectTransform.anchoredPosition.y; // Yの初期値
            float speed = 0.7f; //　移動スピード
            float moveY = 15; // 移動値

            // ふわふわ浮いてるようなアニメーション
            this.UpdateAsObservable()
                .Select(_ => image.rectTransform.anchoredPosition) // 画像のanchoredPositionを取得
                .Subscribe(p =>
                {
                    image.rectTransform.anchoredPosition = new Vector2(p.x, imageY + Mathf.Sin(Time.time * speed) * moveY);
                });

            // ObservablePointerClickTriggerをAddComponent
            var clickTrigger = this.gameObject.AddComponent<ObservablePointerClickTrigger>();
            
            // 画像をクリックしたときに、一定時間だけスピードを速くする
            clickTrigger
                .OnPointerClickAsObservable()
                .ThrottleFirst(TimeSpan.FromSeconds(1)) // クリック後1秒間はスキップするように
                .Select(_ => image.rectTransform.anchoredPosition)
                .Subscribe(p => 
                {
                    speed = 10;
                    moveY = 20;
                    // 画像の位置を初期値へ戻す
                    image.rectTransform.anchoredPosition = new Vector2(p.x, imageY);

                    // 500ミリ秒経過したら、初期値へ戻す
                    Observable
                        .Timer(TimeSpan.FromMilliseconds(500))
                        .Subscribe(_ => 
                        {
                            speed = 0.7f;
                            moveY = 15;
                            image.rectTransform.anchoredPosition = new Vector2(p.x, imageY);
                        });
                })
        }
}
    
```

こんな感じで、クリックするとぴょこっと動くようになりました。（かわいい）
![fuwafuwa.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104377/05118a91-a4d8-414a-23cf-2485b405a9af.gif)

speedとmoveYの値をいい感じに調整して、理想のぴょこ動きを実装してみてください。

#参考サイト

[Sin波で上下に揺らす](https://unity.wiki.fc2.com/wiki/Sin%E6%B3%A2%E3%81%A7%E4%B8%8A%E4%B8%8B%E3%81%AB%E6%8F%BA%E3%82%89%E3%81%99)
[unityのオブジェクトを上下に移動させる。](https://qiita.com/amano-kiyoyuki/items/893055610dc04a9e00eb)