仮想と物理とエトセトラ

xRや物理とかごった煮の備忘録的技術ブログ

MLAPIをHoloLensでつかう その4 (物体操作をHost, Clientの両方で行う)

今回は前回の続きです。

xr-physics-work-etc.hatenablog.com

前回では、物体操作はHostからしか行えませんでした。
このままでは都合が悪いことも多いので、誰でも操作できるように、Clientからも物体の操作を行えるようにします。

物体の操作を管理する

MLAPIには、物体の操作権限を委譲するメソッドNetworkObject.ChangeOwnershipを用いることで、各ユーザに操作権限を与えることができます。

docs-multiplayer.unity3d.com

しかし、この関数はServer(Host)からしか実行できないため、Client側から操作権限を直接取得することができません。
そのため、Client側から操作権限を取得するには、Host側にChangeOwnershipの実行を依頼する必要があります。
Clientから情報を共有する方法として、NetworkVariableがあります。

docs-multiplayer.unity3d.com

そのため、ChangeOwnershipの引数であるnewOwnerClientIdを共有し、値の変更をトリガにServer(Host)側に権限変更を実行させようと思います。
権限変更を依頼するスクリプトを作成します。
ついでに、権限変更の可否を管理する変数も用意しました。

  • NetworkSharingObject.cs
クリックで展開
using MLAPI;
using MLAPI.NetworkVariable;
public class NetworkSharingObject : NetworkBehaviour
{
    /// <summary>
    /// 更新対象のネットワークID
    /// 全ユーザ変更可能
    /// </summary>
    NetworkVariableULong newOwnerClientId
        = new NetworkVariableULong(new NetworkVariableSettings { WritePermission = NetworkVariablePermission.Everyone });

    /// <summary>
    /// 操作権限委譲可否
    /// 全ユーザ変更可能
    /// </summary>
    /// <returns></returns>
    NetworkVariableBool enableChangeOwner
        = new NetworkVariableBool(new NetworkVariableSettings { WritePermission = NetworkVariablePermission.Everyone }, true);


    void OnEnable()
    {
        // OnChangeOwnerイベント設定
        newOwnerClientId.OnValueChanged += OnChangeOwner;
    }

    void OnDisable()
    {
        // OnChangeOwnerイベント設定解除
        newOwnerClientId.OnValueChanged -= OnChangeOwner;
    }

    // Start is called before the first frame update
    void Start()
    {
        if (IsOwner)
        {
            // Spawn時のオーナ情報を取得
            newOwnerClientId.Value = OwnerClientId;
        }
    }

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

    }

    /// <summary>
    /// オーナーシップ変更処理
    /// </summary>
    /// <param name="oldValue"></param>
    /// <param name="newValue"></param>
    void OnChangeOwner(ulong oldValue, ulong newValue)
    {
        // サーバでしか実行できないため、サーバで実施
        if (IsServer)
        {
            // オーナーシップ変更可能な場合
            if (enableChangeOwner.Value == true)
            {
                // オーナーシップ変更を実施
                NetworkObject.ChangeOwnership(newValue);
            }
            else
            {
                // 変更不可の場合はこれまでの値に戻す
                newOwnerClientId.Value = oldValue;
            }
        }
    }

    /// <summary>
    /// オーナーシップ取得依頼
    /// </summary>
    public void TryGetOwnership()
    {
        // 自分のクライアントIDを設定
        newOwnerClientId.Value = NetworkManager.LocalClientId;
    }

    /// <summary>
    /// 操作権限変更不可設定
    /// </summary>
    public void SwitchDisableChangeOwner()
    {
        // 操作権限を持っている場合
        if (IsOwner)
        {
            // 権限変更不可に設定
            enableChangeOwner.Value = false;
        }

    }

    /// <summary>
    /// 操作権限変更可能設定
    /// </summary>
    public void SwitchEnableChangeOwner()
    {
        // 操作権限を持っている場合
        if (IsOwner)
        {
            // 権限変更可能に設定
            enableChangeOwner.Value = true;
        }

    }
}

作成したNetworkSharingObjectコンポーネントを前回作成したCubeにアタッチします。

f:id:napo909:20210724151449p:plain

Object Manipulatorに操作権限変更のための設定を行います。
操作開始時に、権限取得依頼と権限変更停止、操作終了時に権限変更再開を行うことで、誰かが操作している際には操作できないようにします。

f:id:napo909:20210724152550p:plain

Spawn処理を修正する

次に、ClientからもCubeをSpawnできるように、前回作成したNetworkSpawn.csを修正します。
NetworkVariableBoolを用いて、各ユーザ(Host, Client)がtrueにした際にServer(Host)側でSpawnを実行し、falseに戻します。

  • NetworkSpawn.cs
クリックで展開
using UnityEngine;
using MLAPI;
using MLAPI.NetworkVariable;
public class NetworkSpawn : NetworkBehaviour
{
    /// <summary>
    /// Spawnさせるプレハブ
    /// </summary>
    [SerializeField]
    private GameObject spawnPrefab;

    /// <summary>
    /// Spawnスイッチ
    /// 全ユーザ変更可能
    /// </summary>
    /// <returns></returns>
    NetworkVariableBool isSpawn
        = new NetworkVariableBool(new NetworkVariableSettings { WritePermission = NetworkVariablePermission.Everyone }, false); // 追加


    void OnEnable() // 追加
    {
        // OnStartSpawnイベント設定
        isSpawn.OnValueChanged += OnStartSpawn;
    }

    void OnDisable() // 追加
    {
        // OnStartSpawnイベント設定解除
        isSpawn.OnValueChanged -= OnStartSpawn;
    }

    public void SpawnObject()
    {
        isSpawn.Value = true; // 変更
    }

    void OnStartSpawn(bool oldValue, bool newValue) // 追加
    {
        // サーバかつisSpawnがTrueの場合実施
        if (IsServer && isSpawn.Value == true)
        {
            // Spawnされる場所は、自オブジェクトの1m奥、1m上に設定。
            Vector3 spawnPosition = this.transform.position + new Vector3(0f, 1f, 1f);

            // ローカルでSpawn
            GameObject spawnObject = Instantiate(spawnPrefab, spawnPosition, Quaternion.identity);

            // ほかのユーザでもSpawn実施
            spawnObject.GetComponent<NetworkObject>().Spawn();

            // フラグを下げる
            isSpawn.Value = false;
        }
    }
}

NetworkVariableにはNetworkObjectコンポーネントが必要なため、Spawn用のボタンにアタッチします。
ボタンは動的にSpawnしないオブジェクトのため、NetworkManagerに登録する必要はありません。

f:id:napo909:20210724160731p:plain

動作確認

今回も前回と同様にHoloLens側でConnect Host、PC側でConnect Clientボタンを押して共有を開始しました。

f:id:napo909:20210724162832g:plain

これで、PCのClient側からのSpawnや、他人がSpawnしたオブジェクトを別の人が操作できることを確認できました。
最低限物体共有を行うための機能ができました。