6
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

UnityのWebGL出力に簡単に無料でKVSを使うサンプル1(Chat編)

Last updated at Posted at 2017-05-22

ChatChatChat ~シンプルなチャットを作る~

UnityのWebGL出力に簡単に無料でグローバルランキングを実装できる仕組みを考えてみた の続きになります。

まず、 https://github.com/divide-by-zero/GSSA/ をチェックアウトし、
projects/Assets/Samples/Empty/Sample1_ChatChatChat/ChatChatChat.unity を開きます。

※こちら、ある程度作ってあるけれど重要なところが空のプロジェクトです。 完成品はCompleteフォルダ配下にあるので、チマチマと講座なんてみるより完成品見た方が早い! というかたはそちらを見てくれればいいと思います。

0.下準備

  • 元記事にもあったように、GoogleSpreadSheet側の準備と、WebアプリケーションとしてのURL取得、SpreadSheetSettingsのinspectorへのURLのセットが済んでいる事を前提に進めていきます。

初期のソースコードは

using System.Collections;
using System.Collections.Generic;
using System.Linq;
using GSSA;
using UnityEngine;
using UnityEngine.UI;

public class ChatChatChat : MonoBehaviour
{
    [SerializeField] private Text logText;
    [SerializeField] private InputField nameInputField;
    [SerializeField] private InputField messageInputField;

    void Start ()
	{
        messageInputField.onEndEdit.AddListener(SendChatMessage);
	    StartCoroutine(GetChatLogIterator());
	}

    private void SendChatMessage(string s)
    {

    }

    private IEnumerator GetChatLogIterator()
    {
        while (true)
        {

            yield return new WaitForSeconds(5.0f);
        }
    }
}

以上な感じで、実行しても何も起きない状態です。
image.png

徐々に処理を追加していきます。

1.メッセージでエンターを押したらGSSA経由で保存する

NameのInputFieldとMessageのInputFieldは用意してありonEndEditAddListenerSendChatMessageを呼ぶところまではできています。 が、中身が空なので、何もできていない状態です。
ここで、onEndEdit=Enterキー押下 のたびに名前とメッセージをSpreadSheetに登録するよう処理を書きます。

    private void SendChatMessage(string s)
    {
        var so = new SpreadSheetObject("Chat");//Chatシート用のSpreadSheetObject作成
        so["name"] = nameInputField.text;//"name"列に名前用InputFieldのtextをセット
        so["message"] = s;//"message"列にはメッセージ用InputFieldのtext = onEndEditで渡される文字列(s)をセット
        so.SaveAsync();//SpreadSheetへ保存
        messageInputField.text = "";//サーバーに送信したので、手元の文字はクリア
    }

この時点で名前を入れて、メッセージを入力してエンターを押すたびに、サーバに1行データが保存されるようになるはずです。
<EX>
image.png
image.png

2.一定時間間隔で、ログ取得する

すでにサーバーにはデータがたまっているので、たまったデータを取得して、画面に表示すればチャットの大枠の出来上がりです。

上の何もない空間がTextField(logText)なので、ここにサーバーから取得したログを表示いれてあげます。
image.png

    private IEnumerator GetChatLogIterator()
    {
        while (true)
        {
            var query = new SpreadSheetQuery("Chat");//Chatシート用のQueryオブジェクト作成
            query.OrderByDescending("createTime").Limit(20);//生成日時の大きい順にソートして、最新20件を取得
            yield return query.FindAsync();//取得処理(yield returnで、サーバーから返却されるまで待機する)

            logText.text = "";
            foreach (var so in query.Result.Reverse())//取得できた結果をReverseで逆転(↑で生成日時の新しい順でソートしてしまっているので)
            {
                logText.text += so["name"] + ">" + so["message"] + "\n";
            }
            yield return new WaitForSeconds(5.0f);//5秒待ってあげてから、またサーバーからデータ取得
        }
    }

image.png

キモは、queryにOrderByDescendingでcreateTimeを指定して、生成日時の大きい=新しいデータ順にしているところ。
+Limitに20を指定して、最新データ20だけに抑えているところです。
これをしないと、表示しきれないほどのデータを毎回サーバーから取得しようとするので、通信量がなかなかな事になりかねません。

しかし、これでもまだ無駄が多いです。
image.png
SpreadSheetSettingのIs Debug Log Outputにチェックを入れて、通信ログを取得するとわかるのですが
image.png
常に最新データ上20件を取得するので、まったくデータが更新されていなくても同じデータが5秒に1回来てしまいます。
これは・・・あかんですよ・・・。

3.既に取得しているログはいらない

そんなわけで、↑これに尽きるんですが、どうやってすでに取得したものかどうかを判断するか。
今回の例だと、自動で生成されるcreateTimeを保持しておいて、Queryオブジェクトに条件を追加すればよさそうです。

最後に取得した時間(long)を取っておくための変数
private long lastGetTime;をフィールド変数として用意し、
Queryオブジェクトに、保持しているlastGetTimeよりも新しいデータだけほしいというWhereを追加してあげます。
query.OrderByDescending("createTime").Limit(20);query.OrderByDescending("createTime").Where("createTime",">",lastGetTime).Limit(20);

加えて、この変更によって、Queryで返却される件数が0の場合があります(データ更新が無い場合、lastGetTimeよりも新しいデータが無いため)
そのためのif追加や、Logを継ぎ足していくだけではなく、表示しきれない範囲分は消していかなくてはいけないため、いきなりlogTextにログを追加するのではなく、Listの形で表示分の文字列リストを作成してからlogTextにセットするようにします。

    private List<string> chatLogList = new List<string>();
    private long lastGetTime;

    private IEnumerator GetChatLogIterator()
    {
        while (true)
        {
            var query = new SpreadSheetQuery("Chat");
            query.OrderByDescending("createTime").Where("createTime",">",lastGetTime).Limit(20);//ローカルで保持していない最新データ上限20件に絞る
            yield return query.FindAsync();

            if (query.Count > 0)//有効なデータが無い場合は何もしない
            {
                foreach (var so in query.Result.Reverse())
                {
                    chatLogList.Add(so["name"] + ">" + so["message"]);//いきなりlogTextに入れるのではなく、一旦Listに追加
                    if (chatLogList.Count > 17) chatLogList.RemoveAt(0);//画面に収まりきらない部分(17以上)からは古いデータを削除
                }
                logText.text = string.Join("\n", chatLogList.ToArray());//Listを改行でjoinしてlogTextにセット
                lastGetTime = (long)query.Result.First()["createTime"];//createTimeの新しい順に並んでいるので、先頭(First)のcreateTimeを保持しておく。
            }

            yield return new WaitForSeconds(5.0f);
        }
    }

※要注意!:createTimeは実際にはlongでデータが入っていますが、SpreadSheetObject上ではobject型なので、よしなにキャストしてあげる必要があります

この修正により、ローカルにあるデータは来なくなるので、通信量の削減につながります。
image.png

最後に

たかが、チャット。 しかも5秒に1回しか更新しない、プル型のチャットですが、いろいろな基本が詰まっており、これが出来れば大体のことができるんじゃないかと思ってます。
次回は、実際にゲームにランキング機能を付けて行きたいと思います。

6
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?