夜風のMixedReality

xRと出会って変わった人生と出会った技術を書き残すためのGeekなHoloRangerの居場所

XRミーティング2021 1月に登壇しました。

本日はイベント枠です。

〇xRミーティングとは?

月に一度第三水曜日に日本各地のxRコミュニティグループ同士でオンライン上で集まりxRに関するニュースや取り組んだこと等の情報を交換するイベントです。

イベントページは以下になります。

osaka-driven-dev.connpass.com

〇登壇内容

今回は最近取り組んでいる2点のHoloLensの開発に関する情報をシェアしました。

〇スライド

〇EyeTracking

EyeTrackingはHoloLens 2になって始めて搭載された機能です。

HoloLensのレンズ部分に搭載されたカメラによって装着者の虹彩を認識してユーザーの目線を追跡する機能です。

これを使用することによって頭を動かさず目を使った操作が可能になりました。

今回このEyeTrackingに関してと、その精度を動画でシェアしました。

youtu.be

また、MRTKExamplesで提供されているEyeTrackingサンプルが動かない問題をシェアしました。

redhologerbera.hatenablog.com

〇オリジナルShader

本ブログでも先日紹介したHoloLens 向けオリジナルShaderを作成した話とMRTKに同梱されているShaderとの違い、仕組みなどをシェアしました。

来月も参加したいです。

xRミーティングは毎回20名近い方が参加され、企業などのビジネス的なはなしではなくコミュニティーメンバーの情報共有なので非常に楽しいです。

興味がある方はぜひミーティングでお会いしましょう!

Photonのチュートリアルをやる その④ ルームへの接続UI

本日はPhotonのチュートリアル記事です。

〇Photonとは?

PhotonはUnityでのマルチユーザーアプリの実現のためのSDKとその仕組みを指します。

オリジナルのチュートリアルは下記になります。

doc.photonengine.com

〇UIの作成

これまでの状態でルームに接続し、ルームを退出する機能を作成しています。

しかしほとんどの場合のゲームではルームに接続するタイミングをユーザー自身に選択させる必要があります。(アプリを起動したときにいきなりルームに接続してマルチプレイヤーになることはよくない)

今回はルームに接続するためのUIを作成します。

①Launcherシーンを開きます。

②hierarchyウィンドウから右クリック、[UI]→[Button]でボタンを作成し[Play Button]という名前を付けます。

f:id:Holomoto-Sumire:20210118162705j:plain

これによって[Canvas]と[Button]が作成されました。

③[Play Button]のインスペクターウィンドウから[Button]の[OnClick()]にイベントを追加します。

f:id:Holomoto-Sumire:20210118163438j:plain

④[Launcher]の[connect()]を指定します。

f:id:Holomoto-Sumire:20210118163713j:plain

これでButtonをクリックすることでルームに接続されるようになりました。

⑤[Launcher]コンポーネントのStart関数から[Connect()]をコメントアウトします。

        void Start()
        {
            //Connect();
        }

Unityでシーンを実行してボタンを押すことで接続されることを確認します。

f:id:Holomoto-Sumire:20210118164551j:plain

〇プレイヤー名

マルチプレイヤーゲームにおいて必要なことの一つに相手の名前がわかることです。

これは、誰が今ルームに入っているのかを確認するうえでも大切な要素になります。

プレイヤー情報を管理するスクリプトを書きます。

①新規スクリプトを作成して[PlayerNameInputField]と名付けます。

f:id:Holomoto-Sumire:20210118165836j:plain

②[PlayerNameInputField]コンポーネントを開き編集します。

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

using Photon.Pun;
using Photon.Realtime;

using System.Collections;
using ADBannerView = UnityEngine.iOS.ADBannerView;

namespace Com.MyCompany.MyGame
{
    ///<summary>
    ///Player name input field.Let the user input his name,will appear above the player in the game.
    /// </summary>>
    [RequireComponent(typeof(InputField))]     
    public class PlayerNameInputField : MonoBehaviour
    {
        #region  Private Constants

        //Store the PlayerPref Key to avoid types
        private const string playerNamePrefKey = "PlayerName";

        #endregion

        #region MonoBehaviour CallBacke

        void Start()
        {
            string defaultName = string.Empty;
            InputField _inputField = this.GetComponent<InputField>();
            if (_inputField != null)
            {
                if (PlayerPrefs.HasKey(playerNamePrefKey))
                {
                    defaultName = PlayerPrefs.GetString(playerNamePrefKey);
                    _inputField.text = defaultName;   
                }
            }

            PhotonNetwork.NickName = defaultName;
        }

        #endregion

        #region  Public Methods

        public void SetPlayerName(string value)
        {
            if (string.IsNullOrEmpty(value))
            {
                Debug.LogError("Player Name is null orempty");
                return;
            }

            PhotonNetwork.NickName = value;
            
            PlayerPrefs.SetString(playerNamePrefKey,value);
        }

        #endregion
    }
   
}

プレイヤー名は各自が必ず持っていることが必要です。

RequireComponent(typeof(InputField));

ではInputFieldを強制的に追加する処理を行います。

PlayerPrefs.HasKey()、 PlayerPrefs.GetString() PlayerPrefs.SetString():

PlayerPrefsKey、Valueでデータを保存するものでプレイヤー名を保存する際に使用します。

プレイヤー名が既に存在している場合ユーザーは新たに入力することなく以前使用したプレイヤー名がそのまま使用されます。

PhotonNetwork.NickName:はPhotonのネットワーク上でニックネームを指定します。

〇プレイヤー名のUIを作成

①hierarchyウィンドウから[UI]→[Input Field]を選択し新規[Input Field]を作成し、その名前を[Name InputField]にします。

f:id:Holomoto-Sumire:20210119224741j:plain

②[Name InputField]の子オブジェクト[Placeholder]の[Text]に[Enter your Name...]を入力します。

f:id:Holomoto-Sumire:20210119224344j:plain

③[Name InputField]に[PlayerNameInputField]コンポーネントをアタッチします。

④[Name InputField]オブジェクトの[On Value Changed (String)]イベントに自身の[PlayerNameInputField]をアタッチします。

f:id:Holomoto-Sumire:20210119225135j:plain

⑤イベントに[SetPlayerName]を指定します。

f:id:Holomoto-Sumire:20210119225257j:plain

シーンを保存します。 以上でUIが完成しました。

f:id:Holomoto-Sumire:20210119225504j:plain

シーンを実行するとユーザー名の入力テキストボックスとボタンが表示されます。

ボタンをクリックすることでルームが呼び出されます。

HoloLens Meetup Vol.24 で登壇しました。

本日はイベント記事です。

〇HoloLens Meetupとは?

日本最大のHoloLensコミュニティHoloMagiciansの主催するイベントでHoloLensにかかわる事例や活動、作ったものなどを紹介するイベントでTokyo HoloLens Meetup今回でVol.24になります。

hololens.connpass.com

〇HoloLens 日本上陸4周年

本日2021年1月18日はHoloLens(初代)が日本に上陸してから4年の記念日になります。

この一年のHoloLensとMixedRealityのすさまじい進化に驚きます。

〇LT登壇

今回はLT登壇で筆者オリジナルHoloLens 2向けUIに関しての紹介を行いました。

●HoloLens アドベントカレンダー

HoloLensアドベントカレンダーは熊本を中心に活動されているHoloRangerガチ本さんの主催のQiita上のイベントで

2020年12月1日から25日までの間HoloLensに関連した記事を様々な人で毎日連載するという規格です。

筆者も数記事参加しています。

qiita.com

〇Dial型UI

ダイアル型UIは名前の通りダイアルを模したUIです。

ダイアルの回転角を指定することができ、その間の回転の割合で0~100までの値が出力されます。

また、任意の角度でのイベントを設定することもできます。

基本的にはMRTKで提供されている[PinchSlider]に非常に近い機能です。

●応用

 Dial仕組みは入出力のインターフェースだけではなく様々なものに応用できます。

 発表では2種類をシェアしました。

 一つ目は音量を調節するノズルとしての使い方です。

youtu.be

 二つ目はネジを回すという表現です。 

youtu.be

現在MRTKのGitHubへの提供に向けコードの修正とサンプルシーンを作成しています。

GitHub上と過去の記事でコードは公開していますので是非使ってみてください。

redhologerbera.hatenablog.com

SpatialMeshのスキャン表現に使える SpatialScanShaderを作成した。

本日はShader勉強枠です。

今回はHoloLens,HoloLens 2で使用する想定のオリジナルShader[SpatialScanShader]を作成しました。

〇使い方

MRTKを導入したプロジェクトから[SpatialAwareness]の構成で[SpatialScanShader]を使用したマテリアルをアタッチします。

〇実機で動かす。

youtu.be

〇解説

このShaderはMRTKで提供されているWireframe ShaderをベースにSR_TrianglesShaderのように空間メッシュに波紋が広がるような表現を目指しました。

WireframeShader、SR_TrianglesShaderではメッシュの情報を扱えるジオメトリシェーダーを使用することでメッシュごとに発色し表現を行っていました。

この場合ポリゴン単位の色の変化になるため滑らかな波を作成することはできませんでした。

このShaderではフラグメントシェーダーで重要な処理を行っています。

        float4 frag(v2f i ):COLOR{
            float radius;
            
            #if   _AUTO_WAVE_ON
            float speed = 1/_Speed;
                 radius =_WaveSize*(_Time/speed - floor(_Time/speed+1/2));
            #else
               radius = _Radius;
            #endif
          float dist = distance(_Center, i.worldPos);
            float Isize =_LineSize +_InnerSize;
            float intensity =1/_MasterIntensity ;
                float val = 1 - step(dist, radius - _LineSize) * _Inner;
                val = lerp(radius - Isize, dist,radius - 3) * step(dist, radius) * val/intensity;     
                return fixed4(val * _Color.r, val * _Color.g,val * _Color.b, 1.0);
        }
return fixed4(val * _Color.r, val * _Color.g,val * _Color.b, 1.0);

valは次のようになります。

最終的な出力でvalという変数に_Colorで指定した色の要素が積算され出力されます。

                float val = 1 - step(dist, radius - _LineSize) * _Inner;
                val = lerp(radius - Isize, dist,radius - 3) * step(dist, radius) * val/intensity;     
                return fixed4(val * _Color.r, val * _Color.g,val * _Color.b, 1.0);

lerp関数は第三引数で指定された割合で第一引数、第二引数の間を滑らかに補間する関数です。

                float val = 1 - step(dist, radius - _LineSize) * _Inner;

上部のこの式では円の内側を決める処理になります。

f:id:Holomoto-Sumire:20210117124240j:plain

step関数は段階的に値が変換される式です。これによってグラデーションを描くようになります。

_Innerはプロパティの[InnerIntensity]を指し、値を変えることで内部の発色具合を変えることができます。

f:id:Holomoto-Sumire:20210117124806j:plain

次の処理をコメントアウトすることでこの一行で行っている処理を可視化できます。

                float val = 1 - step(dist, radius - _LineSize) * _Inner;
              //  val = lerp(radius - Isize, dist,radius - 3) * step(dist, radius) * val/intensity;     
                return fixed4(val * _Color.r, val * _Color.g,val * _Color.b, 1.0);

f:id:Holomoto-Sumire:20210117125142p:plain

次の行では円の外側の描画に関する処理を行っています。

                val = lerp(radius - Isize, dist,radius - 3) * step(dist, radius) * val/intensity;     
                return fixed4(val * _Color.r, val * _Color.g,val * _Color.b, 1.0);

●AutoWave

このチェックボックスにチェックを入れることでスクリプトなどで外部からRadiusを扱わなくても、Shaderの処理だけで自動的に波紋を広げます。

      #if   _AUTO_WAVE_ON
            float speed = 1/_Speed;
                 radius =_WaveSize*(_Time/speed - floor(_Time/speed+1/2));
            #else
               radius = _Radius;
            #endif

この場合radiusは時間で変化するノコギリ波のグラフになります。

ノコギリ波とは次のような図形のことです。

f:id:Holomoto-Sumire:20210117132443p:plain
https://ja.wikipedia.org/wiki/%E3%81%AE%E3%81%93%E3%81%8E%E3%82%8A%E6%B3%A2 より引用

式で表すと次のようになります。

f:id:Holomoto-Sumire:20210117133034j:plain

これによって周期的に波が一方的に広がります。 波は三角関数を使用することでも可能ですが、この場合広がるだけではなく収縮するため今回はノコギリ波を使用しました。

_WaveSizeは振幅を指します。 これはUintyの場合メートル単位に相当しており、最終的に広がる波の大きさに相当します。

_TimeはUnityでの時間に相当してます。このノコギリ波の場合横軸に相当します。

sppedはノコギリ波の周期に相当します。speedが大きいほど素早く波が広がります。

 

〇shader全文

Shader "HoloMoto/SpatialScener"
{
    Properties {
        _Color ("Color", Color) = (1, 1, 1, 1)
        _Center ("CenterX", vector) = (0, 0, 0)
         [Toggle] _Auto_Wave("AutoWave", Float) = 0
        _WaveSize("MaxWaveSize",Float)=10
        _Speed("WaveSpeed",float)=1
        _Radius ("Radius", float) = 5
        _LineSize("LineSize",Range(0,1))=0.075
        _Inner("InnerIntensity",Range(0,1))=0.806
        _InnerSize("InnerSize",Range(0,200))=0.14
        _MasterIntensity("MasterIntensity",Range(0,10))=0.2
    }
    SubShader
    {
      Tags{"RenderType"="Opaque"}
      Blend SrcAlpha OneMinusSrcAlpha
      BlendOp Add
      ZTest LEqual
      ZWrite On
      Cull Back
        Pass{
        CGPROGRAM
        #pragma  vertex vert
        #pragma  fragment frag
        
        #pragma shader_feature _AUTO_WAVE_ON
        #include  "UnityCG.cginc"
            float4 _Color;
            float3 _Center;
            float _Radius;
            float _LineSize;
            float _Inner;
            float _InnerSize;
            float _MasterIntensity;
        struct v2f
        {
            float4 pos : SV_POSITION;
            float3 worldPos : TEXCOORD1;
        };

        v2f vert(appdata_base v)
        {
            v2f o;
            o.pos = UnityObjectToClipPos(v.vertex);
            o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
            return o;
        }
        float _Speed;
        float _Test;
        float _WaveSize;
        float4 frag(v2f i ):COLOR{
            float radius;
            
            #if   _AUTO_WAVE_ON
            float speed = 1/_Speed;
                 radius =_WaveSize*(_Time/speed - floor(_Time/speed+1/2));
            #else
               radius = _Radius;
            #endif
          float dist = distance(_Center, i.worldPos);
            float Isize =_LineSize +_InnerSize;
            float intensity =1/_MasterIntensity ;
                float val = 1 - step(dist, radius - _LineSize) * _Inner;
                val = lerp(radius - Isize, dist,radius - 3) * step(dist, radius) * val/intensity;     
                return fixed4(val * _Color.r, val * _Color.g,val * _Color.b, 1.0);
        }
        ENDCG    
            }
        
    }
    FallBack "Diffuse"
    FallBack "Mixed Reality Toolkit/Standard"
}

Photonのチュートリアルをやる その③ ゲームシーン

本日はPhotonのチュートリアル枠です。

〇Photonとは?

PhotonはUnityでのマルチユーザーアプリの実現のためのSDKとその仕組みを指します。

オリジナルのチュートリアルは下記になります。

doc.photonengine.com

前回ルームへの接続を学びました。今回はルームの作成と退出の仕組みを学びます。

redhologerbera.hatenablog.com

redhologerbera.hatenablog.com

〇ルームの作成

①Unityのプロジェクトウィンドウから新規のシーンを作成し[Room for 1]と名付けます。

f:id:Holomoto-Sumire:20210116155957j:plain

②[Floor]という名前の[Cube]を作成し原点(0,0,0)に配置します。

f:id:Holomoto-Sumire:20210116160238j:plain

③[Floor]のスケールを[20,1,20]に設定します。

f:id:Holomoto-Sumire:20210116160421j:plain

床が作成できたので次に壁を作成します。

④[Floor]を[Ctrl]キー+[D]キーで複製し、[Wall]という名前に変え、[Wall]を4つ作成します。 これは周囲の壁に当たります。

それぞれの[Wall]のTransformは次のようになります。

f:id:Holomoto-Sumire:20210116160940j:plain

f:id:Holomoto-Sumire:20210116161125j:plain

f:id:Holomoto-Sumire:20210116161246j:plain

f:id:Holomoto-Sumire:20210116161352j:plain

これでルームのオブジェクトが完成しました。

〇ゲームマネージャープレファブ

マルチユーザーゲームのすべてにおいて最も重要なインターフェースの基本要件は『ルームから退出できること』のようです。このためにボタンと連動してPhotonのルームから退出する仕組みを実装します。

①プロジェクトウィンドウから[GameManager]という名前のスクリプトを作成します。 

f:id:Holomoto-Sumire:20210116161724j:plain

②hierarchyウィンドウから空のゲームオブジェクトを配置し[GameManager0]という名前を付けます。また[GameManager0]オブジェクトに[GameManager]コンポーネントをアタッチします。

f:id:Holomoto-Sumire:20210116162233j:plain

③[GameManager]スクリプトを編集します。

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

using Photon.Pun;
using Photon.Realtime;

namespace Com.MyCompany.MyGame
{
    public class GameManager : MonoBehaviourPunCallbacks
    {
        #region Photon Callbacks

        /// <summary>
        /// Called when the local player left the room. We need to load the launcher scene.
        /// </summary>
        public override void OnLeftRoom()
        {
            SceneManager.LoadScene(0);
        }
        #endregion

        #region Public Methods

        public void LeaveRoom()
        {
            PhotonNetwork.LeaveRoom();
        }
        
        #endregion
    }
}
  public override void OnLeftRoom()
        {
            SceneManager.LoadScene(0);
        }

はユーザーがルームから退出した際に読み込まれます。

SceneManager.LoadScene(0)

はUnityのシーン遷移に用います。0はBuildSettingsで指定する一番最初に読み込まれるシーンです。

また

 public void LeaveRoom()
        {
            PhotonNetwork.LeaveRoom();
        }

は外部から呼び出してルームを退出することができます。

④[GameManager0]を[Project]ウィンドウにドラッグアンドドロップし、プレファブを作成します。

f:id:Holomoto-Sumire:20210116163659j:plain

Unityではロビー、ルームといった複数のシーンで一つのアプリケーションが完成します。 この複数のシーンで[GameManager0]を再使用するためにプレファブ化しています。

〇ルームの退出ボタン

ルームを退出するための機能を作成したのでボタンを作成し、機能と結び付けます。

①hierarchyウィンドウで[UI/Plane]を新規作成します。

f:id:Holomoto-Sumire:20210116164122j:plain

②作成したPlaneに[Top Plane 1]と名付けます。

f:id:Holomoto-Sumire:20210116164933j:plain

③[Top Plane 1]オブジェクトから[Image]と[Canvas Renderer]を削除します。

f:id:Holomoto-Sumire:20210116165417j:plain

④ [Top Plane 1]の[RectTransform]の高さを50に指定します。

⑤ [Top Plane 1]の子オブジェクトに[Button]を配置し[Leave Button]と名付けます。

f:id:Holomoto-Sumire:20210116170508j:plain

⑥[Leave Button]のinspectorウィンドウから[OnClick()]にイベントを追加し[GameManager0]の[LeaveRoom()]を指定します。

f:id:Holomoto-Sumire:20210116170728j:plain

⑦[Leave Button]をプロジェクトウィンドウにドラッグアンドドロップしプレファブを作成します。

f:id:Holomoto-Sumire:20210116170823j:plain

〇ほかのルームの作成

[Room for 1]を複製し[Room for 4]までを作成します。

f:id:Holomoto-Sumire:20210116171005j:plain

〇ビルド設定

[GameManager]コンポーネントではBuildSettingsのシーン番号で遷移シーンを変えていました。

BuildSettingsを開いてシーンをaddします。

[Launcher]シーンが0で[Room for 〇]に対応する順番でシーンを追加します。

f:id:Holomoto-Sumire:20210116171411j:plain

以上でゲームシーンの完成が完了しました。

Photonのチュートリアルをやる その② ロビー

①プロジェクトウィンドウに新規のC#スクリプトを作成し、[Launcher]という名前を付けます。

f:id:Holomoto-Sumire:20210115115234j:plain

②[Launcher]を次のように編集します。

using System;
using Photon.Pun;
using UnityEngine;


namespace  Com.MyCompany.MyGame
{

    public class Launcher : MonoBehaviour
    {
        #region Private Serializable Fields
        #endregion
        
        #region Private Fields

        /// <summary>
        /// This client`s version number. Users are separated from each other by gameVersion(which allows you to make breaking changers).
        /// </summary>
        string gameVersion = "1";
        #endregion

        #region  Monobihaviour CallBackes

        
        private void Awake()
        {
            //#Critical
            //this makes sure we can use PhotonNetwork.LoadLevel() on the master client and all clients in the same room sync their level automatically
            PhotonNetwork.AutomaticallySyncScene = true;
        }
        /// <summary>
        /// MonoBehaviour method called on GameObject by Unity during initialization
        /// </summary>
        void Start()
        {
            Connect();
        }

        #endregion

        #region Public Methods

        /// <summary>
        /// Start the connection process.
        ///  If already connected,we attempt joining a random room
        ///  If not yet connected,Connect this application instance to Photon Cloud
        /// </summary>
        void Connect()
        {
            //we check if we are connected or not, we hoin if we are , else we initialize
            if (PhotonNetwork.IsConnected)
            {
                //#Critical we need at this point ot attempt hoining a Random Room.
                PhotonNetwork.JoinRandomRoom();
            }
            else
            {
                //#Critical, we must first and foremost connect to Photon Online Serer
                PhotonNetwork.GemeVersion = gameVersion;
                PhotonNetwork.ConnectUsingSettings();
            }
            
        }

        #endregion
    }
}
スクリプトの説明
namespace  Com.MyCompany.MyGame
{
...
}

ネームスペースを付けることでこの[Launcher]コンポーネントが他の[Launcher]コンポーネントと同一に判断されず唯一のものに区別されます。

 private void Awake()
        {
            PhotonNetwork.AutomaticallySyncScene = true;
        }

マスタークライアントでPhotonNetwork.LoadLevel()を使用し同じルームのクライアントが自動的に同期できるように設定します。

        void Start()
        {
            Connect();
        }

Start関数ではそのままConnectメソッドへ飛んでいます。

●Start関数とAwake関数の違い。

Start関数とAwake関数はどちらも最初に一回呼ばれるメソッドですが、二つを比較するとAwakeが先に呼ばれStart関数が呼ばれます。

呼ばれる順番に違いがあるようです

参考

qiita.com

 void Connect()
        {
            //we check if we are connected or not, we hoin if we are , else we initialize
            if (PhotonNetwork.IsConnected)
            {
                //#Critical we need at this point ot attempt Joining a Random Room.
                PhotonNetwork.JoinRandomRoom();
            }
            else
            {
                //#Critical, we must first and foremost connect to Photon Online Serer
                PhotonNetwork.GameVersion = gameVersion;
                PhotonNetwork.ConnectUsingSettings();
            }
            
        }

Connectメソッドではその名の通りPhotonの接続を行います。

                PhotonNetwork.JoinRandomRoom();

現在接続されている場合フォトンサーバーに接続します。

                PhotonNetwork.GameVersion = gameVersion;
                PhotonNetwork.ConnectUsingSettings();

③前回作成した空のゲームオブジェクト[Launcher]オブジェクトにスクリプトをアタッチします。

f:id:Holomoto-Sumire:20210115133814j:plain

④Unityの上部ワールドタブから[Window]→[Photon Unity Networking]→[Highlight Photon Server Settings]を開きます。

⑤[PUN Logging]を[Full]に指定します。

f:id:Holomoto-Sumire:20210115140054j:plain

これでPhotonに接続する準備が完了しました。

⑥Unityエディタを実行します。

リージョンを検索し、JPであることを処理したログが表示されます。

PUN got region list. Going to ping minimum regions, based on this previous result summary: 
Region Pinging Result: jp[5.8.70.143:5055]: 9ms 

f:id:Holomoto-Sumire:20210115141207j:plain

〇コールバックの実装

コールバックを実装するために[Launcher]スクリプトを開き[MonoBehaviour]を[MonoBehaviourPunCallbacks]にベースクラスを書き換えます。

public class Launcher : MonoBehaviourPunCallbacks
{

[Photon.Realtime]を追加します。

using System;
using Photon.Pun;
using UnityEngine;
using Photon.Realtime;

namespace  Com.MyCompany.MyGame
{
 ...

次にコールバックの処理をクラスの最後に加えます。

        #region MonoBehaviourPunCallbacks Callbacks

        public override void OnConnectedToMaster()
        {
           Debug.Log("PUN Basics Tutorial/Launcher: OnConnectedToMaster() was called by PUN");
        }

        public override void OnDisconnected(DisconnectCause cause)
        {
           Debug.LogWarningFormat("PUN Basics Tutorial/Launcher: OnDisconnected() was Called by PUN with reason {0}",cause);
        }

        #endregion

この状態でUnityのエディタ上で実行することでPhotonのコールバックを取得することができます。

f:id:Holomoto-Sumire:20210115144828j:plain

 このコールバックはユーザーに接続状況を知らせることなど様々な場面で使用できます。

[OnConnectedToMaster()]にPhotonNetworkへの接続処理を加えます。

        public override void OnConnectedToMaster()
        {
           Debug.Log("PUN Basics Tutorial/Launcher: OnConnectedToMaster() was called by PUN");
           //#Critical: The first we try to do is to hoin a potential existing room.If there is, good, else, we'll be called back with OnJoinRandomFailed()
           PhotonNetwork.JoinRandomRoom();
        }

ルームの参加に失敗した場合その旨を通知する必要があります。

次の文をクラスに追加します。

public override void OnJoinRandomFailed(short returnCode, string message)
{
    Debug.Log("PUN Basics Tutorial/Launcher:OnJoinRandomFailed() was called by PUN. No random room available, so we create one.\nCalling: PhotonNetwork.CreateRoom");

    // #Critical: we failed to join a random room, maybe none exists or they are all full. No worries, we create a new room.
    PhotonNetwork.CreateRoom(null, new RoomOptions());
}

public override void OnJoinedRoom()
{
    Debug.Log("PUN Basics Tutorial/Launcher: OnJoinedRoom() called by PUN. Now this client is in a room.");
}

これをUnityエディタで実行します。

するとルームに接続できたログが表示されます。

f:id:Holomoto-Sumire:20210115182752j:plain

これでルームに入るためのロビーが完成しました。

Photonのチュートリアルをやる その①

本日はUnity調査記事です。

Unityではマルチプラットフォームでゲームを開発することができます。 近年のゲームではオンライン対戦や通信など、ネットワークを使用して他者とのやり取りが行えるものが多いです。

Unityでは自身でサーバーを構築する方法やAzureやAWSなどを使用する方法、様々な実装方法があります。今回はその中の一つであるPhotonのチュートリアルを行います。

〇Photonとは?

一言で表すとUnityでのマルチユーザーアプリの実現のためのSDKとその仕組みを指します。

チュートリアル

オリジナルのチュートリアルは下記になります。

doc.photonengine.com

Photon Cloudによってマルチプレイヤーアプリの開発とPUN(Photon Unity Networking)を使用したアプリ開発を学びます。

〇準備・導入

①Unityで新規プロジェクトを作成します。

今回はUnity2019.3.9f1を使用しました。

②シーン名を[Launcher]にして、ヒエラルキーウィンドウから[Launcher]という名前の空オブジェクトを追加します。

f:id:Holomoto-Sumire:20210114154734j:plain

③ウェブブラウザからPhotonのサイトにアクセスしダッシュボードを開きます。

dashboard.photonengine.com

④マイアプリからアプリケーションIDをコピーします。

f:id:Holomoto-Sumire:20210114164005j:plain

アプリケーションを作成していない場合は新規で作成します。

f:id:Holomoto-Sumire:20210114164812j:plain

③Unityに戻りAssetsStoreから[Pun2]と検索バーに入力します。

f:id:Holomoto-Sumire:20210114161908j:plain

④[Pun2]のアセットを見つけ[Import]を行い自身のプロジェクトにPhotonのアセットを導入します。

f:id:Holomoto-Sumire:20210114162332j:plain

⑤アセットが導入されるとウィザードが起動します。④でコピーしたIDを貼り付けます。

f:id:Holomoto-Sumire:20210114165125j:plain

エンターキーを押すことでウィザードが閉じます。

以上で準備が完了しました。

〇PhotonCloud

photonCloudは上記の手順で使用したウィザードを含むマルチプラットフォームを簡単に開始するためのセットアップのことです。

PhotonクラウドはPhotonサーバーの実行されている数多くのマシンの集まりを指し、Exit Games社のメンテナンスを受け動いています。 プレイヤー人数を増やすこともできますが、無料プランの場合同時接続20人となっています。

上記手順で導入したPUN2(Photon Unity Networking)Photon Cloudを管理しています。

次の流れで処理が行われています。

①各アプリが「ネームサーバー」に接続する。

②クライアントが使用する情報をチェックする。

③マスターサーバーに転送する。

マスターサーバーとはリージョンサーバーのハブ(中継)です。

〇ルーム

ルームはphotonを使用して同期したユーザー同士がコミュニケーションや対戦といった相互通信を行うための環境を指します。

f:id:Holomoto-Sumire:20210114173718p:plain
https://doc.photonengine.com/ja-jp/realtime/current/reference/glossary より引用

ルーム外では相互のやり取りを行うことができません。

Photon Cloudはルームベースのゲームを念頭に開発されています。

〇ロビー

ロビーはユーザーがルームに接続するまでのローカルのコンテナを指します。

実装次第でロビーから複数のルームの中から一つのルームを選択して入ることができます。

チュートリアルの流れ

①ロビーシーンの作成

②UIを使用しロビーを改善

③ゲームシーンの作成

④レベルのローディングの実装

⑤複数のユーザープレファブの作成

⑥カメラのプレイヤーへの追従

⑦ネットワーク機能の追加

⑧シーンの切り替えとプレイヤーの初期化

⑨プレイヤーユーザーインターフェース

それぞれ1記事で勉強していきます。