ぐーるらいふ

底辺。

【Unity1week】一週間ゲームジャム「あつい」に参加【Unity】

お疲れ様です。お久しぶりです、ぐーるです。
仕事の方ではゲームとは関係ないまま、すっかり中堅、それ以上となり
打合せ、社外MTGなど座席に余り居ないような毎日を送っています。
Unityの新規開発プロジェクトとかやりたいです。

前回からまた長い間が空いてやってきました、unity1week。
自分からするとつい先日宴ゲームジャムがあったので、
そこまで間が無かったようなそんな印象なんですが、やっぱりこっちも参加したいと。

ghoul-life.hatenablog.com


そうでもしないとunityを触る機会とか無いんだものー

お題について

これまた難しい。どうしようかウンウン唸ってみるが全然アイデアは降りてこない。
・おでん?
・太陽?
・お湯?
こういう時にマインドマップとかやるといいのだろうけど、
流れに身を任せて日々を過ごす。

とりあえず漠然と考えていたのは
「自分でキャラなにか描きたいなぁ」
だった。

相変わらず下手だが、下手の横好き、別に見られても減るもんじゃないし!
(でもリアル知り合いには言わないし、見せないw)
という気持ちで、
自分でも描けそうで、
作りやすそうで、
カジュアルで、
工数は2日ぐらいないい感じの無いかなーと案を巡らせていると
よくある正弦波とかを利用した波乗りとかどうだろうとふと思いついた。

なみのり

昔メッシュを利用したマップを作ってその上を滑らせたりしてたんで
これならすぐ出来るなーと思った。ひっくり返ったらゲームオーバーにしようと。
とりあえず枠組みはすぐ出来るし、キャラも波乗りしている女の子でいいじゃんと。

サーフィンやボディボードとかも思ったけどわかりやすく浮き輪にしました。
浮き輪なら遅くても平気だし!
と描き始める。

f:id:ghoul_life:20180917142613j:plain

ざっとこんな感じで座ってて〜髪は長めで〜
とか試行錯誤をやってるうちにどんどん深みにハマっていく。(ここがめっちゃ楽しいんだけど)
「他のバリエーションとか浮き輪も乗るタイプのものとかアニメーションとか付けたいなー」
と思っているうちに期限は刻一刻と迫っていた。

ゲームについて

もうこっちは特に言うこと無いですね。
uvスクロールで水面を動かしつつとplaneを利用してスクリプトからmesh変形させてるだけ。

    private void Wave()
    {
        _mesh = this.GetComponent<MeshFilter>().mesh; // planeのメッシュ
        _meshCollider = this.GetComponent<MeshCollider>();
        _verticies = _mesh.vertices;

        int counter = 0;
        int yLevel = 0;

        for (var i = 0; i < iLen; i++) 
        {
            for (var j = 0; j < jLen; j++)
            {
                Calc(counter, yLevel);
                counter++;
            }
            yLevel++;
        }

        _mesh.vertices = _verticies;
        _mesh.RecalculateBounds();
        _mesh.RecalculateNormals();
        

        _meshCollider.sharedMesh = _mesh;

    }

    private void Calc(int i , int j)
    {
            var x = (_verticies[i].x + this.transform.position.x) / _detailScale;
            var y = (_verticies[i].y + this.transform.position.y) / _detailScale;
            _verticies[i].z = Mathf.PerlinNoise(x, y) * _heightScale;
            _verticies[i].z -= j;
    }

これプレイが進むと自動でパラメータが切り替わっていくんだけど
この切り替わりがちょっと唐突なのが微妙だった。
今後の課題だなと。lerpで向かっていくといいのかな。

(実はこんなトリッキーな波にすることとかも出来るのです)

f:id:ghoul_life:20180917164252p:plain

絵について

結局描けたのは
・通常
・ジャンプ時
・落ちた時
の3つだけ。
あとUIパーツとかそういったものも手描きで作ってたりしてます。

次回について

次ももちろん参加します。

それとは別で次のゲームの構想は決まっていて
作り出していきたいのですが仕事が忙しい…。
平日は10-23が標準で日付跨ぐ、朝までコースもしばしば。
これで家族がいる人とかどうやって暮らしてんだろう?
とか全然わかりませんね。

あ、宴ゲームジャム、賞ほんとにありがとうございました。
ADVの話のアイデアも練っておこう!
「Unity & 宴ノベルゲーム開発入門」出版記念オンラインゲームジャム | 無料ゲーム投稿サイト unityroom - Unityのゲームをアップロードして公開しよう

【Unity】「Unity & 宴ノベルゲーム開発入門」出版記念オンラインゲームジャム に参加しました

https://unityroom.com/games/shirakisou

先日、ゲームジャムへの提出が完了した。
今回は初のADVということで、思ったこととか大変だったことでも書いてみようかなと。
あんまり技術的な事はありません。基本は全て宴任せ。

ADVについての想い

昔(SFC時代)はかまいたちの夜とかそういったサウンドノベルっていうジャンルが流行った事がありました。

時代は流れ、逆転裁判ダンガンロンパといったカジュアルなものがヒットしたり、
シュタインズゲートが大ヒットしてアニメ化映画化までして、長期ヒットを続けていたり
四十八(仮)がある意味有名になったりと、緩やかな進化を続けているものの、
大きくは変わらず、基本は文章力で勝負しているジャンルになるのかなと思います。

www.famitsu.com
こんなインタビューとかもありました。

それに挑戦しようと言うのは無謀な感じがしましたが、
やるだけやってみようと言うわけでまずは適当なメモ書きから始めました。

メモ書き

・殺人事件を題材にする

容疑者は三人
- 友人のおじさん
- 恋人の女性
- 仕えていたメイド

死因は背中から刺殺
ナイフは死体に刺さったまま
部屋は施錠されていた

キッチンにいた
居間にいた
部屋に居た
でアリバイはなし

誰に話しかけよう
・三人
・もう充分(犯人当てに)

3つの質問
・殺された人
・その他1
・その他2

おじさん
・十年来の友人だった
お金を借りていた事もあったが、今はもう無い

・最近付き合いだしたと聞いた
今回が初対面

・三年ほど前からいるメイドだ
ほとんど会話したことがない

当初はこんな感じでした。
思いついた設定や状況などをとりあえずメモる。
そして繋いでいこうとしてました。

エンディングを先に作る

誰かが言っていた。ゲームはエンディングから作れと。
そうしないといつまで経っても終わらないと。

なので、犯人をざっくり決めて、エンディングの結びの言葉まで
先に決めてました。
これが無かったら絶対間に合ってなかった…。
ありがとうセンパイ。

どんどん間延びする文章

OP -> 到着 -> 環境 -> 人物紹介 -> 事件 -> 調査 -> 解決 -> ED

当初はこんな流れにしようと決めていました。
「二日目とか無いと短すぎるかなー」
とか余裕ぶっこいてたらとんでもなかった。

とりあえず書く
->
読み直す
->
違和感を直す
->

のループで精度を上げていて
「ここ唐突だな」
と思ったところを直し続けていると、
あっという間に10行、20行と増える。

気がついたら三万文字を超えていた。

「あ、こりゃダメだ」
と思い切って組み直しをすることに。

思い切って

要点だけに絞っていく作業。
だが、最小限の人物紹介は必要だなとその辺りも組み直し。
最初にミステリーですよ!ということを伝えるために殺人現場からのスタートにした。

事件発覚 -> 人物紹介 -> 事件 -> 解決 -> ED

とシンプルな座組に。

もう少しゲームらしくするには、マップを移動することが出来たり、
質問する内容を自分で選択出来たりといった部分を作れば良かったのですが、
どうしてもプレイ時間が間延びしてしまってそれは断念しました。

個別に会話をして人となりを知るシーンなどもあったのですが、泣く泣く外しました。

宴について

技術的な事は無いとか言っちゃったけど、ちょっとだけ。
とにかく言えることは

「困ったらSample.xlsを見ろ」

これに尽きる。これに全てが乗っていた。
ですが、とりあえず自分が使えるようになるために要点に絞って調べたことを。

xlsのシート「Start」から始まる
分岐:Selection
ラベルに飛ぶ:Jump
ローカルラベル:**~~~
シート名:*~~~
キャラの表示:Character , CharacterOff
頻出しそうなコマンドをメモ
- FadeIn , FadeOut : フェードイン、フェードアウト。 Arg6で秒数を指定出来る
- Wait : 待機。Arg6で秒数指定
- bg : Arg1で指定した背景に差し替え
- Sprite : 背景の上に出す画像を指定
- SpriteOff : 差し込み画像を削除
- Bgm , Se のサウンド

と使いたい機能だけに絞って少しずつ把握していった。
特にLayerは非常に強力なので、これは使いこなしたいし、
自分でも作れるようになりたい。

感想

製作期間は1week challengeと同じ一週間です。
最初は本当にボリューミーでした。

文章を書く、ということが国語の授業とか以外では本当に初めてだったのですが
思ったよりも書くの楽しかったなーと。また書きたい。
ですが、文章を書くだけなら簡単なんですが、まとめるのは本当に難しかったです。

そして最後に、EDはちょっと切なげなEDになってます。
良ければちょっとプレイしてもらえると、そしてハートを押して下さいー!

推理ADV 白木荘の殺人 | 無料ゲーム投稿サイト unityroom - Unityのゲームをアップロードして公開しよう

【Unity1week】一週間ゲームジャム「ぎりぎり」に参加 前編?【Unity】

前置き

Unity unity1week「ぎりぎり」に参加しました。
今回作ったのはこちら。脱出ゲーム。

f:id:ghoul_life:20180612201305p:plain
https://unityroom.com/games/giriescape


なんでこんなめんどくさいのを選んだんだ…と。

パッと見でわかる。

  • アイテムマスタ
  • メッセージマスタ

が必要だということが。


最初にお題の「ぎりぎり」と聞いて、最初に思いついたのがコレで、
一度挑戦してみたかったなーと思ってたので、「まぁいいか、やってみよう」と挑戦することにしました。

設計について

基本的には

  • キャラが移動
  • オブジェクトをターゲット
  • アクションボタン押下でメッセージが出る
  • ギミックがあればギミックを処理
  • 結果メッセージYES
  • 結果メッセージNO

というような流れになるだろうと想定。

この時メッセージは所持しているアイテムによって違う、
またはアイテムを使用するとメッセージが変わる

といった具合だ。

すでにめんどくさい雰囲気が漂っている。

ざっとフローをまとめるとこんな感じだ。

f:id:ghoul_life:20180612173846p:plain
f:id:ghoul_life:20180612173856p:plain


力尽きたので、プログラム編は後日

【Unity】 【有料Assets】 Simple Bones Animationを使って適当に取ってきたキャラにオリジナルアニメーションを作る

お疲れ様です。ぐーるです。
お久しぶりになってしまった。
サラリーマンや学生も期の変わりは忙しいんですよね。

ProBuilderはどうした

やってるんですが、まだ記事的にまとまってなくて…
もうすぐUnityの勉強会もあるんで、そちらの情報と合わせて記事にしたいな

ProBuilderで学ぶレベルデザイン

Simple Bones Animation

Unity Asset Storeでいい感じのキャラを見つけた!
早速使ってゲーム作るぞ!
走りはこれで、攻撃はこれで…
あれ、ゲームクリア時になんかポーズしたいのにいい感じなの無いじゃん!

なんてことがあった時、3Dモデルが作れない自分を呪い諦めていたんですが、
どうやらこんな便利なツールがあると汗人柱さんが記事にしてたのを見つけました。

www.asset-sale.net

(ちょっと古い記事ですが。ほんと神)

おお、これイイじゃん、早速使ってみよう!と思いたち、買いました。
(有料です。4.95ドル…!)

assetstore.unity.com

事前準備

キャラクターはこいつを使います。

assetstore.unity.com


可愛い宇宙服シューターです。ライトなSFシューティングのプレイヤーにぴったり。
だが

  • Idle
  • Run
  • Run Back
  • Left
  • Right

しかアニメーションが無く、実際に撃つようなモーションはありませんでした。
(2018/04/13。updateで追加されたりするかもしれません。)
これに撃つアニメーションを作りたいと思います。

新規プロジェクトを立ち上げ、Simple Bones AnimationとSpace ManをImportしておきます。

1.space manのRigをHumanoidからLegacyに変える

これを行わないとSimple Bones Animationが上手く動かない

f:id:ghoul_life:20180413215704p:plain
f:id:ghoul_life:20180413215729p:plain

2.space_map_modelのPrefabを配置する

配置したらAnimatorを削除し、Animationに付随しているAnimation Clipを一旦全て外す。
これで新たなAnimation Clipを作ることができるようになります。
space manのRootを選択して、Add ScriptからSimple Bonesスクリプトを追加する
Root Nodeが空の状態になるので、Space Manの Pelvisをセットする。

f:id:ghoul_life:20180413215745p:plain

ここまでやるともうボーンがSceneビューに見えるはず。

3.Shot.animを作成

Animationが空っぽの状態でAnimationビューを開くと 新たなAnimation ClipがCreate出来る。
Createを押下して、Shot.animとしてanimファイルを作成

すると、Simple BoneのAnimationにShotが選択されている状態になる。
これで準備完了です。

f:id:ghoul_life:20180413215803p:plain

4.アニメーションを作る

SimpleBonesのAnimationから「Create Animation Keys」にあるPosition , Rotation , Scaleのいずれかを押下すると
ボーン周りのTransformがAnimationビューに紐付けることが出来る。
が、今回は使いません!

初期ポーズはIdleからコピーして持ってきます。

f:id:ghoul_life:20180413215818p:plain

そして動かしたいパーツを選択しつつ…動かして…Add Key!!
間違えたら…一度消して、直して再度Add Key!!

f:id:ghoul_life:20180414014342p:plain

5.Shotアニメーションが作れました

こんな感じでShotアニメーションが出来ました。

f:id:ghoul_life:20180414014419g:plain

簡単でしょう?と言いたいですが、自然なボーンアニメーション作るのは難しいですね…。
でもツールはほんと凄い。有料は優良なんですね!

【Unity】Rigidbodyの弾道予測線をシュミレートする

ghoul-life.hatenablog.com

前回の続きで、今回は撃つ側。

RigidbodyにAddForceを加えた場合の移動位置をシュミレートして表示する

なんていうのはみんなやっているようで、ググると大量に出てきます。

f:id:ghoul_life:20180322150647p:plain

そのまま使わせて頂くのもOKですが、自分なりにまとめたソースもメモ。

Rigidbodyの移動を予測計算

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

/**
 * Rigidbodyにvecを加えた時の弾道をシュミレート。
 */
public class BallSimulator : MonoBehaviour {

    [SerializeField]
    private GameObject _ballSimPrefab; // 何でもOK。予測位置を表示するオブジェクト

    private const int SIMULATE_COUNT = 20; // いくつ先までシュミレートするか

    private Vector3 _startPosition; // 発射開始位置
    private List<GameObject> _simuratePointList; // シュミレートするゲームオブジェクトリスト

    void Start () {
        Init();
    }
	
    void Update () {
        // デバッグ用に線を出してみる。必要無いなら無くても問題なし。
        if (_simuratePointList != null && _simuratePointList.Count > 0)
        {
            for (int i = 0; i < SIMULATE_COUNT; i++)
            {
                if (i == 0)
                {
                    Debug.DrawLine(_startPosition, _simuratePointList[i].transform.position);
                }else
                if (i < SIMULATE_COUNT)
                {
                    Debug.DrawLine(_simuratePointList[i - 1].transform.position, _simuratePointList[i].transform.position);
                }
            }
        }

    }

    // 初期化
    private void Init()
    {
        if (_simuratePointList != null && _simuratePointList.Count > 0)
        {
            foreach (var go in _simuratePointList)
            {
                Destroy(go.gameObject);
            }
        }

        // 位置を表示するオブジェクトを予め作っておく
        if (_ballSimPrefab != null)
        {
            _simuratePointList = new List<GameObject>();
            for (int i = 0; i < SIMULATE_COUNT; i++)
            {
                var go = Instantiate(_ballSimPrefab);
                go.transform.SetParent(this.transform);
                go.transform.position = Vector3.zero;
                _simuratePointList.Add(go);
            }
        }
    }

    /**
     * 弾道を予測計算する。オブジェクトを再生成せず、位置だけ動かす。
     * targetにはRigidbodyが必須です
     **/
    public void Simulate(GameObject target , Vector3 _vec)
    {
        if (_simuratePointList != null && _simuratePointList.Count > 0)
        {
            // 発射位置を保存する
            _startPosition = target.transform.position;
            var r = target.GetComponent<Rigidbody>();
            if (r != null)
            {
                // ベクトルはmassで割る
                Vector3 force = (_vec / r.mass);

                //弾道予測の位置に点を移動
                for (int i = 0; i < SIMULATE_COUNT; i++)
                {
                    var t = (i * 0.5f); // 0.5秒ごとの位置を予測。
                    var x = t * force.x;
                    var y = (force.y * t) - 0.5f * (-Physics.gravity.y) * Mathf.Pow(t, 2.0f);
                    var z = t * force.z;
                    
                    _simuratePointList[i].transform.position = _startPosition + new Vector3(x, y, z);
                }
            }
        }
    }
}

発射する側は

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;

/**
 * 弾を発射する
 */
public class BallShooter : MonoBehaviour {

    [SerializeField]
    private BallSimulator _ballSimurator; // 弾道予測線

    [SerializeField]
    private Slider _powerSlider; // 力を変えるスライダー

    [SerializeField]
    private GameObject _shooterBase; // 土台

    private Vector3 _firstPosition;
    private Rigidbody _rigidbody;
    private Vector3 _touchDownPos;

    private bool _isShot;
	// Use this for initialization
    void Start () {
        Init();
    }

    
    // Update is called once per frame
    void Update () {

        if (!EventSystem.current.IsPointerOverGameObject())
        {
            if (Input.GetMouseButtonDown(0))
            {
                _touchDownPos = Input.mousePosition;
            }
            else if (Input.GetMouseButton(0))
            {
                var tempPos = Input.mousePosition;
                Vector3 value = Vector3.zero;
                value.x = (_touchDownPos.x - tempPos.x);
                value.y = (_touchDownPos.y - tempPos.y);
                value.z = 0;
                _touchDownPos = tempPos;

                var qot1 = Quaternion.AngleAxis(value.x, new Vector3(0, 1, 0));
                var qot2 = Quaternion.AngleAxis(value.y, new Vector3(1, 0, 0));
                _shooterBase.transform.rotation *= qot1;
                this.transform.rotation *= qot2;
            }
        }


        Debug.DrawLine(this.transform.position, this.transform.position + this.transform.forward , Color.blue);
        
        // 発射中はシュミレートを止める
        if (!_isShot)
        {
            var vec = this.transform.forward * _powerSlider.value;
            _ballSimurator.Simulate(this.gameObject, vec);
        }
    }

    public void Init()
    {
        _firstPosition = this.transform.position;
        _rigidbody = this.GetComponent<Rigidbody>();
        _rigidbody.isKinematic = true;
        _powerSlider.value = 10.0f;
        _isShot = false;
        
    }

    //--------------------------------------------------------------------------
    // 発射(Buttonから呼ぶとか)
    public void Shoot()
    {
        _isShot = true;
        var vec = this.transform.forward * _powerSlider.value;
        _ballSimurator.Simulate(this.gameObject , vec);
        _rigidbody.isKinematic = false;
        _rigidbody.AddForce(vec, ForceMode.Impulse);
    }
}

こんな感じでOK。

動かす

f:id:ghoul_life:20180322151038g:plain

  • 発射方向を変えるとそれに合わせて予測線も変わるか
  • 発射する力を変えるとそれに合わせて予測線も変わるか
  • massを変えると予測線も変わるか
  • 予測線に沿って飛ぶか

f:id:ghoul_life:20180322151137g:plain

この辺りに注目。

この次は

砲台を突然作り出したのはProbuIlderの勉強のため

assetstore.unity.com

今回でなんちゃって簡易砲台(作ろうとしているゲームでは砲台ではないんだけど)が出来上がったので、
次回からProBuilderを使用したステージ制作に入っていければいいな

【Unity】スライド操作で回転する砲台を作りたかったお話

何も考えずに砲台を作る

まずは図を観てほしい。

f:id:ghoul_life:20180320200335p:plain

左右ドラッグで横回転、上下ドラッグで縦回転というシンプルなものだ。
で、発射ボタンか何かを押下するとforward方向へ発射されるという仕様と思って欲しい。
シンプルでいいんで、オブジェクトは弾だけで、自分で飛んでいくって感じ。

で、直感で作ったソースはこれ。テキトー。

Vector3 _touchDownPos;
void Update(){
  if (Input.GetMouseButtonDown(0))
  {
      _touchDownPos = Input.mousePosition;
  }
  else if (Input.GetMouseButton(0))
  {
      var tempPos = Input.mousePosition;
      Vector3 value = Vector3.zero;
      value.x = (_touchDownPos.x - tempPos.x);
      value.y = (_touchDownPos.y - tempPos.y);
      value.z = 0;
      _touchDownPos = tempPos;

      var qot1 = Quaternion.AngleAxis(value.x , new Vector3(0,1,0)); // 横回転
      var qot2 = Quaternion.AngleAxis(value.y , new Vector3(1,0,0)); // 縦回転

      this.transform.rotation *= qot1 * qot2;// Quartanion同士は掛け算で合体させる
  }

  // forwardチェック用のデバッグライン
  Debug.DrawLine(this.transform.position, this.transform.position + (this.transform.forward * 5));
}

動かしてみると

で、実際に動かすと、

  • 左右スライド -> いい感じに回る

f:id:ghoul_life:20180320200525g:plain

  • 上下スライド -> いい感じに上下に向いてくれる

f:id:ghoul_life:20180320200547g:plain

「完成だね!」
とは行かず、上下を角度を付けた状態で
左右に回すと斜めに回ってしまう。(当たり前だけど)

f:id:ghoul_life:20180320200613g:plain

やはり自然に回ってほしい

数学的には上の動きで全く問題ないのだけど、
こう回るのが直感的でゲーム的な理想だと思う。

f:id:ghoul_life:20180320200731g:plain

解決法(暫定)

とりあえず土台でやってしまった。

f:id:ghoul_life:20180320202037p:plain

  • 左右スライド -> 土台を回す
  • 上下スライド -> 弾の角度を調整
  var qot1 = Quaternion.AngleAxis(value.x , new Vector3(0,1,0));
  var qot2 = Quaternion.AngleAxis(value.y , new Vector3(1,0,0));
  _shooterBase.transform.rotation *= qot1; // 土台を回す
  this.transform.rotation *= qot2; // 砲塔を回す

というオブジェクトの分離で理想の動きになりました。

打ち終わったらまた弾に元に戻って欲しかったり、
発射位置がわからないと困ることも多いし…と土台があるとそれはそれで便利。

そもそも砲台だって土台と砲塔とって別れとるやんけ!
そう作ればええんや!

ってだけなんですが、計算で上手く回転軸を取ったり出来る…?

【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へ飛ぶような感じでも誤魔化せそう。

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