Boss Roomいじり2:キャラクター選択→適当な待機室を作成

Boss Roomいじり、その2です。

前回はメインメニューシーンをいじって2Dキャンバスを追加することに成功しました。

今回は、CharSelect(キャラクター選択)シーンをいじります。

私のゲームでは、このシーンではプレイヤーは自由に動けるようにして、

マップ上のお店などで各プレイヤーの準備を整えるシーンにしたいと思っています。

なので、まずは2Dのマップを作り、2Dのキャラクターが動けるようにしたいと思います。


【キャラクターやマップの素材のダウンロード】

スタジオしまづ様による解説動画

の通りにやりました。

ぴぽや倉庫様から素材をダウンロードし、

Assets > GameData2D > Character にキャラ素材をダウンロードしました。

Assets > GameData2D > Map にマップ素材をダウンロードしました。(32×32)

スライス等の処理は動画の通りで、

まず、ダウンロードした画像たちに

Sprite Mode: Multiple

Pizels Per Unit: 32(これは32*32の素材だから)

Filter Mode: Point(no filter)

とします。


そして画像をクリックした時のInspectorの真ん中上くらいのSprite Editorにて、

SliceのTypeをGrid By Cell Sizeにして、サイズを32&32にします。

そしてSliceボタン、Applyボタンとクリックすれば素材をきちんと分割できます。

これで一枚の絵に収まった素材をいくつかのパーツに分割できます。

マップも同様にスライスします。


【キャラクターを置く】

適当な画像、ここでは動画の通りサンタ女子の二枚目の画像(正面)をドラッグして

ゲームオブジェクトとして置き、名前をplayerにします。


【キャラクターの移動】

参考:スタジオしまづ様の解説動画

Asset > Scripts > 2DScriptsMoguというフォルダを作りました。

元々ある他のフォルダと分けたかったからです。

その中に、PlayerController.csを作り、上記のplayerオブジェクトにアタッチします。

そしてその中身は

namespace Unity.Multiplayer.Samples.BossRoom
{
    public class PlayerController : MonoBehaviour
    {
        public float velocity;
        // Start is called before the first frame update
        void Start()
        {
            velocity=5f*Time.deltaTime;
        }

        // Update is called once per frame
        void Update()
        {
            // キーボードで移動する
            float x = Input.GetAxisRaw("Horizontal");
            float y = Input.GetAxisRaw("Vertical");
           
            transform.position += new Vector3(x,y)*velocity;
       
        }
    }
}

こんな感じにしました。キーボードの上下左右キーで動きます。

これでサンタ女子が動きました。


【アニメーション】

このままでは平行移動してしまうので

アニメーションを付けたいところですが、

とりあえず今回はスキップ。

スタジオしまづ様の解説動画にわかりやすい解説があるので、

後で見て実装します。


【背景の作成:タイルマップ・障害物の作成】

またまたスタジオしまづ様の解説動画の通りにやっていきます。

これも後で実装。


【キャラクターの同期】

この時点では、キャラクターは各プレイヤーのローカルでしか存在していないので、

これを部屋に居る全員に同期します。

これはBoss Roomで既に実装されているので、どのようにしているのか調べます。

BossRoomにて、実際にボスルームに入ってみますと、


こんな感じにキャラクターが生成され、Hierarchyタブでいうと
(見にくいですが)赤枠のPlayerAvatar0というのが緑丸のキャラクターに対応しており、
緑枠のコンポーネントが付いています。多いですね・・・
この中でネットワーク関係だと思われるものは
・Network Object
・Network Health State
・Network Life State
・Server Character
・Server Animation Handler
・Server Character Movement
・Network Transform
・Network Avatar Guid State
・Network Name State
・Network Stats
・UIStateDisplayHandler
・Player Server Character
・Client Input Sender
・Publish Message On Life Change
・Client Player Avatar
・Client Avatar Guid Handler
です。多すぎる!
多いけど、一つ一つ見ていきましょう。

・Network Object
(\Library\PackageCache\com.unity.netcode.gameobjects@1.7.1\Runtime\Core\NetworkObject.cs)
これはライブラリのスクリプトなのでオブジェクトにはつけるもんなんでしょう。

・NetworkHealthState(/Scripts/Gameplay/GameplayObjects/NetworkHealthState.cs)
キャラクターのHPの同期・管理っぽいですね。
私が作りたいものにそのまま流用できそうです。

・NetworkLifeState(/Scripts/Gameplay/GameplayObjects/NetworkLifeState.cs)
生きている、気を失っている、死んでいるなどの状態の同期・管理っぽい。
これもそのまま流用。

・ServerCharacter
(\Scripts\Gameplay\GameplayObjects\Character\ServerCharacter.cs)
これは結構内容が多いスクリプトになっています。
流用したとしても、もしかしたら内容変更をする必要があるかもしれませんね。

・Server Animation Handler
(Scripts\Gameplay\GameplayObjects\Character\ServerAnimationHandler.cs)
これはLifeStateに合わせて何かを同期させているようです。
例えば、仲間が死ぬと死んだモーションをする、とかでしょうか?
使えそう。

・Server Character Movement
(Scripts\Gameplay\GameplayObjects\Character\ServerCharacterMovement.cs)
    /// <summary>
    /// Component responsible for moving a character on the server side based on inputs.
    /// </summary>
と書いているので、インプットに応じたサーバーサイドのキャラクターの動きに
関するスクリプトです。
結構豊富な内容になっていますので、
一部流用する感じになると思います。

・Network Transform
(\Library\PackageCache\com.unity.netcode.gameobjects@1.7.1\Components\NetworkTransform.cs)
これはそのまま使うやつ。

・Network Avatar Guid State
(\Scripts\Gameplay\GameplayObjects\Character\NetworkAvatarGuidState.cs)
guidはGUID(全世界のUnityプロジェクト内で唯一のID)のことらしい。
内容はよくわからないが、このまま使ってよさそう。

・Network Name State
(Scripts\Utils\NetworkNameState.cs)
各プレイヤーの名前の管理かな?そのまま使えそう。


・Network Stats(Scripts\Utils\NetworkNameState.cs)
各プレイヤーのキャラクターの名前管理。
たぶんそのまま使える。

・UIStateDisplayHandler
(Scripts\Gameplay\UI\UIStateDisplayHandler.cs)
    /// <summary>
    /// Class designed to only run on a client. Add this to a world-space prefab to display health or name on UI.
    /// </summary>
    /// <remarks>
    /// Execution order is explicitly set such that it this class executes its LateUpdate after any Cinemachine
    /// LateUpdate calls, which may alter the final position of the game camera.
    /// </remarks>
自分のキャラクターの名前、HPなどのUIに表示するための状態管理。
できれば他のプレイヤーの名前、HPなども表示したいので、
最終的にはいじる必要がありそう。

・Player Server Character
(Scripts\Gameplay\GameplayObjects\Character\PlayerServerCharacter.cs)
なんかよくわからんけどサーバに接続したプレイヤーたちの情報を管理するための
スクリプトらしい。そのまま使えそう。

・Client Input Sender
(Scripts\Gameplay\UserInput\ClientInputSender.cs)
ここで各プレイヤーの入力(マウスクリック、キーボード入力など)に応じた
アクションが定義されます。
例えば、左クリックで
                if (Input.GetMouseButtonDown(0))
                {
                    RequestAction(GameDataSource.Instance.GeneralTargetActionPrototype.ActionID, SkillTriggerStyle.MouseClick);
                }
が呼ばれるので、
        /// <summary>
        /// Request an action be performed. This will occur on the next FixedUpdate.
        /// </summary>
        /// <param name="actionID"> The action you'd like to perform. </param>
        /// <param name="triggerStyle"> What input style triggered this action. </param>
        /// <param name="targetId"> NetworkObjectId of target. </param>
        public void RequestAction(ActionID actionID, SkillTriggerStyle triggerStyle, ulong targetId = 0)
        {
            Assert.IsNotNull(GameDataSource.Instance.GetActionPrototypeByID(actionID),
                $"Action with actionID {actionID} must be contained in the Action prototypes of GameDataSource!");

            if (m_ActionRequestCount < m_ActionRequests.Length)
            {
                m_ActionRequests[m_ActionRequestCount].RequestedActionID = actionID;
                m_ActionRequests[m_ActionRequestCount].TriggerStyle = triggerStyle;
                m_ActionRequests[m_ActionRequestCount].TargetId = targetId;
                m_ActionRequestCount++;
            }
        }
によると、
actionID=GameDataSource.Instance.GeneralTargetActionPrototype.ActionID
triggerStyle=SkillTriggerStyle.MouseClick
が代入されて何かが起こるわけですが、このスクリプト内ではactionIDとやらが
何番なのかよくわかりませんので、
どこかで管理されているんだと思います。(GameDataResource?)
実際は、クリックしたところに移動する動きをします。

・Publish Message On Life Change
    /// <summary>
    /// Server-only component which publishes a message once the LifeState changes.
    /// </summary>
誰かが死んだらその旨をみんなに伝える、みたいなことに使っているっぽい。

・Client Player Avatar
(Scripts\Gameplay\GameplayObjects\Character\ClientPlayerAvatar.cs)
これは非常に重要です。
        public override void OnNetworkSpawn()
        {
            name = "PlayerAvatar" + OwnerClientId;

            if (IsClient && IsOwner)
            {
                LocalClientSpawned?.Invoke(this);
            }

            if (m_PlayerAvatars)
            {
                m_PlayerAvatars.Add(this);
            }
        }
という部分があるのですが、
クライアントがホスト(サーバー)に接続するとPlayer Prefabが生成され
OnNetworkSpawnが実行されます。参考:Colorful Palette様
// ホスト(サーバー)に接続するとPrefabのオブジェクトが生成され、 // ホスト(サーバー)側とクライアント側の両方でOnNetworkSpawnが実行される
ということらしいです。
実際、Assets/Prefabs/Character/PlayerAvatarというprefabが用意されていて、
こいつに上記のPlayerAvatar0と全く同じcomponentsがアタッチされています。
このことから、このスクリプトがアタッチされたprefab(PlayerAvatar)は
PlayerAvatar0という名前を持って、オブジェクト化されるようです。
というわけで、とにかく上記のコンポーネントを持った
prefabを作っておけば、プレイヤーキャラとして使えそうです。
ただ、3Dキャラクターとしての要素は排除したいので、
改変するべき箇所は多いかもしれません。

・Client Avatar Guid Handler
(Scripts\Gameplay\GameplayObjects\Character\ClientAvatarGuidHandler.cs)
    /// <summary>
    /// Client-side component that awaits a state change on an avatar's Guid, and fetches matching Avatar from the
    /// AvatarRegistry, if possible. Once fetched, the Graphics GameObject is spawned.
    /// </summary>
1つ上のClient Player Avatarにてオブジェクト化したPlayerAvatarの
グラフィック関連のスクリプトのようです。
実際、PlayerAvatar prefabの下の階層に
playerGraphicという何かが付いているので、
これをインスタンス化しているのでしょうか?
この辺りはよくわかっていないのでまた後で。

他にも、
・PhysicsWrapper
 (Scripts\Gameplay\GameplayObjects\Character\PhysicsWrapper.cs)
 衝突に関するスクリプト。ネットワーク関係の関数含む。
 他プレイヤーとすり抜けないようにする場合はこれらも流用すればいいですね。
・ClientClickFeedback(Scripts\Gameplay\UI\ClientClickFeedback.cs)
 クリックしたときの動作に関するもの。ネットワーク関係の関数含む。
などもアタッチされていました。

結局、基本的には
2Dのprefabに上記のスクリプトを全部アタッチすればOKな気がします。
ただし、3Dアニメーション関連はアタッチすると参照先が不明だったりして
まずいことになりそうなので、適宜削る必要がありますね。
そのあたりの調整に時間がかかりそう。

【BossRoomシーン】

ちなみに、MainMenuからCharacterSelectシーンに移り、

部屋に居るプレイヤーたちがキャラクター選択を完了させると

BossRoomシーンに移ります。その部分は

Assets\Scripts\Gameplay\GameState\ServerCharSelectState.csの

        IEnumerator WaitToEndLobby()
        {
            yield return new WaitForSeconds(3);
            SceneLoaderWrapper.Instance.LoadScene("BossRoom", useNetworkSceneManager: true);
        }

が呼ばれるところのようです。

BossRoomシーンをロードすると、



の4つのシーン?がロードされます。
実際にゲームを起動してBossRoomシーンまでいったヒエラルキータブと比較すると、

という感じです。DungeonBossRoomとDungeonTransitionがなくなっていますが、
たぶん他の部屋なので本質的に問題はなさそうです。
そしてDontDestroyOnLoadに色々入っていますが、
これがCharacterSelectシーンでのヒエラルキータブと比較してみると、

DontDestroyOnLoad的には全部一緒のように見えます。
なので、CharacterSelectシーンになくてBossRoomシーンにある
ネットワーク関連のオブジェクトをCharacterSelectシーンに追加すれば、
同期されたキャラクターを置けるかもしれません。
やってみましょう。

「CharacterSelectシーンになくてBossRoomシーンにある
ネットワーク関連のオブジェクト」としては、
・NetworkObjectPool
だけのように見えます。
一応コピーして置いてみましたが、特に何の効果があるのかわかりませんでした。

【2Dキャラクターの同期】
PlayerAvatar prefabをコピーして、
・上記のPlayerController.csをアタッチ(上下左右に動く)
・Sprite Rendererでサンタクロースの画像をアタッチ
・Rigidbody, Nav Mesh Agent, Capsule Colliderはオフにする
・子オブジェクトのPlayerGraphicsをオフにする(オンのままだとすごい勢いでエラーが出る)
と、いくつかのエラーが出ますが、
2窓で起動してみると、以下のような挙動になりました。
◎ Character Selectシーンに入ると、ホストはサンタが生成される。(期待通り)
× クライアント(Player 2)にはサンタ生成されず。
△ ホストのサンタの動きは横方向にはPlayer 2に同期されるが、
  縦方向に動いても同期されず。
こんな感じの動作でした。
縦方向の同期はすぐなんとかなるんじゃないかと思いますが、
クライアントに生成されない件はすぐにはわかりませんね。










この時点で、

ロビーから入ったらみんなが動ける

【待機室(ホスト)のパラメータ】








コメント

このブログの人気の投稿

写真・イラストからドット絵を作る方法

Boss Room: a Co-op, Multiplayer RPG Sampleを触ってみる

Unity, 3D↔2Dの変更