Developer's Blog

UnityとiPhoneでUDP通信 〜Unityを使ってiPhoneをウユニ塩湖に転送する方法〜

こんにちは。デザイン部企画課の小川です。

唐突ですが、ウユニ塩湖、インスタ映えするということで、流行ってますね!

Unityを使えば、iPhoneとUnityの通信も比較的簡単に行えるので、iPhoneの動きと連動した動作をUnity上でつけることが可能です。さらに、鏡面反射効果をつければ、iPhone をPC上のウユニ塩湖に転送したっぽいものも作れてしまいます。

やや強引な動機ではありますが、早速作ってみましょう!

iPhoneとUnityで通信する

今回はUDP通信という方法を使ってみたいと思います。UDP通信とは、アプリケーション層のプロトコル(通信手段)で、パケット制御を行わないので、通信速度は速いものの、信頼性の低いプロトコルになります。速度が出るので、送りっぱなしでも構わないもの、ストリーミングサービスやインスタレーションなどで使われています。

Unityとの通信は、UDPの目的に応じて送信受信ができるものでしたら、MaxMSPProcessingopenFrameworksvvvvTouchDesigner など幅広く通信することが可能です。今回はiPhoneのセンサーから得た回転情報をJSON形式で受け取るために、ZIG SIMというアプリケーションを使います。加速度や、ジャイロ、GPSや、タッチなどの値を送ることができるので、他の実行環境と組み合わせて、インスタレーションを作る際など、プロトタイピングをする際に便利なアプリです。

UnityでUDP通信を実装する

まずは、先ほど紹介したZIG SIMと Unityの連携をしてみましょう。CreateEmptyで空のGameObejctを作って、名前をMainControllerなどとつけておきます。プロジェクトの画面にて、Createのタブから、C#スクリプトを選択し、UDPReceiver.cs のような名前のクラスを作ります。そして以下のように記述します。このUDPReceiverクラスのUDPStartメソッドが呼ばれてから、通信が発生するのを想定しています。

 
using System;
using System.IO;
using System.Text;
using System.Threading;
using System.Net;
using System.Net.Sockets;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using MiniJSON;

public class UDPReceiver : MonoBehaviour {
	
	int LOCAL_PORT = 22222;
	static UdpClient udp;
	Thread thread;

	public void UDPStart(){
		udp = new UdpClient (LOCAL_PORT);
		thread = new Thread (new ThreadStart (ThreadMethod));
		thread.Start ();
	}

	private static void ThreadMethod()
	{
		while (true)
		{
			IPEndPoint remoteEp = null;
			byte[] data = udp.Receive (ref remoteEp);
			string text = Encoding.ASCII.GetString (data);

			JsonNode jsonNode = JsonNode.Parse (text);

			double ax = jsonNode ["sensordata"] ["accel"] ["x"].Get<double> ();
			double ay = jsonNode ["sensordata"] ["accel"] ["y"].Get<double> ();
			double az = jsonNode ["sensordata"] ["accel"] ["z"].Get<double> ();

			double qutX = jsonNode ["sensordata"] ["quaternion"] ["x"].Get<double> ();
			double qutY = jsonNode ["sensordata"] ["quaternion"] ["y"].Get<double> ();
			double qutZ = jsonNode ["sensordata"] ["quaternion"] ["z"].Get<double> ();
			double qutW = jsonNode ["sensordata"] ["quaternion"] ["w"].Get<double> ();

			// Debug.Log("ax = " + ax + ", ay = " + ay + ", az = " + az);
		}
	}

	void OnApplicationQuit(){
		thread.Abort ();
	}
}

アプリからの値は JSON形式で受け取っているので、MiniJSONJsonNode を使ってパースしているところがポイントです。

次に、MainController.csのスクリプトを作成し、先ほど作ったGameObject、MainControllerに貼り付けます。MainController.csとUDPReceiver.csの二つのスクリプトが貼られた状態になっています。忘れずに、public変数のupdReceiverに参照を入れておきましょう。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class MainController : MonoBehaviour {

	public UDPReceiver updReceiver;

	// Use this for initialization
	void Start () {
		updReceiver.UDPStart ();
	}
}

記述が終わり、エラーがなさそうでしたら、Unityを実行、ZIG SIMを起動します。UnityのConsole画面内に、アプリからの値を受け取っている様子が確認できれば、OKです。

3Dモデルをインポートする

せっかくなので、自分の iPhoneにあった3Dモデルを探すか、作りましょう。
今回はこちらのモデルを使用させていただきました。

iPhone 6 – 3d model – .3ds, .obj, .blend

天球素材をインポートする

遠景素材として、ウユニ塩湖ぽっく見える天球素材を探しましょう。AssetStoreにて配布されているフリー素材のSky5X Oneがオススメです。ダウンロード後、importのダイアログが出るので、importボタンを押してプロジェクトに追加してください。メインカメラを選択し、インスペクタ上で、SkyboxコンポーネントをAdd Componentします。先ほどダウンロードした、sky5X > sky5X_skyboxes フォルダに入っているマテリアルを SkyboxコンポーネントのCustom Skyboxの欄にドラッグ&ドロップすると、天球素材を設定することができます。ウユニ塩湖ぽっく見えるのは、sky5X3ですが、色々試してみるのも面白いかもしれません。


Unityで鏡面反射効果をつけてみる

リフレクションプローブという機能を使うことで、オブジェクトが鏡面反射されたような効果を得ることができます。負荷は高めなので、必要に応じて使いましょう。

Unity – マニュアル: リフレクションプローブ

ヒエラルキー上に、CreateEmpty で空のGameObjectを作成し、Reflection ProbeコンポーネントをAddComponentします。
設定は、Type を Realtime に RefreshMode を Every fame に設定します。境界をうまく設定することで鏡面反射の効果を得ることができます。

UDPReceiver.csとMainController.csを修正する

スクリプトに修正を加えます。UDP通信のデータをコールバックするために、Actionを使います。

using System;
using System.IO;
using System.Text;
using System.Threading;
using System.Net;
using System.Net.Sockets;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using MiniJSON;

public class UDPReceiver : MonoBehaviour {

	int LOCAL_PORT = 22222;
	static UdpClient udp;
	Thread thread;

	public static Action<float, float, float> AccelCallBack;
	public static Action<float, float, float, float> GyroCallBack;

	public void UDPStart(){
		udp = new UdpClient (LOCAL_PORT);
		thread = new Thread (new ThreadStart (ThreadMethod));
		thread.Start ();
	}

	private static void ThreadMethod()
	{
		while (true)
		{
			IPEndPoint remoteEp = null;
			byte[] data = udp.Receive (ref remoteEp);
			string text = Encoding.ASCII.GetString (data);

			JsonNode jsonNode = JsonNode.Parse (text);

			double ax = jsonNode ["sensordata"] ["accel"] ["x"].Get<double> ();
			double ay = jsonNode ["sensordata"] ["accel"] ["y"].Get<double> ();
			double az = jsonNode ["sensordata"] ["accel"] ["z"].Get<double> ();

			double qutX = jsonNode ["sensordata"] ["quaternion"] ["x"].Get<double> ();
			double qutY = jsonNode ["sensordata"] ["quaternion"] ["y"].Get<double> ();
			double qutZ = jsonNode ["sensordata"] ["quaternion"] ["z"].Get<double> ();
			double qutW = jsonNode ["sensordata"] ["quaternion"] ["w"].Get<double> ();

			AccelCallBack ((float)ax, (float)ay, (float)az);
			GyroCallBack ((float)qutX, (float)qutY, (float)qutZ, (float)qutW);
		}
	}

	void OnApplicationQuit(){
		thread.Abort ();
	}
}

Actionで受け取った値を、Transformの回転情報に変換してあげます。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class MainController : MonoBehaviour {

	public UDPReceiver updReceiver;
	public Transform phoneTf;
	public Quaternion phoneRot;

	// Use this for initialization
	void Start () {
		UDPReceiver.AccelCallBack += AccelAction;
		UDPReceiver.GyroCallBack  += GyroAction;
		updReceiver.UDPStart ();
	}

	public void AccelAction(float xx, float  yy, float zz){

	}

	public void GyroAction(float xx, float yy, float zz, float ww){
		var newQut = new Quaternion (-xx, -zz, -yy, ww);
		var newRot = newQut * Quaternion.Euler (90f, 0, 0);
		phoneRot = newRot;
	}

	// Update is called once per frame
	void Update () {
		phoneTf.localRotation = phoneRot;
	}
}

完成

Unityを実行してから、iPhoneの ZIG SIM を立ち上げると、動画のような状態になりました。実際のiPhoneを回転させることで、Unityの画面上のiPhoneがくるくる回っています。いつか本物のウユニ塩湖に行ってみたいものですね!

まとめ

今回はUDPという技術を使って、Unityの空間上に3DモデルのiPhoneを浮かべてみました。使い慣れたスマートフォンが仮装空間上に浮かび、現実と連動して動く様子はシンプルな面白さがあります。スマートフォンはセンサーの塊なので、今回使用した回転情報の他にもジャイロセンサーやコンパス、GPSや加速度などを活かすことで、ゲームのコントローラーとして扱ったり、VRコンテンツと組み合わせて仮想空間上での携帯にしてみたりと、表現の幅が広がる可能性を感じます。


フェンリルのオフィシャル Twitter アカウントでは、フェンリルプロダクトの最新情報などをつぶやいています。よろしければフォローしてください!


フェンリルの開発者アカウントでは、フェンリルプロダクトの最新開発情報などをつぶやいています。よろしければフォローしてください!


フェンリル採用チームの Twitter アカウントです。応募前のお問い合わせや、ちょっとした相談ごとなどお気軽にどうぞ!


フェンリルの Facebook ページでは、最新トピックをお知らせしています。よろしければいいね!してください!


Sleipnir の Facebook ページでは、ユーザーの方たちとのコミュニケーションや最新情報の投稿などを行なっています。よろしければいいね!してください!


Copyright © 2018 Fenrir Inc. All rights reserved.