Interactive UI with UniRx

60
Interactive UI With UniRx

Transcript of Interactive UI with UniRx

Page 1: Interactive UI with UniRx

Interactive UI With UniRx

Page 2: Interactive UI with UniRx

• 株式会社トライフォート

• Unity/JSエンジニア

• 24歳

岩下 侑冬 Yuto Iwashita

@y120sb

Page 3: Interactive UI with UniRx

?UI今日話すこと

Page 4: Interactive UI with UniRx

MVなんちゃらの話ではない

• パターンの話は先人の記事を読もう

• MVVMとかMVPとか

Page 5: Interactive UI with UniRx

最近のUIに求められるもの

• リッチなアニメーション

• 手触り感

• パフォーマンス

Page 6: Interactive UI with UniRx

UIの実装はラクじゃない

• ビジネスロジックなんて大した問題ではない

• (個人的に)アプリの実装コストの7割はUI

• ドラッグ、アニメーション、etc…

Page 7: Interactive UI with UniRx

ラクしたい

Page 8: Interactive UI with UniRx

UniRxでできます!

Page 9: Interactive UI with UniRx

UniRxが解決する2つの問題

ロジック的ややこしさ

手触り感の表現

Page 10: Interactive UI with UniRx

ロジック的ややこしさ

Page 11: Interactive UI with UniRx

ロジック的ややこしさ

• 仕様が複雑で実装も複雑になるやつ

• そういうUIをユーザーに分からせるには  親切さも表現しなくてはならない         ➔ 色んな親切機能を追加する羽目に

Page 12: Interactive UI with UniRx

Page 13: Interactive UI with UniRx
Page 14: Interactive UI with UniRx

トゥモローアイランドhttp://tomorrowisland.trifort.jp/

• 弊社ソーシャルゲームタイトル第一弾

• 箱庭 × MO

• 開発期間1年くらい、          Unityエンジニアは最大で5人

Page 15: Interactive UI with UniRx

自分だけの箱庭を作ってみたり

仲間と一緒に 冒険したり

Page 16: Interactive UI with UniRx
Page 17: Interactive UI with UniRx
Page 18: Interactive UI with UniRx

サービス終了するけど…

• アプリを通してUniRxガッツリ!

• クローズしてもUniRxは便利

Page 19: Interactive UI with UniRx

アイテム合成UI

Page 20: Interactive UI with UniRx

• D&D

• ドラッグ通過判定

• 所要時間の表示

• アニメーション

複雑なUI

Page 21: Interactive UI with UniRx

• D&D

• ドラッグ通過判定

• 所要時間の表示

• アニメーション

複雑なUI

全部UniRxの守備範囲

Page 22: Interactive UI with UniRx

すべてをObservableへ

• IObservable<T>だらけにするとロジックからUI、アニメーションへの繋ぎが簡単になる

• 書く人によって違う非同期処理の書き方も 統一できる(C# event, callback, coroutine)

Page 23: Interactive UI with UniRx

所要時間の表示//ドラッグ開始 OnLongTapDragStart

.Select(tuple => tuple.Item2.Model.Recipe) //スロットにレシピが投入されるストリーム .SelectMany(recipe => OnThrowIn

.Select(slots => slots.Where(s => s.Thrown).Count())

.Select(count => new { recipe, count }) //ドラッグ開始時にレシピ1枚の場合の所要時間を表示するため .StartWith(new { recipe, count = 1 }))

//所要時間に変換 .Select(anon => anon.recipe.CalculateNeedTime(anon.count)) //ドラッグ終了まで待ち受け .TakeUntil(OnLongTapDragEnd) //繰り返し .Repeat().Subscribe(time => needTime.text = time)

Page 24: Interactive UI with UniRx

DragStart

SelectMany

Select

TakeUntil

Repeat

Page 25: Interactive UI with UniRx

アニメーション/// <summary>/// 箱のなかに吸い込まれる /// </summary>public IObservable<Unit> IntoBox (SlotBox box) {

const float duration = 0.45f;

//拡縮var tween = itemIconTransform.DOScale(Vector3.zero, duration);

//移動 itemIconTransform.DOMove(box.Position, duration);

//TweenerをIObservable<Tweener>に変換return tween.AsObservable();

}

Page 26: Interactive UI with UniRx

UniRxが解決する2つの問題

ロジック的ややこしさ

手触り感の表現

Page 27: Interactive UI with UniRx

手触り感の表現

Page 28: Interactive UI with UniRx

手触り感

• 最近の流行り(だと思う)

• 触ったら動く

• 動いたらそれが連鎖していく

Page 29: Interactive UI with UniRx

Page 30: Interactive UI with UniRx

Paperhttps://www.facebook.com/paper

• Facebook製

• ぬるぬる動く、触れる

• 見やすくはない

Page 31: Interactive UI with UniRx
Page 32: Interactive UI with UniRx

• 指の動きと連動して徐々に変化するUI

• どうつくる?

Page 33: Interactive UI with UniRx

動きの関係性を定義する

Page 34: Interactive UI with UniRx

1) アクションから発生する動きを見つける

2) 動きを細かく分解する

3) 分解した動きをストリームとして記述する

4) 動きを全て合成し、定義する

動きの関係性を定義する

Page 35: Interactive UI with UniRx

1) アクションから発生する動きを見つける

指が動く   要素が動く

Page 36: Interactive UI with UniRx

2) 動きを細かく分解する

• 指がY方向に動く   拡縮する

• 指がX方向に動く   X方向に動く

Page 37: Interactive UI with UniRx

2) 動きを細かく分解する

• 指がY方向に動く   拡縮する

• 指がX方向に動く   X方向に動く

• Y方向に拡縮する   Y方向の位置がずれる     ずれを戻す

• X方向に拡縮する   X方向の位置がずれる  ずれを戻す

Page 38: Interactive UI with UniRx

3) 分解した動きを記述する

• UniRxのストリームとして動きを記述する

Page 39: Interactive UI with UniRx

指がX方向に動く ⇣

X方向に動く

//IObservable<Vector2> var dragMoveStream = onDrag

.Select(e => container.InverseTransformVector(e.delta).x)

.Select(deltaX => new Vector2(deltaX, 0f));

Page 40: Interactive UI with UniRx

指がY方向に動く ⇣

拡縮する

//IObservable<Vector3> var scaleStream = onDrag

.Select(e => parent.localScale.y * ToScreenPosition(e.position).y / ToScreenPosition(e.position - e.delta).y)

.Select(scale => Vector3.one * scale);

Page 41: Interactive UI with UniRx

Y方向に拡縮する、ずれる ⇣

Y方向のずれを戻す

//IObservable<Vector2>var cancelSlipYStream = scaleStream

.Select(scale => defaultHeight * scale.x)

.Select(y => new Vector2(0f, y - parent.anchoredPosition.y));

Page 42: Interactive UI with UniRx

X方向に拡縮する、ずれる ⇣

X方向のずれを戻す

//IObservable<Vector2> var cancelSlipXStream = onDrag

.Select(e => container.InverseTransformPoint(e.position))

.Zip(scaleStream.Select(scale => parent.localScale.x / scale.x), (position, comparsionScale) => new { position, comparsionScale })

.Select(anon => (anon.comparsionScale - DefaultScale) * (-parent.anchoredPosition.x + anon.position.x))

.Select(deltaX => new Vector2(deltaX, 0f));

Page 43: Interactive UI with UniRx

//移動 Observable.Merge(dragMoveStream, cancelSlipYStream, cancelSlipXStream)

.Subscribe(move => parent.anchoredPosition += move)

.AddTo(this);

//拡縮scaleStream

.Subscribe(scale => parent.localScale = scale)

.AddTo(this);

4) 動きを全て合成し、定義する

Page 44: Interactive UI with UniRx
Page 45: Interactive UI with UniRx

Demo

Page 46: Interactive UI with UniRx

関係性の定義によるUIの実装

• 原因(指の動き)と起こる結果(拡縮、移動)を 細かく分解し、そのまま記述する

• 最後にUniRxで合成して適用する

Page 47: Interactive UI with UniRx

Settings編

Page 48: Interactive UI with UniRx
Page 49: Interactive UI with UniRx

Settingsの動きの関係性

• 指が動く   触れた要素が動く

• 要素が動く   真上にある要素が遅れて動く

• 要素が動く ➔ 真下にある要素が遅れて動く

Page 50: Interactive UI with UniRx

Settingsの動きの関係性

• 指が動く   触れた要素が動く

• 要素が動く   直後にある要素が遅れて動く

• 要素が動く ➔ 直前にある要素が遅れて動く

ループしてしまう

Page 51: Interactive UI with UniRx

Settingsの動きの関係性• 指が動く   触れた要素が動く        前後の要素に動きが伝わる

• 上から動きが伝わってくる ➔ 下に伝える

• 下から動きが伝わってくる ➔ 上に伝える

伝播してきた動きは逆向きへ伝える

Page 52: Interactive UI with UniRx

ToPrev

つられて動く

動く要素

ToPrev

つられて動く

Page 53: Interactive UI with UniRx

public class ListItem : MonoBehaviour { //上に動きを伝えるパイプ public IObservable<Vector2> ToPrev { get { return toPrev.AsObservable(); } } //下に動きを伝えるパイプ public IObservable<Vector2> ToNext { get { return toNext.AsObservable(); } }

• まず上下の要素を繋ぐパイプを用意する

リスト要素のクラスを作成する

Page 54: Interactive UI with UniRx

上から動きが伝わってくる ⇣

下に伝える

var fromPrev = myTransform.parent.GetChild(siblingIndex - 1) .GetComponent<ListItem>() .ToNext .Delay(TimeSpan.FromMilliseconds(10));

fromPrev.Subscribe(toNext).AddTo(this);

Page 55: Interactive UI with UniRx

下から動きが伝わってくる ⇣

上に伝える

var fromNext = myTransform.parent.GetChild(siblingIndex + 1) .GetComponent<ListItem>() .ToPrev .Delay(TimeSpan.FromMilliseconds(10));

fromNext.Subscribe(toPrev).AddTo(this);

Page 56: Interactive UI with UniRx

指が動く ⇣

上下に伝わる

//ドラッグによる移動IObservable<Vector2> fromDrag = this.OnDragAsObservable()

.Select(e => myTransform.parent.InverseTransformVector(e.delta))

.Select(delta => new Vector2(delta.x, 0));

fromDrag.Subscribe(toPrev).AddTo(this);fromDrag.Subscribe(toNext).AddTo(this);

Page 57: Interactive UI with UniRx

動きをまとめ、関係性として定義

Observable.Merge(fromPrev, fromNext, fromDrag) .Subscribe(delta => myTransform.anchoredPosition += delta) .AddTo(this);

Page 58: Interactive UI with UniRx

Demo

Page 59: Interactive UI with UniRx

注意とまとめ

• RxはUIでも便利。使えそうな所を見つけたらガンガンつかっていこう。

• 無理して使わない。

• 複雑な動きも分解してみると大したことないかも。

Page 60: Interactive UI with UniRx

Thanks!