MLAPI でテキストチャットっぽいのを作る (2)| UI表示【MLAPI】

テキストチャットっぽいのを作りながらMLAPIのお勉強しています…。

前回記事

xrdnk.hateblo.jp

Debug.Log でそれっぽいの表示できるところまでできてました。

UI準備

適当にUIを準備します。
テキストメッセージ表示用にText、InputField、Buttonを用意します。

サンプルスクリプト(WIP)

サンプルスクリプトのWIPはこんな感じになりました。
ばりばりUniRxを使ってます。解説は軽くコメント入れました。

Chat.cs

using UnityEngine;
using System.Collections.Generic;
using MLAPI;
using MLAPI.NetworkedVar;
using MLAPI.NetworkedVar.Collections;
using UnityEngine.UI;
using UniRx;

public class Chat : NetworkedBehaviour
{
    private NetworkedList<string> ChatMessages = new NetworkedList<string>(new NetworkedVarSettings()
    {
        ReadPermission = NetworkedVarPermission.Everyone,
        WritePermission = NetworkedVarPermission.Everyone
    }, new List<string>());

    [SerializeField]
    private InputField InputField_Chat;
    [SerializeField]
    private Button Button_Chat;
    [SerializeField]
    private Text Text_Chat;

    private void Start()
    {
        // 入力されるまでボタンを非活性化
        InputField_Chat
            .OnValueChangedAsObservable()
            .Subscribe(inputField => Button_Chat.interactable = !string.IsNullOrWhiteSpace(inputField));

        // クライアントかつ文字が入力済の条件下でボタンを押下してチャットする
        Button_Chat
            .OnClickAsObservable()
            .Where(_ => IsClient)
            .Where(_ => !string.IsNullOrWhiteSpace(InputField_Chat.text))
            .Subscribe(_ => AddChatMessage(InputField_Chat.text));

        // チャットメッセージの内容が変わった際に呼び出す
        ChatMessages.OnListChanged += UpdateTextChat;
    }

    /// <summary>
    /// チャットメッセージに追加
    /// </summary>
    private void AddChatMessage(string chatLine)
    {
        ChatMessages.Add(chatLine + System.Environment.NewLine);
        // 入力された文字を消す
        InputField_Chat.text = "";
    }

    /// <summary>
    /// テキストチャットの更新
    /// </summary>
    private void UpdateTextChat(NetworkedListEvent<string> changeEvent)
    {
        Debug.Log("Chat::OnListChangedDelegate: " + changeEvent.value);
        ShowTextChat();
    }

    /// <summary>
    /// テキストチャットの表示
    /// </summary>
    private void ShowTextChat()
    {
        // これまでのメッセージを消してから更新
        Text_Chat.text = "";
        for (int i = ChatMessages.Count - 1; i >= 0; i--)
        {
            Text_Chat.text += ChatMessages[i];
        }
    }
}

一つずつ簡単に解説します。

        // 入力されるまでボタンを非活性化
        InputField_Chat
            .OnValueChangedAsObservable()
            .Subscribe(inputField => Button_Chat.interactable = !string.IsNullOrWhiteSpace(inputField));

こちらのヒューマンインタフェースガイドラインに従って、入力されるまではチャットボタンを非活性化してます。

www.sociomedia.co.jp

        // クライアントかつ文字が入力済の条件下でボタンを押下してチャットする
        Button_Chat
            .OnClickAsObservable()
            .Where(_ => IsClient)
            .Where(_ => !string.IsNullOrWhiteSpace(InputField_Chat.text))
            .Subscribe(_ => AddChatMessage(InputField_Chat.text));

IsClient という条件は必要かどうか微妙ですが…。
文字が入力されていれば、AddChatMessageにInputFieldのテキストを渡します。

    /// <summary>
    /// チャットメッセージに追加
    /// </summary>
    private void AddChatMessage(string chatLine)
    {
        ChatMessages.Add(chatLine + System.Environment.NewLine);
        // 入力された文字を消す
        InputField_Chat.text = "";
    }

ChatMessagesはListなので、Addで要素を加えます。
NetworkedVar はイベント駆動型なので、値が更新された瞬間、
クライアントからサーバーへ、サーバーから各クライアントへ…という感じによしなに同期してくれます。
入力された文字に System.Environment.NewLine を加えることで改行にできます。
この時点でInputFieldの文字は不要なので、ここで消します。

        // チャットメッセージの内容が変わった際に呼び出す
        ChatMessages.OnListChanged += UpdateTextChat;

NetworkedList には OnListChanged という NetworkedList の内容が変化した時に呼び出されるイベント関数があります。
ここに UpdateTextChat を登録します。

mlapi.network

    /// <summary>
    /// テキストチャットの更新
    /// </summary>
    private void UpdateTextChat(NetworkedListEvent<string> changeEvent)
    {
        Debug.Log("Chat::OnListChangedDelegate: " + changeEvent.value);
        ShowTextChat();
    }

Debug.Log は一応 changeEvent.value の値チェックのために入れてます。
NetworkedList に Add した値が出てくるはずです。
その後にShowTextChatを呼びます。

    /// <summary>
    /// テキストチャットの表示
    /// </summary>
    private void ShowTextChat()
    {
        // これまでのメッセージを消してから更新
        Text_Chat.text = "";
        for (int i = ChatMessages.Count - 1; i >= 0; i--)
        {
            Text_Chat.text += ChatMessages[i];
        }
    }

これまでのメッセージを一度決してから再度Listの中身を取り出して更新しています。
もっといいやり方がありそうですが…。一旦、解説はここまでです。

動作確認

チャットっぽいのが出来ました。
まだまだ課題があるので、引き続き取り組みます。