ぐーるらいふ

しがないリーマンの記録。遊びのunityのメモ帳にしたい。

【Unity】【uGUI】 滑らかにスクロールするチャットウィンドウのようなものを作りたかった話

お疲れ様です。ぐーるです。
いきなりですけど、表題のようなものが作りたくて四苦八苦してました。
結局自作に終わったんですが、ちょっと経緯をメモっておこうかと。

ScrollRectで余裕っしょ

UnityにはScrollRectがあって、それはnormalizedPositionっていう値を持っていて、
その値でスクロール位置を制御できます。

docs.unity3d.com

f:id:ghoul_life:20180316174936p:plain

こちらを利用しようとまず考えました。

追加する行について

今回作ろうとしていたのはチャットウィンドウなんで、文字の長さや改行によって
追加される行の高さはまちまちな訳です。
これはLayoutElementと自分計算で行けるなということで

  _messageText.text = strMessages[Random.Range(0, strMessages.Length)]; // 適当なメッセージ
  _messageText.rectTransform.sizeDelta = new Vector2(_messageText.rectTransform.sizeDelta.x, _messageText.preferredHeight);
  _backGroundImage.GetComponent<RectTransform>().sizeDelta = new Vector2(_backGroundImage.GetComponent<RectTransform>().sizeDelta.x, _messageText.preferredHeight + 40);

こんな感じで、テキストの高さから計算して設定すればとりあえずいいでしょう。

スクロールイメージ

最初に起こしたイメージは

f:id:ghoul_life:20180316175418p:plain

というイメージでAddする度に0.0fへ
毎回スクロールさせてあげればいいじゃん。シンプル。

実際これは問題無く実装出来ました。

void Update () {
        if (isScroll)
        {
            var val = _scrollRect.verticalNormalizedPosition;
            if(val > 0){
                val -= scrollSpeed * Time.deltaTime;
                _scrollRect.verticalNormalizedPosition = val;
            }
            else
            {
                _scrollRect.verticalNormalizedPosition = 0;
                isScroll = false;
            }
        }
}

void AddRow()
{
            var go = Instantiate(_rowPrefab);
            go.transform.SetParent(_tableContent.transform);
            isScroll = true;
}

工夫すると

チャットシステムとはいえ、単純に作るとInstantiateの嵐になってしまいます。
なので、出来るだけコンポーネントを使い回したいなと考えました。

f:id:ghoul_life:20180316174956p:plain

このようになると単純なスクロールでは上手くいかず、
少し計算が必要になりました。

float contentHeight = _tableContent.GetComponent<RectTransform>().sizeDelta.y; // 全体の長さ
float beforeTextHeight = row.GetComponent<RectTransform>().sizeDelta.y; // 前の行の高さ
float afterTextHeight = row.Create(); // Createの中では、ランダムにメッセージを生成している。行の高さを返却

float hDelta = contentHeight - beforeTextHeight + afterTextHeight; // 前回のを消し、新しい高さを計算
float normal = afterTextHeight / hDelta; // 最終的なスクロール位置

Debug.Log("contentHeight == " + contentHeight + " | before " + beforeTextHeight + " | after " + afterTextHeight + " | normal " + normal + " | delta " + (hDelta));
row.transform.SetAsLastSibling(); // 最下部にスライド

こんな感じで計算すると追加した行の手前に丁度フォーカスが動きます。
後はそのまま0.0fへ向かってスクロールすればOKです。

f:id:ghoul_life:20180316174856g:plain

スクロールはいい感じです。
が、全体としてはどうでしょうか。
ScrollRectの再計算なのかガタっと表示が動く事があるため、微妙に荒い感じに…。
(gifで見ると悪くなく見えますが、実際見るとちょっとガタつきがあることがあります)

結局自作?

使い回しをせず、素直にInstantiateすれば問題ないです。

自動スクロールさせない、なんていうのも良いし、
アニメーションさせず、0.0fへ飛ぶような感じでも誤魔化せそう。

ちょっとヘンな事をしようとすると上手く行かなかった…って話でした。
実装でカバー出来そうだけど、自作のほうが制御が楽?