【Unityプラグイン】マルチプレイヤーオンラインゲームの実装 - Mirrorプラグインの使い方入門

序文

ついに登場しました。これまで多くの人が、マルチプレイヤー ゲームの実装方法を知りたいと私にプライベート メッセージを送ってきました。それがここにあります。

実は、私は長い間 Mirror プラグインに注目していましたが、最近になってようやくそれを整理する時間ができました。

Mirror は、シンプルで効率的なオープン ソースの Unity マルチプレイヤー ゲーム ネットワーク フレームワークであり、Unity ストアで無料で入手できます。

公式 API アドレス: https://mirror-networking.gitbook.io/docs

ミラープラグインのインポート

https://assetstore.unity.com/packages/tools/network/mirror-129321
ここに画像の説明を挿入します

簡単な紹介

1.RPC呼び出し

関数の変更に 3 つのキーワードが使用されている場合、関数はローカルではなくリモートで呼び出されます。[コマンド]、[ClientRpc]、[TargetRpc]。

  • コマンドはクライアントで呼び出され、サーバーで実行されます。メソッド名は「Cmd」で始まります。
  • ClientRpc はサーバー上で呼び出され、サーバーに接続されているすべてのクライアント上で実行されます。メソッド名は「Rpc」で始まります。
  • TargetRpc はサーバー上で呼び出され、サーバーに接続されている指定されたクライアント上で実行されます。このメソッドには、どのクライアントで実行されるかを決定するために使用される NetworkConnection の仮パラメータが少なくとも 1 つあり、メソッド名は「Target」で始まります。
  • ServerCallback: サーバーによってのみ呼び出され、サーバー上で実行できます。そしてメソッド名は「Server」で始まります。使い方はTargetRpcと似ています

使用

using Mirror;
using UnityEngine;

public class MyNetworkBehaviour : NetworkBehaviour
{
    
    
    [Command]
    void CmdFire()
    {
    
    
        // 在服务器上调用
    }

    [ClientRpc]
    void RpcGetHit()
    {
    
    
        // 在客户端上调用
    }
}

たとえば、特定のクライアント キャラクターが追加のポイントを獲得します。

# TargetRpc
NetworkIdentity netIdentity = GetComponent<NetworkIdentity();
TargetShowMessage(netIdentity.connectionToClient,1);
[TargetRpc]
private void TargetShowMessage(NetworkConnection target, int count)
{
    
    
    sumCount += count;//加分
}

# ServerCallback
[ServerCallback]
private void ServerPlayerReady(NetworkConnection connection)
{
    
    
    // 将指定客户端标记为已准备状态的逻辑
}

2. エラーに関する注意事項

  • 1. コードにリンクされているシーン内のすべてのオブジェクトは、デフォルトでネットワーク ID とともに追加されますが、ネットワーク マネージャー コンポーネントとネットワーク ID コンポーネントが同じオブジェクトに配置されている場合、エラーが報告されます。

  • 2. オブジェクト内にネットワーク マネージャー コンポーネントはあるが Kcp トランスポート コンポーネントがない場合、エラーが報告されます。

  • 3. シーン内に複数のネットワーク マネージャー コンポーネントがある場合、エラーが報告されます。

  • 4. キャラクター プレハブをプレーヤー プレハブ列にロードできない場合、ネットワーク ID コンポーネントでハングしない可能性があります。

  • 5. ロール コードには、if (!isLocalPlayer) return; それ以外の場合を含める必要があります。あなたはその結果を知っています。

基本的な使い方

1.シーンネットワークマネージャーを作成する

ネットワーク マネージャーは、マルチプレイヤー ゲームの中核となる制御コンポーネントです。ネットワーク マネージャーは、マルチプレイヤー ゲームの中核となる制御コンポーネントです。

開始シーンに空のゲーム オブジェクトを作成し、新しく作成したネットワーク マネージャー コンポーネント (networkManager、Kcp Transport、networkManagerHUD コンポーネント) を追加します。
ここに画像の説明を挿入します
kcp トランスポート コンポーネントは、networkManager のトランスポートにマウントされ
ここに画像の説明を挿入します
、シーン シーンを構成します。オフラインとオンラインは、オフライン インターフェイスとゲーム インターフェイスです。たとえば、新しいオフライン シーンを作成し、その中にネットワーク マネージャーのネットワーク マネージャー HUD を配置し、新しいオンライン シーン。オフラインを一番上にして、ビルド設定のビルドのシーン バーにすべて登録 (ドラッグ イン)。次に、オフライン シーンに入り、実行し、ホストをクリックすると、オンライン シーンに入ります。

2. プレーヤーを作成する

プレーヤー オブジェクト Player を作成し、ネットワーク同期用の一意の識別子として networkIdentity をプレーヤーに追加します。一般に、除了ネットワーク マネージャー コンポーネントを含むゲーム内のすべてのオブジェクト (これからハッチングされるオブジェクトも含めて) をこのコンポーネント内でハングする必要があります。これには 2 つのオプションしかありません。1 つのチェック ボックスは [サーバーのみ] で、これはサーバーのみが操作できることを意味します。必要に応じてチェックできます。2 つ目は表示されており、Default、Force Hidden、ForceShown の 3 つのオプションがありますが、個人的には役に立たないと感じているので、誰もがデフォルトで構いません。
ここに画像の説明を挿入します
networkIdentity がマウントされている場合にのみ、ネットワーク ハブはこのオブジェクトを認識して同期できます。次に、预制体プレーヤーをプレーヤーとして保存し、シーン内で削除し、プレハブをネットワーク ハブ (networkManager) のプレーヤー プレハブ スロットにドラッグします。将来の世代は、ネットワーク ハブに完全に依存して、接続後に自動的に生成されます。ザ・ホスト。

Auto Create Player: デフォルトでチェックされており、チェックされている場合、サーバーに接続するときに上記の「プレーヤー プレハブ」が自動的に生成されます。
ここに画像の説明を挿入します

注: キャラクター プレハブをプレーヤー プレハブ列にロードできない場合、ネットワーク ID コンポーネントでハングしない可能性があります。

プレーヤーの Network Transform を追加し、ネットワーク内のネットワーク化されたゲーム オブジェクトの位置、回転、スケーリングを同期し、networkTransport の Client Authority 属性を確認します。

Mirror は現在 2 種類のネットワーク変換を提供しています。
ここに画像の説明を挿入します

Reliable:低带宽,与Rpcs/Cmds/等相同的延迟。

Unreliable:高带宽,极低延迟

使用Reliable,除非需要超低延迟。

ここに画像の説明を挿入します
注: プレーヤー本体のスケールを使用して、後で反転します。ここでは、本体にネットワーク変換コードも追加します。同期スケールをオンにすることを忘れないでください。
ここに画像の説明を挿入します

3. プレイヤーの初期スポーン位置を追加します

プレーヤーの初期スポーン位置としていくつかの空のオブジェクトを作成し、ネットワーク開始位置スクリプトを追加して、オブジェクトを適切な場所にドラッグします。
ここに画像の説明を挿入します
そして、NetworkManager でランダム (Random) またはラウンドロビン (Round Robin) のスポーンポイント選択方法を選択します。

1.Random:生成为随机(可能相同的生成位置将被两个或更多玩家使用)

2.Round Robin:循环(使用每个可用位置,直到客户端数超过生成点数)。

ここに画像の説明を挿入します
効果
ここに画像の説明を挿入します

4. プレーヤーのコントロール

ネットワーク同期については次の点に注意してください。

1. ネットワーク関数を使用する必要があるスクリプトは、対応する API を使用するために Mirror を追加し、MonoBehaviour の代わりに NetworkBehaviour を継承する必要があります。

2. プレーヤー入力に関しては、まず isLocalPlayer を判断し、islocalplayer を使用して現在のオブジェクトの権限があるかどうかを判断する必要があります。

ゲーム オブジェクトを制御するには、NetworkBehaviour を継承する単純なキャラクター制御スクリプトを PlayerControl.cs に追加します。

using UnityEngine;
using Mirror;
public class PlayerControl : NetworkBehaviour //MonoBehaviour --> NetworkBehaviour
{
    
    
    private Rigidbody2D rb; // 刚体组件
    void Start()
    {
    
    
        rb = GetComponent<Rigidbody2D>(); // 获取刚体组件
    }

    //速度:每秒移动5个单位长度
    public float moveSpeed = 5;

    void Update()
    {
    
    
        if (!isLocalPlayer) return; //不应操作非本地玩家
        Move();
    }

    void Move()
    {
    
    
        //通过键盘获取水平轴的值,范围在-1到1
        float horizontal = Input.GetAxisRaw("Horizontal");
        rb.velocity = new Vector2(horizontal * moveSpeed, rb.velocity.y); // 设置刚体速度
        if (horizontal != 0)
        {
    
    
            transform.GetChild(0).localScale = new Vector3(-horizontal, 1, 1); // 翻转角色
        }
    } 
}

効果
ここに画像の説明を挿入します

5. カメラを同期する

各クライアントで独立して生成されたオブジェクト (ここでは、各プレーヤーのカメラを例として取り上げます) の場合、開始メソッドを OnStartLocalPlayer() に変更する必要があります。これにより、複数のクライアントのカメラが同じカメラに変更されるのを防ぐことができます。

OnStartLocalPlayer: クライアントでのみ実行され、スクリプトが配置されているオブジェクトがプレイヤー キャラクターの場合に呼び出され、追跡カメラの設定、キャラクターの初期化などに使用されます。

public override void OnStartLocalPlayer()
{
    
    
	rb = GetComponent<Rigidbody2D>(); // 获取刚体组件
	
    //摄像机与角色绑定
    Camera.main.transform.SetParent(transform);
    Camera.main.transform.localPosition = new Vector3(0, 0, Camera.main.transform.position.z);
 }   

効果
ここに画像の説明を挿入します

6. 異なるキャラクターの名前と色の変更を同期する

同期変数は、同期変数でマークする必要があります[SyncVar(hook=nameof(FunctionExecOnClient))]。同期変数が変更されると、後続の FunctionExecOnClient メソッドが呼び出されます。

サーバー シーンの SyncVar の値が変更されると、他のすべてのクライアントと同期されます。

同期変数の変更には[Command]タグ(メソッドのマーク、メソッド名が で始まるCmd)を使用します。

using TMPro;

public TMP_Text nameText;

//需要把name和颜色同步给其他玩家,添加同步变量的标记[SyncVar(hook=nameof(FunctionExecOnClient))]
[SyncVar(hook = nameof(OnPlayerNameChanged))]
public string playerName;
[SyncVar(hook = nameof(OnPlayerColorChanged))]
private Color playerColor;

//申明OnPlayerNameChanged和OnPlayerColorChanged这两个方法
//第一个变量(oldstr)是同步变量修改前的值,第二个(newstr)是同步变量修改后的值
private void OnPlayerNameChanged(string oldstr, string newstr)
{
    
    
    nameText.text = newstr;
}
private void OnPlayerColorChanged(Color oldCor, Color newCor)
{
    
    
    nameText.color = newCor;
}

void Update()
{
    
    
   if (!isLocalPlayer) return; //不应操作非本地玩家
   
   Move();

   if (Input.GetKeyDown(KeyCode.Space))
   {
    
    
       //随机生成颜色和名字
       ChangedColorAndName();
   }
}
public override void OnStartLocalPlayer()
{
    
    
    //。。。

    //开始就随机生成颜色和名字
    ChangedColorAndName();
}

//player 的随机名称和颜色
private void ChangedColorAndName()
{
    
    
    //随机名称和颜色
    var tempName = $"Player{
      
      Random.Range(1, 999)}";
    var tempColor = new Color(Random.Range(0, 1f), Random.Range(0, 1f), Random.Range(0, 1f), 1);

    //同步变量进行修改
    CmdSetupPlayer(tempName, tempColor);
}

//对于同步变量的修改,使用[Command]标记(针对方法的标记,方法名以Cmd开头)
//通过这个方法同时对name和颜色进行修改
[Command]
private void CmdSetupPlayer(string name, Color color)
{
    
    
    playerName = name;
    playerColor = color;
}

効果
ここに画像の説明を挿入します

7. 同期アニメーション

Network Animator コンポーネントをマウントする
ここに画像の説明を挿入します

private Animator anim; // 动画组件
anim = gameObject.GetComponentInChildren<Animator>(); // 获取动画组件

public override void OnStartLocalPlayer()
{
    
    
   //。。。
   
   anim = gameObject.GetComponentInChildren<Animator>(); // 获取动画组件
}

void Update()
{
    
    
    if (!isLocalPlayer) return; //不应操作非本地玩家

    //。。。
    
    //攻击动画控制
    if (Input.GetMouseButtonDown(0))
    {
    
    
        anim.SetTrigger("isAttack");
        anim.SetBool("isIdle", false);
    }else{
    
    
        anim.SetBool("isIdle", true);
    }
}

ここに画像の説明を挿入します

8. シンクロ弾丸

爆弾は通常の爆弾の組立式本体です
ここに画像の説明を挿入します

方法 1

[ClientRpc] キーワードでは、サーバーは接続されているすべてのクライアントに同期指示を送信できます。メソッド名も Rpc で始まる必要があります。

public GameObject bomb;//炸弹预制体

void Update()
{
    
    
    if (!isLocalPlayer) return; //不应操作非本地玩家

    //。。。
    
    //生成炸弹
    if (Input.GetMouseButtonDown(1))
    {
    
    
        Cmdshoot();
    }
}

[Command]
private void Cmdshoot()
{
    
    
    RpcWeaponFire();
}

[ClientRpc]
private void RpcWeaponFire()
{
    
    
    GameObject b = Instantiate(bomb, transform.position, Quaternion.identity);
    b.transform.Translate(1, 0, 0);//防止子弹撞到角色
    b.GetComponent<Rigidbody2D>().AddForce(Vector2.up * 500f);
}

効果
ここに画像の説明を挿入します

方法 2

NetworkManager一番下にリスト(登録済みスポーン可能プレハブ)があり、敵や弾などゲーム内で孵化させる必要があるオブジェクトを配置するために使用されますので、それらをドラッグしてください
ここに画像の説明を挿入します
。そうでないと、ドラッグすることができません。

public GameObject bomb;//炸弹预制体

void Update()
{
    
    
    if (!isLocalPlayer) return; //不应操作非本地玩家

    //。。。
    
    //生成炸弹
    if (Input.GetMouseButtonDown(1))
    {
    
    
        Cmdshoot();
    }
}

[Command]
private void Cmdshoot()
{
    
    
    GameObject b = Instantiate(bomb, transform.position, Quaternion.identity);
    b.transform.Translate(1, 0, 0);//防止子弹撞到角色
    b.GetComponent<Rigidbody2D>().AddForce(Vector2.up * 500f);
    Destroy(b, 2.0f);//两秒后删除
    NetworkServer.Spawn(b);//服务器孵化,同步客户端
}

効果の
ここに画像の説明を挿入します
問題

クライアントによって爆弾に適用された AddForce フォースが効果がないことがわかります。その理由は、同期された剛体コンポーネントを追加せず、ネットワーク Rigidbody 2D コンポーネントを爆弾に追加したためです。
ここに画像の説明を挿入します

効果
ここに画像の説明を挿入します

9.チャット機能

ChatControllerスクリプトを追加しました

using UnityEngine;
using Mirror;
using TMPro;
using UnityEngine.UI;

public class ChatController : NetworkBehaviour
{
    
    
    public TMP_InputField chatText;//输入框
    public Button chatBtn;//发送按钮
    public GameObject chatInfo;//聊天框内容预制体
    public GameObject chatFrame;//聊天框
    public PlayerController playerController ;

    [SyncVar(hook = nameof(OnChatTextStringChanged))]
    public string chatTextString;

    private void OnChatTextStringChanged(string oldstr, string newstr)
    {
    
    
    	//添加聊天内容
        GameObject ci = Instantiate(chatInfo);
        ci.GetComponent<TMP_Text>().text = newstr;
        ci.transform.SetParent(chatFrame.transform);
    }

    void Awake()
    {
    
    
        chatBtn.onClick.AddListener(SendBtn);
    }

    public void SendBtn()
    {
    
    
        if (player != null)
        {
    
    
            playerController.CmdSendPLayerMessage(chatText.text);
        }
    }
}

PlayerControllerを変更し、転送キャラクター名を呼び出します

private ChatController chatController;

void Awake()
{
    
    
    chatController = FindObjectOfType<ChatController>();
}

public override void OnStartLocalPlayer()
{
    
    
	//。。。
	
    chatController.playerController = this;
}
[Command]
public void CmdSendPLayerMessage(string message)
{
    
    
    if (chatController != null)
    {
    
    
        chatController.chatTextString = playerName + "说:" + message;
    }
}

UI ページを描画するときは、ネットワーク ID コンポーネントを追加することを忘れずに、
ここに画像の説明を挿入します
ネットワーク ID スクリプトをチャット UI キャンバスにマウントすることを忘れないでください。
ここに画像の説明を挿入します

効果
ここに画像の説明を挿入します

10. 同期シーン切り替え

3 つの新しいシーン NetworkManager オブジェクトを作成し
ここに画像の説明を挿入します
、NetworkManagerHUD の可視性を制御するために SceneController コードを追加します。

using UnityEngine;
using UnityEngine.UI;
using Mirror;
using UnityEngine.SceneManagement;

public class ScenceController : MonoBehaviour
{
    
    
    private void Update()
    {
    
    
            Scene scene = SceneManager.GetActiveScene();
            //控制NetworkManagerHUD的显隐
            if(scene.name == "Main"){
    
    
                GetComponent<NetworkManagerHUD>().enabled = false;
            }else{
    
    
                GetComponent<NetworkManagerHUD>().enabled = true;
            }
    }
    
    //开始游戏,场景切换
    public void ButtonLoadScene()
    {
    
    
        SceneManager.LoadScene("SampleScene1");
    }
}

Main シーンはゲームのスタート ページです。デフォルトでは、ボタンが配置されます。ボタンは ButtonLoadScene メソッドを呼び出します。Network Manager は最初のシーン (および Main シーン) にのみマウントする必要があります。前のコードは可視性を制御しました。エラーが報告されたときに HUD ビューにいない場合、メイン シーンには
ここに画像の説明を挿入します
マウントに対応するシーンが表示されます。
ここに画像の説明を挿入します


ここに画像の説明を挿入します
ここに画像の説明を挿入します
SampleScene1 シーンと SampleScene2 シーンに基本的な違いはありません。前のゲーム ページと同じです。メイン インターフェイスの Main シーンとの競合を防ぐために、元の NetworkManager オブジェクトを削除します。シーンの切り替えを制御する ButtonChangeScene メソッドを追加します。ゲームのSampleScene1シーンとSampleScene2シーンにメソッドが実装されており、シーン切り替えボタン上にあります。

//同步切换场景
public void ButtonChangeScene()
{
    
    
    if (isServer)
    {
    
    
        var scene = SceneManager.GetActiveScene();
        NetworkManager.singleton.ServerChangeScene
        (
            scene.name == "SampleScene1" ? "SampleScene2" : "SampleScene1"
        );
    }
    else
    {
    
    
        Debug.Log("你不是host");
    }
}

効果
ここに画像の説明を挿入します

11. HUD インターフェースを再描画する

NetworkManagerHUD (Network Manager コンポーネントと連携する必要があります)、GUI を自動的に描画します。
ここに画像の説明を挿入します
Host (ホスト): サーバーとクライアントに相当します。

クライアント: サーバーに接続し、サーバーの IP アドレスを続けます。localhost はローカル ポートであり、自分自身で接続するのと同じです。

サーバーのみ: サーバーとしてのみ機能します。

ただし、この UI インターフェイスはあまり見栄えがよくないため、通常はこのコンポーネントを使用せず、独自の GUI を作成します。

シーンに 3 つの新しいボタンを追加します
ここに画像の説明を挿入します

ゲームページにマウントされた MyNetworkManagerHUD コードを追加、サンプルコード

using UnityEngine;
using UnityEngine.UI;
using Mirror;
public class MyNetworkManagerHUD : MonoBehaviour
{
    
    
    private NetworkManager networkManager; // 创建 NetworkManager 对象
    public GameObject btn;
    public GUISkin mySkin;
    private GameObject startHost;//启动网络主机按钮
    private GameObject startClient;//启动网络客户端按钮
    private GameObject stopHost;//停止网络主机或客户端按钮
    void Awake()
    {
    
    
        networkManager = FindObjectOfType<NetworkManager>();
        startHost = GameObject.Find("StartHost");
        startClient = GameObject.Find("StartClient");
        stopHost = GameObject.Find("StopHost");
        startHost.GetComponent<Button>().onClick.AddListener(OnStartHost);
        startClient.GetComponent<Button>().onClick.AddListener(StartClient);
        stopHost.GetComponent<Button>().onClick.AddListener(StopHost);
    }

    private void Update()
    {
    
    
        // GetComponent<NetworkManagerHUD>().enabled = true;

        if (!NetworkClient.isConnected && !NetworkServer.active) // 检查客户端和服务器的连接状态
        {
    
    
            startHost.SetActive(true);
            startClient.SetActive(true);
            stopHost.SetActive(false);
        }
        else
        {
    
    
            startHost.SetActive(false);
            startClient.SetActive(false);
            stopHost.SetActive(true);
        }

    }
     private void OnStartHost()
    {
    
    
        networkManager.StartHost(); // 启动网络主机
    }
    private void StartClient()
    {
    
    
        networkManager.StartClient(); // 启动网络客户端
    }
    private void StopHost()
    {
    
    
        networkManager.StopHost(); // 停止网络主机或客户端
    }
}

もちろん、元の NetworkManagerHUD コンポーネントは役に立たないので削除しても構いませんが、NetworkManagerHUD の表示と非表示を制御する以前のコードも同時に削除することを忘れないでください。

実行結果
ここに画像の説明を挿入します

12. サーバーを見つける

ネットワークディスカバリーHUD+ネットワークディスカバリーコンポーネントを使用して、元のNetworkManagerHUDを置き換えます。

このネットワーク検出コンポーネントは、ネットワーク マネージャーでも使用する必要があります。LAN 内のすべてのサーバーを一覧表示できます。トランスポート列があります。ネットワーク マネージャー コンポーネントと一緒に Kcp トランスポート コンポーネントをそこにドラッグする必要があります。そうしないと実行されません。
ここに画像の説明を挿入します

network DiscoveryHUD は networkmanagerHUD に似ていますが、クライアントが 1 つ少なく、検索サーバーが 1 つ多い点が異なります。

ここに画像の説明を挿入します
この機能は、「サーバーの検索」をクリックすると、LAN 内のすべての競技 (サーバー) がリストされますが、LAN 内のみが表示されます。

実行結果
ここに画像の説明を挿入します

14. キャラクターの死と復活

// 角色是否死亡的标志
[SyncVar(hook = nameof(OnIsDeadChanged))]
 public bool isDead = false;

 // 当角色死亡状态改变时的回调方法
 void OnIsDeadChanged(bool oldValue, bool newValue)
 {
    
    
     if (newValue)
     {
    
    
         // 执行死亡逻辑,例如播放死亡动画、禁用角色控制等
         Debug.Log("Player has died.");
         Destroy(gameObject, 2f); // 延迟2秒后销毁角色对象
     }
 }
void Update()
{
    
    
	if (Input.GetKeyDown(KeyCode.T))
	{
    
    
	     CmdDestroyPlayerServer();
	     
	     // 创建一个新的Camera对象
	     GameObject cameraObject = new GameObject("Main Camera");
	     // 添加Camera组件到对象上
	     Camera cameraComponent = cameraObject.AddComponent<Camera>();
	     // 设置摄像机的位置和旋转
	     cameraComponent.transform.position = new Vector3(0, 0, -10f);
	     cameraComponent.transform.rotation = Quaternion.identity;
	 }
}

[Command]
private void CmdDestroyPlayerServer()
{
    
    
    isDead = true;
}

復活方法(キャラの再生成)は別の場所に書いてください

public class GameManager : MonoBehaviour
{
    
    
    void Update()
    {
    
    
        if (Input.GetKeyDown(KeyCode.R))
        {
    
    
           //让当前客户端复活角色
		    NetworkClient.AddPlayer();
        } 
    }
}

効果
ここに画像の説明を挿入します

13. 自分でネットワーク管理者を立てる

ロールを作成する簡単な関数は次のとおりです。

using Mirror;
using UnityEngine;
using UnityEngine.UI;
 
public class MyNetworkManager : NetworkManager//继承network manager类
{
    
    
    public InputField myname; // 输入玩家名称的InputField

    // 在服务器启动时调用
    public override void OnStartServer()
    {
    
    
        Debug.Log("启动服务器");
        // 启动服务器
        base.OnStartServer();

        // 注册CreateMMOCharacterMessage消息的处理方法
        NetworkServer.RegisterHandler<CreateMMOCharacterMessage>(OnCreateCharacter);
    }
    public override void OnStopServer()
    {
    
    
        Debug.Log("关闭服务器");
        // 关闭服务器
        base.OnStopServer();
    }
    public override void OnServerConnect(NetworkConnectionToClient conn)
    {
    
    
        Debug.Log("处理连接事件");
        // 处理连接事件
        base.OnServerConnect(conn);
    }

    public override void OnServerDisconnect(NetworkConnectionToClient conn)
    {
    
    
        Debug.Log("处理断开事件");
        // 处理断开事件
        base.OnServerDisconnect(conn);
    }

    // CreateMMOCharacterMessage消息结构体
    public struct CreateMMOCharacterMessage : NetworkMessage
    {
    
    
        public string playername; // 玩家名称
    }

    // 在客户端连接到服务器时调用
    public override void OnClientConnect()
    {
    
    
        base.OnClientConnect();

        // 在这里发送消息,或者您想要的任何其他地方
        CreateMMOCharacterMessage characterMessage = new CreateMMOCharacterMessage
        {
    
    
            // playername = myname.text // 设置玩家名称为InputField中的文本
            playername = "测试" // 设置玩家名称为InputField中的文本
        };

        NetworkClient.Send(characterMessage); // 发送消息给服务器
    }

    // 创建角色的方法,在收到CreateMMOCharacterMessage消息时调用
    // 参数conn:与服务器的连接;参数message:接收到的消息
    void OnCreateCharacter(NetworkConnectionToClient conn, CreateMMOCharacterMessage message)
    {
    
    
        //PlayerPrefab是在Network Manager的Inspector中指定的,
        //但您可以在每个比赛中使用不同的预制体,例如:
        GameObject gameobject = Instantiate(playerPrefab); // 实例化玩家预制体

        //根据游戏的需要,应用消息中的数据
        Player player = gameobject.GetComponent<Player>();
        player.playerName = message.playername; // 将玩家名称赋值给Playercode组件中的playername变量

        //将此游戏对象添加为连接上的玩家的控制对象
        NetworkServer.AddPlayerForConnection(conn, gameobject);
    }
}

元の NetworkManager を削除し、作成したコードをマウントし、ロールを自動的に作成するためのチェック ボックスを削除します。そうしないと、最初に 2 人の主人公が作成されます。
ここに画像の説明を挿入します
実行効果は正常です。
ここに画像の説明を挿入します

AOIフォグエフェクトの実装

空間ハッシュ関心管理コンポーネントをネットワーク管理者と同じオブジェクトに配置します。
ここに画像の説明を挿入します

パラメータ:
可視距離、
表示にかかる時間、
3D 2D 切り替え、
デバッグ スライダー

近づいたときのみアイテムを表示
ここに画像の説明を挿入します

ルームオープン機能

実際には、Mirror/Examples/MultipleMatches/Scenes/Main に公式デモがあり、最初にエフェクトを確認することができます。
ここに画像の説明を挿入します

オンラインサーバーへの接続(つづき)

Linuxサーバー

オンライン サーバーの IP とポート番号を変更します。
ここに画像の説明を挿入します
サーバーにアクセスして、対応するポートのセキュリティ グループのホワイトリストを忘れずに追加してください。
ここに画像の説明を挿入します

HUD スクリプト コンポーネントを削除します
ここに画像の説明を挿入します
。サーバーかクライアントかを制御するコードを作成します。
ここに画像の説明を挿入します
最初にサーバーをパッケージ化するため、最初にサーバーを確認してください。
ここに画像の説明を挿入します

次に、Linux サーバー側プログラムをパッケージ化します
ここに画像の説明を挿入します
ps: お持ちでない場合は、最初に Unity モジュールをインストールすることを忘れないでください。
ここに画像の説明を挿入します
すべてのサーバー側プログラムをサーバーにアップロードします。
最初に、実行可能ファイルにアクセス許可を追加し
ここに画像の説明を挿入します
、実行可能プログラムを実行します。
ここに画像の説明を挿入します

その後、クライアントを通常どおりパッケージ化し、サーバーに接続して再生できます。
パッケージ化するときは、AppIsServer のチェック ボックスを削除し、ターゲット プラットフォームを Windows に変更することを忘れないでください。

接続に成功しました
ここに画像の説明を挿入します

ソースコード

整理できたら載せます。

終わり

バラの贈り物、香りを贈ろう!点赞评论和关注記事の内容が参考になった場合は、できるだけ早くフィードバックを受け取ることができるよう、ケチらずにフィードバック支持をいただければ、私が創作を続ける最大のモチベーションとなります。「いいね!」が増えると更新が早くなります!もちろん、記事内で存在错误何かを見つけた場合は更好的解决方法、お気軽にコメントしてプライベートメッセージを送ってください。

はい、私向宇https://xiangyu.blog.csdn.net

小さな会社で黙々と働いていた開発者が、最近興味本位でUnityを独学で勉強し始めました。何か問題が発生した場合は、コメントやプライベート メッセージを送っていただければ幸いです。私が必ずしも問題を知っているわけではないかもしれませんが、すべての関係者からの情報を確認し、最善の提案を提供できるよう努めます。プログラミングを学びたい人が増えますよ、みんなで励まし合いましょう〜
ここに画像の説明を挿入します

おすすめ

転載: blog.csdn.net/qq_36303853/article/details/132789888
おすすめ