Se ha denunciado esta presentación.
Utilizamos tu perfil de LinkedIn y tus datos de actividad para personalizar los anuncios y mostrarte publicidad más relevante. Puedes cambiar tus preferencias de publicidad en cualquier momento.

Interactive UI with UniRx

59.585 visualizaciones

Publicado el

https://unirx.doorkeeper.jp/events/25218
UniRx勉強会発表スライド

触って楽しい、見て面白いUIを、UniRxを使って作る方法を紹介。
先日リリースした弊社タイトルの事例を交えながらお話します。

Publicado en: Tecnología
  • Sé el primero en comentar

Interactive UI with UniRx

  1. 1. Interactive UI With UniRx
  2. 2. • 株式会社トライフォート • Unity/JSエンジニア • 24歳 岩下 侑冬 Yuto Iwashita @y120sb
  3. 3. ?UI 今日話すこと
  4. 4. MVなんちゃらの話ではない • パターンの話は先人の記事を読もう • MVVMとかMVPとか
  5. 5. 最近のUIに求められるもの • リッチなアニメーション • 手触り感 • パフォーマンス
  6. 6. UIの実装はラクじゃない • ビジネスロジックなんて大した問題ではない • (個人的に)アプリの実装コストの7割はUI • ドラッグ、アニメーション、etc…
  7. 7. ラクしたい
  8. 8. UniRxでできます!
  9. 9. UniRxが解決する2つの問題 ロジック的ややこしさ と 手触り感の表現
  10. 10. ロジック的ややこしさ
  11. 11. ロジック的ややこしさ • 仕様が複雑で実装も複雑になるやつ • そういうUIをユーザーに分からせるには   親切さも表現しなくてはならない         ➔ 色んな親切機能を追加する羽目に
  12. 12.
  13. 13. トゥモローアイランド http://tomorrowisland.trifort.jp/ • 弊社ソーシャルゲームタイトル第一弾 • 箱庭 × MO • 開発期間1年くらい、           Unityエンジニアは最大で5人
  14. 14. 自分だけの箱庭を 作ってみたり 仲間と一緒に 冒険したり
  15. 15. サービス終了するけど… • アプリを通してUniRxガッツリ! • クローズしてもUniRxは便利
  16. 16. アイテム合成UI
  17. 17. • D&D • ドラッグ通過判定 • 所要時間の表示 • アニメーション 複雑なUI
  18. 18. • D&D • ドラッグ通過判定 • 所要時間の表示 • アニメーション 複雑なUI 全部UniRxの守備範囲
  19. 19. すべてをObservableへ • IObservable<T>だらけにするとロジックから UI、アニメーションへの繋ぎが簡単になる • 書く人によって違う非同期処理の書き方も  統一できる(C# event, callback, coroutine)
  20. 20. 所要時間の表示 //ドラッグ開始 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)

  21. 21. DragStart SelectMany Select TakeUntil Repeat
  22. 22. アニメーション /// <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(); }

  23. 23. UniRxが解決する2つの問題 ロジック的ややこしさ と 手触り感の表現
  24. 24. 手触り感の表現
  25. 25. 手触り感 • 最近の流行り(だと思う) • 触ったら動く • 動いたらそれが連鎖していく
  26. 26.
  27. 27. Paper https://www.facebook.com/paper • Facebook製 • ぬるぬる動く、触れる • 見やすくはない
  28. 28. • 指の動きと連動して徐々に変化するUI • どうつくる?
  29. 29. 動きの関係性を定義する
  30. 30. 1) アクションから発生する動きを見つける 2) 動きを細かく分解する 3) 分解した動きをストリームとして記述する 4) 動きを全て合成し、定義する 動きの関係性を定義する
  31. 31. 1) アクションから発生する動きを見つける 指が動く   要素が動く
  32. 32. 2) 動きを細かく分解する • 指がY方向に動く   拡縮する • 指がX方向に動く   X方向に動く
  33. 33. 2) 動きを細かく分解する • 指がY方向に動く   拡縮する • 指がX方向に動く   X方向に動く • Y方向に拡縮する   Y方向の位置がずれる     ずれを戻す • X方向に拡縮する   X方向の位置がずれる   ずれを戻す
  34. 34. 3) 分解した動きを記述する • UniRxのストリームとして動きを記述する
  35. 35. 指がX方向に動く ⇣ X方向に動く //IObservable<Vector2> var dragMoveStream = onDrag .Select(e => container.InverseTransformVector(e.delta).x) .Select(deltaX => new Vector2(deltaX, 0f));

  36. 36. 指が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);

  37. 37. Y方向に拡縮する、ずれる ⇣ Y方向のずれを戻す //IObservable<Vector2>
 var cancelSlipYStream = scaleStream .Select(scale => defaultHeight * scale.x) .Select(y => new Vector2(0f, y - parent.anchoredPosition.y));

  38. 38. 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));

  39. 39. //移動 Observable.Merge(dragMoveStream, cancelSlipYStream, cancelSlipXStream) .Subscribe(move => parent.anchoredPosition += move)
 .AddTo(this); //拡縮
 scaleStream .Subscribe(scale => parent.localScale = scale) .AddTo(this);
 4) 動きを全て合成し、定義する
  40. 40. Demo
  41. 41. 関係性の定義によるUIの実装 • 原因(指の動き)と起こる結果(拡縮、移動)を  細かく分解し、そのまま記述する • 最後にUniRxで合成して適用する
  42. 42. Settings編
  43. 43. Settingsの動きの関係性 • 指が動く   触れた要素が動く • 要素が動く   真上にある要素が遅れて動く • 要素が動く ➔ 真下にある要素が遅れて動く
  44. 44. Settingsの動きの関係性 • 指が動く   触れた要素が動く • 要素が動く   直後にある要素が遅れて動く • 要素が動く ➔ 直前にある要素が遅れて動く ループしてしまう
  45. 45. Settingsの動きの関係性 • 指が動く   触れた要素が動く         前後の要素に動きが伝わる • 上から動きが伝わってくる ➔ 下に伝える • 下から動きが伝わってくる ➔ 上に伝える 伝播してきた動きは逆向きへ伝える
  46. 46. ToPrev つられて動く 動く要素 ToPrev つられて動く
  47. 47. public class ListItem : MonoBehaviour {
 
 //上に動きを伝えるパイプ
 public IObservable<Vector2> ToPrev {
 get { return toPrev.AsObservable(); }
 }
 
 //下に動きを伝えるパイプ
 public IObservable<Vector2> ToNext {
 get { return toNext.AsObservable(); }
 } • まず上下の要素を繋ぐパイプを用意する リスト要素のクラスを作成する
  48. 48. 上から動きが伝わってくる ⇣ 下に伝える var fromPrev = myTransform.parent.GetChild(siblingIndex - 1) .GetComponent<ListItem>() .ToNext .Delay(TimeSpan.FromMilliseconds(10)); fromPrev.Subscribe(toNext).AddTo(this);
  49. 49. 下から動きが伝わってくる ⇣ 上に伝える var fromNext = myTransform.parent.GetChild(siblingIndex + 1) .GetComponent<ListItem>() .ToPrev .Delay(TimeSpan.FromMilliseconds(10)); fromNext.Subscribe(toPrev).AddTo(this);
  50. 50. 指が動く ⇣ 上下に伝わる //ドラッグによる移動
 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);
  51. 51. 動きをまとめ、関係性として定義 Observable.Merge(fromPrev, fromNext, fromDrag) .Subscribe(delta => myTransform.anchoredPosition += delta) .AddTo(this);
  52. 52. Demo
  53. 53. 注意とまとめ • RxはUIでも便利。使えそうな所を見つけたらガ ンガンつかっていこう。 • 無理して使わない。 • 複雑な動きも分解してみると大したことないかも。
  54. 54. Thanks!

×