Unity上でUDP/IPを用いた通信機能(マルチプレイ)を実装する

こんちゃ!洋梨🍐です。

unityでマルチプレイをはじめとする通信機能を実装したい方、多いのではないでしょうか?

そこでUNET(廃止予定の様ですが…)、Socket.ioなどを使おうと考えた・考えている方も多いと思うのですが私は勉強の為やライセンスの問題、使いやすさの点から1から作ってみたのでそれまでの過程及びソースコードなどを記事にしました。

今回作るもの

今回はUnityの動く端末間で、オブジェクトの位置情報を共有できるようにしていきたいと思います。


PC・スマホ間でステージ内Cubeの位置情報を共有している図

応用次第ではクライアントサーバー形式・p2p形式でのマルチプレイ機能実装など様々なことに使えるのではないでしょうか?

仕様

今回はゲームオブジェクトの位置共有、つまりTransform.positionのVector3 情報を受信及び反映、送信したいとおもいます。

なお、今回行う通信はアクションゲームで使うことを推定し、高速化を図るためUDPで行います。

Tips 「TCPとUDPの違い」
TCP : 安定性は高いが低速。

>ゲームではチャットやマッチメイキングに使われる。
UDP : 不安定だが高速。

>FPSなど速さが大切なゲームの通信で使われている。
(※RUDP : 二つを組み合わせ安定性、高速を兼ねそろえたもの。実装はアプリ側。)

プロジェクト

今回作ったプロジェクト

今回はステージ内に配置したCubeの座標を共有します。椅子とかは距離感をつかみやすい様になんとなくおいた飾りです。Textには現在のCubeの座標を表示しようと思います。

(プロジェクトファイルのダウンロードは下にあります。)

スクリプト

main.cs メイン。主な動作はここに書く。
UdpSystem.cs UDP通信の動作をまとめてあるものです
MyNetwork.cs IPアドレス取得等、ネットワーク関連の基本動作を担当

(ソースファイルのダウンロードは下にあります。)

ポート番号

同じでもいいのですが今回はホスト端末Aには5001、クライアント端末Bには5002としてあります。なお、送信に使うポートは6001としました。

パケット構造

今回は位置情報の共有のみなのでVector3(x,y,z)の情報が入るパケットを作って送ります。パケット情報を管理するクラスは以下の通りです。

DATAクラスはインスタンス生成時の引数(Vector3もしくはbyte配列)に応じてfloat x,y,zの値に変換します。インスタンス生成後、ToVector3(),ToByte()で双方に変換できるようにしています。

Vector3のそれぞれの値(x,y,z)はfloat型なので4byteです。そのためパケットサイズは12Byteになります。パケットの構造は以下の通りです。

今回の送信パケットのデーター構造

※今回は受信側・送信側と1対1での通信で一つのオブジェクトの位置座標共有しかしていないためパケットに識別番号や送信元IPなど情報を含んでいません。なお、TCPなら送信元IP等がなくてもコネクションでわかりますがUDPはそうもいかないため含まないと判別できません。

ソースコード

※なお、今回紹介するソースコードは以前私が作ったマルチプレイ仕様ゲームの一部を切り取り、説明用に編集したものになります。

Main.cs

UdpSystem.cs

ライセンスなんて書いちゃってますけどこの Younaship.com をどっかに紹介してほしいなーって思ってるだけなんであまり気にしなくて大丈夫です。

ソースファイル説明

UdpSystem.cs の説明です。

Class : UDPSystem()

コントラクタ
UDPSystem(Callback)

インスタンス生成時、コールバック関数を引数としてとります。このコールバックは受信時発生するため送信用として生成するときはnullでも大丈夫です。

関数

Set(hostIP,hostPort,clientIP,clientPort)
自分及び相手のIP、ポートをセットします。
Receive()
受信を開始します。受信するとコールバックが呼ばれます。
Send(byte[])
byte[]を「clientIP:Port」へ送信します。
Send_NonAsync(byte[])
byte[]を同期送信します。※同期送信なので送信中はほかの動作は停止します。
Stop()
WebSocket を閉じます。

Class : ScanIPAddr()

自分のIPアドレス取得用のクラスです。
var myIpAddr = ScanIPAddr.IP[n] といった感じで使います。
nには識別番号を入れます。

~NOTE~
例えばスマートフォンの場合、Wi-fi接続時はキャリア回線(4G/3G)でのIPアドレス・Wi-fi上でのIPアドレスが存在してしまうため選ばなくてはいけません。また、PCの場合仮想PCのネットワークアダプタがあるとそのIPアドレスが存在してしまいます。どちらにおいても優先順位が高いほう(接続中)が0になっているはずなのでデフォルトでは0でいいと思います。状況に合わせて変えてください。

Class : ScanDevice()

特に現段階であまり意味ないです。気にしないでください。

ソースファイル・動作説明

・まず通信を開始するためにUdpClientクラスのインスタンスを生成します。

ホスト側 (ipAddr)
udpSystem = new UDPSystem(null);
udpSystem.Set(ipAddr, 5001, ipAddr2, 5002);

クライアント側(ipAddr2)
udpSystem = new UDPSystem((x) => Receive(x));
udpSystem.Set(ipAddr2, 5002, ipAddr, 5001);
udpSystem.Receive();

クライアント側には受信時の動作としてReceive(byte[])を指定しています。
これで指定ポートでの受信時にReceive(byte[])関数が呼ばれるようになります。(引数は受信したByte配列)

・Update()内で繰り返し行う部分を書き込みます

ホスト側
vector3 = gameObject.transform.position;
DATA sendData = new DATA(vector3);
udpSystem.Send(sendData.ToByte(),99);

ホスト側はゲームオブジェクトの座標を取得・送信を繰り返します。
SendData.ToByte()でVector3の座標情報をByte配列に変換し、その内容をudpSystem.Send()で送信しています。

またSend()で送信する際に引数99をとっているのは理由がありまして、私の作ったSend関数は送信に失敗した場合(送信処理中などで)に10回までコンテニューをするようになっているためにループで呼ぶ際は失敗するとどんどん送信待機中のデータが溜まってしまうので引数に10以上の数値を入れることで失敗しても1度しか送らないようにし、回避しています。この辺は各自でカスタマイズしてください。

クライアント側
gameObject.transform.position = vector3;

vector3に保存されている値をゲームオブジェクトに反映させています。

void Receive(byte[] bytes)
{
DATA getData = new DATA(bytes);
vector3 = getData.ToVector3();
}

クライアント側のみサーバーからデータを受信時この関数が呼び出されます。受信したByte配列のデータを使える形に変換及び読み取り用の変数vector3に代入しています。

共通部分
text.text = “(” + vector3.x + “,” + vector3.y + “,” + vector3.z + “)”;

現在のゲームオブジェクトの位置を数値として画面に表示しているだけです。


ダウンロード

「ソースコード見ただけじゃ分からんわ👼」
「いや、動かんやけど♡」

という方などに向けてプロジェクトやソースコードの配布を行います。
※なお、全ての環境における動作保証はしていません事をご理解ください。

当方が確認した動作環境

・「デスクトップPC」- (有線LAN) – (wi-fi) -「スマホ」
・「ノートPC」- (wi-fi) – 「スマホ」
・ 「デスクトップPC」- (有線LAN) -「ノートパソコン」

での通信ができることを確認しました。なお、スマホは「Android(Xperia-Z5及びX)、iphone 6」を用いて確認しました。

ソースファイル ( main.cs、UdpClient.cs )

(2019/01/18 追記) 一応これでも動きますが変な部分・書き直すべき部分があったので一部書き直しています。いつか新しいソースコードに変えておきます。

Unity プロジェクトファイルのダウンロード (zip形式)


ここまで読んでくれてありがとうございました!
沢山の人でゲーム業界・アプリ業界など盛り上げていけるといいですね!

ホームページでは他にも

・様々な記事や作った作品および過程
・ソースコード、3Dファイル
・あらゆる”モノ”の作り方

などなど随時記事を新規公開・更新していますので是非見ていってくださいね!見ていただけると本当に嬉しいです!

また、「このアプリの作り方を知りたい。この部分どうなってるの?」「身の回りのこんなもの作れるの?」などなどご意見何でも受け付けていますので是非連絡くださいね!

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です