シンプルなUnityネットワーク同期エンジンを実現

6693 ワード

シンプルなUnityネットワーク同期エンジンNetgoを実現
現在、GOLANGは特にネットワークプログラミングの面で大きなトレンドを持っています.c/c++と比較すると、GCは一部の機器性能を占有しているが、エラー確率が小さく、開発効率が大幅に向上し、そのオリジナルサポートのコラボレーションを応用することで、高同時性のサービスエンドプログラムを開発することが容易になる.筆者はVR業界に2年余り接触し、いくつかのビジネスunityネットワークエンジンに接触し、いつも使っているものが時代遅れだと感じ、自分で簡単なエンジンを書いた.現在実装されている基本的な機能:
  • は部屋のコンセプトをサポートしています.
  • は、フレーム同期およびRPCを含む柔軟なデータ同期方式をサポートする.
  • は、カスタムイベントの送信をサポートします.

  • 簡単なdemoも実現しましたが、同期効果は下図を参照してください.後で詳しく説明します.
    プロジェクトのアドレス:https://github.com/harlanc/netgo-unity-client次は簡単なプロジェクトの複盤です.
    データ通信フォーマット
    データ通信フォーマットの定義は、プロジェクト全体の基盤です.ここのクライアントとサービス側は、プラットフォーム間、言語間通信です.そのため、言語に関係なく、プラットフォームに関係なく、簡単で使いやすく、効率的でトラフィックを費やさないデータフォーマットを定義します.ここではGoogleのProtobufを選び、この投稿を参考に詳しく紹介します.
    ProtobufのC#コードライブラリには2つの選択肢があります.1つはprotobuf-net、1つはprotobuf-csharp-portです.前者のインタフェースの書き方はC#文法の規範に合っており、より快適に見えます.プラットフォームをまたぐ必要がある場合は、後者を使うことをお勧めします.異なる言語のインタフェースが似ているので、開発が容易になります.原作者の返事を見てみましょう.
    protoファイルの定義
    protobufをどのように使用するかは、まずprotoファイルを書き、独自の構造化データを定義します.netgoでは、netgoで定義されたメッセージ・ボディの一部を次に示します.
    enum CacheOptions{
    
    AddToRoomCache = 0;
    RemoveFromRoomCache = 1;
    }
    
    message NGVector3{
    
        float x = 1;
        float y = 2;
        float z = 3;
    }
    
    message NGQuaternion{
    
        float x = 1;
        float y = 2;
        float z = 3;
        float w = 4;
    }
    
    message NGColor{
    
        float r = 1;
        float g = 2;
        float b = 3;
        float a = 4;
    }

    参照を完全に定義します.
    c#とgolang APIインタフェースファイルの生成
    ネーミングスペースを更新したら、次のコマンドを実行してAPIファイルを生成します.
  • golang protoc --go_out=. *.proto
  • c# protoc --csharp_out=. *.proto

  • サービス側ネットワークモデル
    Unityネットワーク同期エンジンの実装には、サービス側とクライアントの2つの部分が含まれます.NegoはUnityネットワーク同期エンジンのサービス端であり、golangを用いて実現され、その原生的な協程を十分に利用して高同時性を実現している.そのネットワークモデルはgotcpに基づいて実現される.
    上図を参照すると、netgoはsocketリンクごとに1つのコヒーレンスを確立し、1つのsocketコヒーレンス内部に3つのコヒーレンスを確立します.
  • ReadLoopは、ネットワーク側からデータを読み出し、チャネルに格納するために使用される.
  • HandleLoopは、アプリケーション層のデータを解析し、対応する処理を完了し、処理後のデータをChannelを介してWriteLoopに送信するために使用される.
  • WriteLoopは、処理結果forwardを他のクライアントまたはresponseに本クライアントに渡す責任を負う.

  • 参照コード:
    func (c *Conn) Do() {
        if !c.srv.callback.OnConnect(c) {
        return
    }
    
    asyncDo(c.handleLoop, c.srv.waitGroup)
    asyncDo(c.readLoop, c.srv.waitGroup)
    asyncDo(c.writeLoop, c.srv.waitGroup)
    }

    クライアントコード構造
    APIを書くのは基本的にユーザー向けのプログラミングで、筆者は、はっきりしたコード構造、良い命名方式は大部分の注釈を省くことができて、コードの書く乱れは注釈によって救うことしかできなくて、コード構造は下図を見ます:
    ネーミングスペースによって、Library、ネットワーク層とアプリケーション層に分けられる(後でユーザインタフェース層が分けられる).
    関連概念
    データ同期
    ここでの同期とは、1つの部屋のデータの同期であり、1つの部屋にはネットワーク上の複数の端末ユーザーが存在し、各Clientは部屋内の他の人のデータをローカルでCloneし、データの同期とはあなた自身のデータを他のCientの自分のCloneに同期することであり、送信範囲は他のユーザーが受信する.
    データの同期は次の2つに分けられます.
  • View Sync

  • ViewSyncはミリ秒レベルのデータ同期です.仮想キャラクタアクションの同期に使用できます.
  • RPC

  • 同期はユーザーによって手動でトリガーされます.交換などの同期に使用できます.
    Custom Event
    Custom Eventは、同期メッセージを他のすべてのClientエンティティに送信するのではなく、1つまたは複数の指定されたClientに送信します.
    インタフェースの紹介
    ルーム関連インタフェース
    リクエストインタフェース
       //        
       public static void JoinOrCreateRoom(string roomid,uint maxnumber)
       //    
       public static void CreateRoom(string roomid, uint maxnumber)
       //    
       public static void JoinRoom(string roomid)
       //    
       public static void LeaveRoom()

    コールバックインタフェース
       //      
        void OnGreatedRoom();
        //      
        void OnGreateRoomFailed(string errmsg);
        //      
        void OnJoinedRoom();
        //      
        void OnJoinRoomFailed();
        //      
        void OnLeftRoom();

    Player関連インタフェース
       //       
       public static void Instantiate(string prefabname, Vector3 position, Quaternion rotation, uint[] viewids)
      //          
       void OnOtherPlayerEnteredRoom(NGPlayer player);
       //         
       void OnOtherPlayerLeftRoom(NGPlayer player);

    CustomEventインタフェース
    リクエストインタフェース
        //    
        public static void SendCustomEvent(uint eventid, uint[] targetpeerids, NGAny[] customdata)
        

    コールバックインタフェース
        //    
        void OnCustomEvent(uint eventID, NGAny[] data);

    View Sync
    ビューの同期には、コンポーネントスクリプトを独自に実装し、シーケンス化逆シーケンス化インタフェースを実装し、物体にマウントする必要があります.
    public interface INGSerialize
    {
        void SerializeViewComponent(NGViewStream stream);
        void DeserializeViewComponent(NGViewStream stream);
    }
    
    public class CubeViewComponent : NGIncomingEvent, INGSerialize
    {
        public void SerializeViewComponent(NGViewStream stream)
        {
            stream.Send(this.transform.position);
            stream.Send(this.transform.rotation);
        }
        public void DeserializeViewComponent(NGViewStream stream)
        {
            mCorrentPosition = (NGVector3)stream.Receive();
            mCorrentRotation = (NGQuaternion)stream.Receive();
        }
    }

    Cloneエンティティは、データの逆シーケンス化を受けた後、Updateでリアルタイムに更新すればよい.
    void Update()
    {
        if (!view.IsMine)
        {
            transform.position = mCorrentPosition;//Vector3.Lerp(transform.position, mCorrentPosition, Time.deltaTime * 5);
            transform.rotation = mCorrentRotation;//Quaternion.Lerp(transform.rotation, mCorrentRotation, Time.deltaTime * 5);
        }
    }

    RPC
    RPCを使用するには、ビュースクリプトにRPC関数を書く必要があります.
    [NGRPCMethod]
    public void OnColor(NGAny[] c)
    {
        mMat.color = c[0].NgColor;
    }

    次のインタフェースを呼び出して、RPC呼び出しを他のCloneエンティティに送信します.
    public static void SendRPC(uint viewID, string methodname, RPCTarget target, params NGAny[] parameters)

    RPC、View Sync、およびCustom Eventの詳細な使用方法については、ソースコードを参照してください.
    デモ
    サービス・エンドの導入
    Cloneコード
    git clone https://github.com/netgo-framework/netgo.git

    インストール依存
    go get -d ./...

    リスニングIPの更新
    mainを開きます.go
    tcpAddr, err := net.ResolveTCPAddr("tcp", "192.168.0.104:8686")

    サービスの開始
     go run main.go

    クライアントコンパイルインストール
    クライアントはwindows/MacOS/Andorid/IOSマルチプラットフォームをサポートします.以下、AndroidとMacOSでテストします.
    IPとポートの構成
    Androidプラットフォームの切り替え
    コンパイル生成APK
    APKインストール後の初期化画面は以下の通りです.
    機能テスト
    2つのClientが同じ部屋に入ると、各Clientは2つのCubeをインスタンス化し、1つはネイティブエンティティ(Mine Cube)、1つは相手エンティティ(Clone Cube)となる.
    View SYnc
    ボタンMoveをクリックすると、ビュー同期でpostionとrotationの同期が行われます.つまり、文章が始まったばかりの動図の展示の様子です.
    RPC
    Mine Cubeをクリックすると、Cubeの色が変化し、他のマシンに同期します.ここでの色同期はRPCで実現されます.
    Custom Event
    Clone Cubeをクリックすると、相手エンティティにメッセージが送信され、効果は相手のMine Cube Scaleが増加します.
    Road Map
    次に、最適化が必要な機能が追加されるか、または追加されることを考慮します.
  • ホール機能
  • をサポート
  • は負荷等化
  • をサポートする.
  • UDP等のネットワーク伝送プロトコルをサポートする
  • を追加する.
  • jsonなどの複数種類のデータ符号化フォーマットをサポートする
  • を追加する.
  • View Syncデータ転送最適化
  • ルーム間Custom Event
  • 対応
  • .....