【Unity + Photon + WebGL】マルチプレイヤー対応のバーチャル空間を自作してみた

<br>
Unityで作ったものをマルチプレイヤー対応のバーチャル空間をウェブで公開してみた。
UnityとPhotonとWebGLを使えばできた。


##動作確認バージョン
Unity 2019.4.22f1


##Photon
Photonにアクセス
https://www.photonengine.com/ja/

アカウント登録して、アプリ新規登録する。

※全世界を対象としてなく日本国内のみでする場合は、リージョンは jp にしたほうが動作は早い。

##Unity
Unityを起動する。

このとき作ったプログラム、アニメーションを使う　（カメラ追随以外）
https://qiita.com/tatsuya1970/items/5dea86208951574fcc9c

###PUN2
Asset STOREで、 PUN2 -FREE をダウンロードし、インポートする。
[![Image from Gyazo](https://i.gyazo.com/d8a3161e3464123ab696563e505e27a5.png)](https://gyazo.com/d8a3161e3464123ab696563e505e27a5)


ポップアップが出るので、さきほどのPhotonのウェブサイトで作成したアプリのアプリケーションＩＤを入力
[![Image from Gyazo](https://i.gyazo.com/9205bb456a8b152af7c6e262d6eaaddf.png)](https://gyazo.com/9205bb456a8b152af7c6e262d6eaaddf)

Photon ServerSetting というのができる。

[![Image from Gyazo](https://i.gyazo.com/9eceead4d017818a46df2132ad05f2b1.png)](https://gyazo.com/9eceead4d017818a46df2132ad05f2b1)

###アバターの設定
[Resources] フォルダを作成する。
プレイヤー自身のアバターとして表示するゲームオブジェクトのプレハブを作成し、「Resources」フォルダーの中に入れる。
アバターは３つ用意し、後述するスクリプトでは乱数でアバターを選ぶようにした。
[![Image from Gyazo](https://i.gyazo.com/114898e8c467c475b3ad1056d451d80d.png)](https://gyazo.com/114898e8c467c475b3ad1056d451d80d)

アバターなど人型の場合
Avatarのinspectorには
Add Componentから [Photon view]を追加
[![Image from Gyazo](https://i.gyazo.com/2993445e822dc91ca9a0f40bbea86a94.png)](https://gyazo.com/2993445e822dc91ca9a0f40bbea86a94)

[Photon Transform View]を追加
[![Image from Gyazo](https://i.gyazo.com/d0ea039fab016665f58c3f8fd51563c6.png)](https://gyazo.com/d0ea039fab016665f58c3f8fd51563c6)


[Photon Animator View]を追加
以下設定

disable（同期しない）
discrete（非連続的 = 一定間隔で同期）
continuous（連続的 = 常に同期）

[![Image from Gyazo](https://i.gyazo.com/d113e87c81dcd8f3a568df23c750073b.png)](https://gyazo.com/d113e87c81dcd8f3a568df23c750073b)

<BR>
もちろん、RigidBodyとCollidar　は忘れずに。
ヒト型は不安定なのでFreeze Rotation　のXYZにチェック
[![Image from Gyazo](https://i.gyazo.com/7ba364d3ca41400c5597311194440998.png)](https://gyazo.com/7ba364d3ca41400c5597311194440998)

<BR>
今まで述べてきたInspectorをまとめると、
[![Image from Gyazo](https://i.gyazo.com/7b1fda47a47bae15e5ec658ffe5f6742.png)](https://gyazo.com/7b1fda47a47bae15e5ec658ffe5f6742)




<BR>
なお、inspector と関係ないが、Hierarchy にデフォルトで存在する Main Camera は絶対に消さないこと。
※スクリプト上で各参加者を追随するカメラとして利用するため


##スクリプト

空のオブジェクトを作り、そこに以下のコードをアタッチ


```PhotonController.cs
using Photon.Pun;
using Photon.Realtime;
using UnityEngine;

public class PhotonController : MonoBehaviourPunCallbacks
{
    private void Start()
    {
        // PhotonServerSettingsの設定内容を使ってマスターサーバーへ接続する
        PhotonNetwork.ConnectUsingSettings();
    }

    // マスターサーバーへの接続が成功した時に呼ばれるコールバック
    public override void OnConnectedToMaster()
    {
        // "Room"という名前のルームに参加する（ルームが存在しなければ作成して参加する）
        PhotonNetwork.JoinOrCreateRoom("Room", new RoomOptions(), TypedLobby.Default);
    }

    // ゲームサーバーへの接続が成功した時に呼ばれるコールバック
    public override void OnJoinedRoom()
    {
        //何番目の入室者か？　１から順番通り採番される。退室者がいた場合の法則は未確認
        //今回使わないけど、今後何かで必要そうなので備忘録としてここに残してます。
        int num = PhotonNetwork.CurrentRoom.PlayerCount;

        //自身のアバターはランダムで決定
        //今回のアバター３体で、名をAvatar1,Avatar2,Awatar3とし、Resources フォルダに入れておく
        string avatarName="";
        int _ranNum = Random.Range(1, 4);
        avatarName = "Avatar" + _ranNum;
        
        // ランダムな座標に自身のアバター（ネットワークオブジェクト）を生成する
        var position = new Vector3(Random.Range(-10f, 10f), Random.Range(10f, 0f), Random.Range(-10f, 10f));
        GameObject avatar = PhotonNetwork.Instantiate(avatarName, position, Quaternion.identity);

        var camera = GameObject.Find("MainCamera");

        camera.transform.parent = avatar.transform;
        //camera.transform.position = avatar.transform.position;

        var pos = avatar.transform.position;

        camera.transform.position = new Vector3(pos.x , pos.y+1.0f, pos.z);


    }
}


```

<br>
アバターのスクリプト Walk.cs の
public class Walk : MonoBehaviourを
public class Walk : MonoBehaviourPunCallbacks　に変更

Update()関数に
if (photonView.IsMine)
を追加。

これで、自分のオブジョクトとその他オブジョクトの切り分けができる。


```Walk.cs
using UnityEngine;
using System.Collections;
using Photon.Pun;
using Photon.Realtime;

public class Walk : MonoBehaviourPunCallbacks
{

    private Animator animator;
    // Use this for initialization
    void Start()
    {
        animator = GetComponent<Animator>();
    }

    // Update is called once per frame
    void Update()
    {
        if (photonView.IsMine)
        {

            if (Input.GetKey("up"))
            {
                transform.position += transform.forward * 0.02f;
                animator.SetBool("is_walking", true);
            }
            else if (Input.GetKey("down"))
            {
                transform.position -= transform.forward * 0.02f;
                animator.SetBool("is_walking", true);
            }
            else
            {
                animator.SetBool("is_walking", false);
            }
            if (Input.GetKey("right"))
            {
                transform.Rotate(0, 1.0f, 0);
            }
            if (Input.GetKey("left"))
            {
                transform.Rotate(0, -1.0f, 0);
            }
        }
    }

}

```



##ビルド
WebGLでやってみる

[![Image from Gyazo](https://i.gyazo.com/1d4de44c02420c0cdd59df48508235b7.png)](https://gyazo.com/1d4de44c02420c0cdd59df48508235b7)


インストールする必要があるので、クリック

モジュールを追加
[![Image from Gyazo](https://i.gyazo.com/6c8e20c888ccee9c6ccd7326a8b10c58.png)](https://gyazo.com/6c8e20c888ccee9c6ccd7326a8b10c58)


再度ビルド！


HTMLファイルなどウェブサイト公開に必要なファイルができる。
[![Image from Gyazo](https://i.gyazo.com/4912caad4739716a7e5c48911aba4605.png)](https://gyazo.com/4912caad4739716a7e5c48911aba4605)

いろいろなやり方があるが、
私は、できたものをそのまま、契約しているレンタルサーバーにFTPでファイル転送。（使用ソフトはCyberduck)


完了！！


<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">なんとか２日かかって、自分のレンタルサーバー上にバーチャル空間を構築しました（Unity + Photon +WebGL）<br>こちらのリンク先　→　<a href="https://t.co/uL4L1tNH4s">https://t.co/uL4L1tNH4s</a><br>・マルチプレイヤー対応（20人まで）<br>・アバター３種類（選択できない）<br>・チャット機能無し、視点選べない<br>と、まったく何も無いですが・・ <a href="https://t.co/B4ZyuSsYn2">pic.twitter.com/B4ZyuSsYn2</a></p>&mdash; たつや (@tatsuya1970) <a href="https://twitter.com/tatsuya1970/status/1424687882439065604?ref_src=twsrc%5Etfw">August 9, 2021</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>



##プレイヤー名
2021.8.11追記

このままでは、どれが誰のアバターなのか判別がつかないので、アバターの頭上にプレイヤー名を表示してみました。
プレファブのアバターをクリックして、空のオブジェクトを追加（この画像では名前を　NameDisplay）

inspector に　TextMesh Pro を追加すると、いろんなものが付いてくる。

[![Image from Gyazo](https://i.gyazo.com/9af6b1e007d8f36c9b75a88dc79040ff.png)](https://gyazo.com/9af6b1e007d8f36c9b75a88dc79040ff)

NameDisplay オブジェクトに、以下のスクリプトをアタッチする

``` AvatarNameDisplay.cs
using Photon.Pun;
using TMPro;

// MonoBehaviourPunCallbacksを継承して、photonViewプロパティを使えるようにする
public class AvatarNameDisplay : MonoBehaviourPunCallbacks
{
    private void Start() {
        var nameLabel = GetComponent<TextMeshPro>();
        // プレイヤー名とプレイヤーIDを表示する
        nameLabel.text = $"{photonView.Owner.NickName}({photonView.OwnerActorNr})";
    }
}

```

テキストの位置調整
[![Image from Gyazo](https://i.gyazo.com/e716951a38c4edb5157d7e0eea27171c.png)](https://gyazo.com/e716951a38c4edb5157d7e0eea27171c)

PhtonContoroller.cs の private void Start() に Player名設定のコードを追加

```PhtonContoroller.cs 
private void Start()
    {
        // プレイヤー自身の名前を"Player"に設定する
        PhotonNetwork.NickName = "Player";

        // PhotonServerSettingsの設定内容を使ってマスターサーバーへ接続する
        PhotonNetwork.ConnectUsingSettings();
    }
```

できた！

でも、手前側が裏になってしまいますww

![236884580_4247686941946474_6227506079556874046_n.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/291592/c4894fde-ae1f-c47b-9545-b844748189fb.jpeg)




<br>

##参考サイト

https://www.lifull.blog/entry/2021/04/01/105611

https://zenn.dev/o8que/books/bdcb9af27bdd7d/viewer/c04ad5

https://qiita.com/tsukumaru/items/99b1376fdcdd62316324

https://jsapachehtml.hatenablog.com/entry/2019/02/16/185457