UnityでUDP通信を使う！リアルタイムオンラインゲーム向けスクリプト

# 編集

このままだとNAT越えできないのでSTUNクライアント機能を記事に含めることを検討しています。


# 【注意！】対象読者について

残念ながらこの記事は**UnityでC#プログラミングがある程度できる人**を想定しています。

初心者の方でも実行できるような形で配布していますが、活用する面で難しい部分があるかもしれません。

# この記事でできるようになること

### リアルタイムオンラインゲームが作れます！！

ただしあなたの努力次第で...笑

この記事はあくまで**プレイヤーオブジェクトの同期**のみで「その他のオブジェクト、ゲームロジックの同期」には対応していません。

それはオンラインゲームによってはFPSから横スクロールアクションなど、**同期するもの/しないものが変わってくる**からです。
同じ理由でプレイヤーの移動スクリプトは含まれていません。
ただしプレイヤー同期は含まれているので、それを参考に追加、削除が可能です。

これがUDPをわざわざ使う醍醐味のようなもので、これで「しんどいかも」と思ったら残念ながら**Photonをはじめとする外部サービスを強くおススメします**。これ以降は「俺はUDPなんや！ごちゃごちゃうるせえから次いけ！」っていう人中心に読んでもらいたいです。

なのでこの記事を読むと具体的には「**プレイヤー2人がわちゃわちゃしているシーン**」が作れます。

もしすでにプレイヤー移動の基盤がしっかりしている人であれば、一気にオンラインゲーム実現が近づくと思います。

# 手順

### スクリプト

まず**スクリプトは以下で公開**しています。これを全コピーしましょう。

https://github.com/AtomicHeadd/UnityScripts/blob/main/Multiplayer.cs

### 便利なエディター拡張

外部の方のエディター拡張で申し訳ないのですが、オンラインゲーム開発には「**ParrelSync**」がとても便利です。
エディターを２つ起動はできないので、今までデバッグを「ビルドしたゲーム」と「エディター」で行っていたのですが、**ParrelSync**を使うとそのような手間なしにエディターを複製することが可能です。

以下のダウンロードページから**Latest**が付いている「ParrelSync-1.?.1.unitypackage」をダウンロードし、追加したいプロジェクトを開いた状態でダブルクリックしてください。

[ParrelSync 紹介フォーラム](https://forum.unity.com/threads/editor-extension-parrelsync-test-multiplayer-without-building-uecho-replacement.964388/)
[github](https://github.com/VeriorPies/ParrelSync)
[ダウンロードページ](https://github.com/VeriorPies/ParrelSync/releases)

ParrelSyncがimportできたら上にParrelSyncというボタンができるのでそこから
[Clones Manager]→[Add new clone]→[Open in New Editor]と進みます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/496596/e2269f52-0062-e08f-0d77-5dfe81656c34.png)



ParrelSyncをダウンロードしたら、Emptyオブジェクトにダウンロードした**Multiplayer.cs**をアタッチでします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/496596/8dbcf601-1aec-9520-d24b-a23b24e70e21.png)


ここで各プロパティの説明をします。

**MyPort**はクライアントのポート番号です。
**OpponentPort**は相手のポート番号です。

例えばParrelSyncでエディターを複製した後に、オリジナルのポートを3000、クローン側のポートを3001とすると、
**オリジナル**のエディターでは**myPortに3000**を、**opponentPortに3001**を、
**クローン側**のエディターでは**myPortに3001**を、**opponentPortに3000**を入力します。

この作業はStart内にClonesManager.IsClone()で条件分岐することで自動化ができます。

**EnableSmoothing**は受信間を線形補完するかです。
例えば1秒に4回送受信する場合、1/4秒の間は相手が完全に停止していることになります。そこで、1/4秒の間も敵が移動するように
敵の位置を更新の間で線形補完を行います。

**Player**は同期するプレイヤーのオブジェクトです。
**OtherPlayerObjects**は同期する相手側のオブジェクトです。

# 実行方法

両方のエディターで再生ボタンを押したのちに、Multiplayerコンポーネントの**右上のContextMenu**から**Register**を押します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/496596/9194b5eb-4f1c-f88e-3fbe-4d9a66c159b1.png)

上の正しい設定をした後に、両方のエディターで**Register**を押すと同期が開始します。

![ezgif.com-gif-maker (1).gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/496596/942dd041-a7aa-0a9f-4914-568272d1c30c.gif)

gif動画でフレームが低くなって分かりにくいですが、左上がEnableSmoothingあり、右下がスムージングなしです。

ここで注意するのは赤が各クライアントにおけるプレイヤーオブジェクトであり、それぞれの視点ではそれぞれがプレイヤーなので全体では一致しません。

### うまくいかない場合

- Multiplayerスクリプトをアタッチしましたか？
- 動画では**シーンビューでプレイヤーオブジェクトを無理やり動かしています**。当然ながら相手のオブジェクトは動かすことができません。
- Portの設定は**2つのエディターで交互になるように**割り振ってください。詳しくはプロパティで説明しています。
- 必ず**再生ボタンを押した後に**コンテキストメニューから**Register**を選択してください。

# 終わりに

さて、ここで実装したのは**UDP**を使った**P2P**の**二人のみ**の場合の実装です。

また位置、回転以外の同期には対応していません。
それ以外の部分は冒頭に述べたように読者の皆様に作ってもらいます。

オンラインゲームを実装するために、Webアプリを使ったロビーサーバーでIPアドレス、ポートを共有できればこの方法で通信ができます。

サーバー・クライアント方式で実装するためにはさらに高度な制御ロジックが必要です。

この記事がリアルタイムオンラインゲーム実装に役立つことを期待しています。

### このスクリプトを理解するには

- スレッドを使っていますが、変更は必要ありません
- メッセージ種類の判別にEnumを使っています
- P2Pの実装のためにNAT越え(NATパンチスルー)を行っています
- BitConverterを使ってfloat⇔byte[]の変換を行っています。