[Unity] Intel Edison で Unity を実行する


ただし **グラフィクスは一切なし** です。
使用した Unity のバージョンは 5.1.1.f1 personal です。

#Edison をセットアップ
Switch Science さんの解説は各ＯＳごとにまとめてあってすばらしいです。
[Intel Edisonを手に入れたらやること](http://trac.switch-science.com/wiki/IntelEdisonStart)
ただし現時点で、一部コマンドの使い方が異なっていました。
`configure_edison --setup`
の引数 --setup は必要なかったり。

また、そのままだと自動で Wifi に接続してくれないので、
[[Intel Edison] 起動したら Wifi につながるようにする](http://qiita.com/dsedb/items/30823fbb7170e1c42402)

をやっときます。

#Unity プロジェクト準備
プロジェクトはなんでもいいですが、実験なので軽いものがよいですね。
とはいえグラフィクスは表示されないので、動作確認がむずかしい。できれば Edison にログインしなくても確認できるのが望ましいので、今回は UDP パケットを投げるサンプルを作りました。（たまたま最近似たようなのを作ったこともあり）。

##１秒置きにUDPパケットを送り続けるサンプル

```c#:UdpSender.cs
using UnityEngine;
using System.Collections;
using System.Net.NetworkInformation;

public class UdpSender : MonoBehaviour {
	public int listenPort = 2002; // ポートはサーバ・クライアントで合わせる必要がある
	private static bool sent = false;
	private Coroutine coroutine_ = null;

	public static void SendCallback(System.IAsyncResult ar)
	{
		// System.Net.Sockets.UdpClient u = (System.Net.Sockets.UdpClient)ar.AsyncState;
		sent = true;
	}

	IEnumerator send(string server, string message)
	{
		Debug.Log("sending..");
		var u = new System.Net.Sockets.UdpClient();
		u.EnableBroadcast = true;
		u.Connect(server, listenPort);
		var sendBytes = System.Text.Encoding.ASCII.GetBytes(message);
		sent = false;
		u.BeginSend(sendBytes, sendBytes.Length, 
					new System.AsyncCallback(SendCallback), u);
		while (!sent) {
			yield return null;
		}
		u.Close();
		coroutine_ = null;
		Debug.Log("done.");
	}

	IEnumerator sending()
	{
		for (;;) {
			if (coroutine_ == null) {
				NetworkInterface[] nicList = NetworkInterface.GetAllNetworkInterfaces();
				string sendMsg = "Edison";
				foreach( NetworkInterface nic in nicList ) {
					IPInterfaceProperties ipInfo = nic.GetIPProperties();
					foreach (var addr in ipInfo.UnicastAddresses) {
						var bytes = addr.Address.GetAddressBytes ();
						// RFC1918
						if (bytes[0] == 10 ||
						    (bytes[0] == 172 && 16 <= bytes[1] && bytes[1] <= 31) ||
						    (bytes[0] == 192 && bytes[1] == 168)) {
							sendMsg += string.Format (":{0}", addr.Address.ToString ());
						}
					}
				}
				string server = "192.168.0.1"; // 可能ならブロードキャストが楽
				coroutine_ = StartCoroutine(send(server, sendMsg));
				yield return new WaitForSeconds(1f);
			}
		}
	}

	void Start() {
		StartCoroutine(sending());
	}

	void Update() {
    }
}
```
このスクリプトを適当なオブジェクトにアタッチして、Hierarchy に置きます。
下のほうにある
`string server = "192.168.0.1";`
ここは適宜書き換えてください。自宅などサブネットを掌握している場合はブロードキャストアドレスを書いちゃうのが楽です。職場とか学校なら迷惑をかけないよう、自分の作業ＰＣのアドレスを入れましょう。

パケットにはアドレスを文字列にして入れておきます。あとでこれが役に立つのです。検出したNICを列挙して、RFC1918 に規定してあるプライベートネットワークアドレスを抽出しています。


#Unity でビルド
上記スクリプトの入ったシーンを保存して、BuildSettings Window から

- Target Platform:Linux
- Architecture: x86

でビルドします。吐き出し先のフォルダ名は linux にしておきます。（なんでもよいです）
linux という名前にしてあれば、プロジェクトの下に

- linux.x86
- linux_Data

というファイル＆フォルダが現れています。これらを Edison に送るわけですが、
ディレクトリもあるので、tar で固めます。

`$ tar -cvfz linux_Data.tar.gz linux.x86 linux_Data`

#Edison へ転送
上記のビルド結果を Edison の /var に送ります。(/var は例で、どこでもよいです）

`$ scp linux_Data.tar.gz root@192.168.2.15:/var`


Edison にログインして /var 以下を確認し、展開します。

`root@yuji-edison:~# cd /var`
`root@yuji-edison:/var# tar -xvfz linux_Data.tar.gz`

#実行
実行してみましょう。

```shell-session:
root@yuji-edison:/var# ./linux.x86
./linux.x86: error while loading shared libraries: libGL.so.1: cannot open shared object file: No such file or directory
```

エラーが出ますよね。これは予定通り。引数でグラフィクスを無効にすればよいのです。

```shell-session:
root@yuji-edison:/var# ./linux.x86 -batchmode -nographics
./linux.x86: error while loading shared libraries: libGL.so.1: cannot open shared object file: No such file or directory
```

同じエラーが出ましたね。・・・だめじゃん！

# ダミーso の作成
本当の戦いはここからです。

Unity のドキュメント見ても、-batchmode -nographics でグラフィクスの初期化しない、と書いてあります。初期化はしない。しないが、so だけ読む。んですね。ならば読み込みにさえ成功させればよいはず。なのでダミーを作ります。

/var で作業するのはなんなので、ホームに移動して、main.cpp というファイルを作成します。

```C++:main.cpp
void dummy()
{
}
```

清々しいコードです。include すらしないコードなんて滅多に書きませんね。
ビルドして、so を作ります。

`root@yuji-edison:~/work/dummy# gcc -shared main.cpp -o libGL.so.1`

できました。この libGL.so.1 を /usr/lib にコピーします。

`root@yuji-edison:~/work/dummy# cp libGL.so.1 /usr/lib`

さあ、どうだ。

```shell-session:
root@yuji-edison:/var# ./linux.x86 -batchmode -nographics
./linux.x86: error while loading shared libraries: libX11.so.6: cannot open shared object file: No such file or directory
```

だめ。でも一歩前進。こんどは libX11.so.6 がない。
この調子でダミーを作っていくわけですが、結論からいいますと、ここには複数の関数定義も含める必要がありました。

```C++:main.cpp
extern "C" {
typedef void Display;

Display* XOpenDisplay(char* display_name)
{
  return 0;
}

typedef void* XIM;
typedef void* XrmDatabase;
XIM XOpenIM(Display*, XrmDatabase, char*, char*)
{
  return 0;
}

typedef void* XIC;
XIC XCreateIC(XIM im, ...)
{
  return 0;
}

}
```

これをビルドして、
`root@yuji-edison:~/work/dummy# gcc -shared main.cpp -o libX11.so.6`
コピーします。
`root@yuji-edison:~/work/dummy# cp libX11.so.6 /usr/lib`

これでも動きません。必要なダミーso はあと２つありました。
libXcursor.so.1
libXrandr.so.2
これらは空っぽでよいので、libGL.so.1 をコピーします。置き場所は同じく /usr/lib です。
`root@yuji-edison:~# cd /usr/lib`
`root@yuji-edison:/usr/lib# cp libGL.so.1 libXcursor.so.1`
`root@yuji-edison:/usr/lib# cp libGL.so.1 libXrandr.so.2`

さあ、動くかな？
`root@yuji-edison:/var# ./linux.x86 -batchmode -nographics`

```shell-session:
Set current directory to /var
Found path: /var/linux.x86
Mono path[0] = '/var/linux_Data/Managed'
Mono path[1] = '/var/linux_Data/Mono'
Mono config path = '/var/linux_Data/Mono/etc'
PlayerConnection initialized from /var/linux_Data (debug = 0)
PlayerConnection initialized network socket : 0.0.0.0 55014
Multi-casting "[IP] 192.168.0.105 [Port] 55014 [Flags] 2 [Guid] 4221828501 [EditorId] 628846382 [Version] 1048832 [Id] LinuxPlayer(192.168.0.105) [Debug] 0" to [225.0.0.222:54997]...
PlayerConnection already initialized - listening to [192.168.0.105:55014]
displaymanager : trying .X11-unix
Using libudev for joystick management


Importing game controller configs
```

動いた！・・・のか？
確認するために受信側のプログラムを作成します。

#受信プログラムの作成
Unity でプロジェクトを用意して、以下のスクリプトを用意します。

```C#:UdpReceiver.cs
using UnityEngine;
using System.Collections;

public class UdpReceiver : MonoBehaviour {
	public int listenPort = 2002; // ポートはサーバ・クライアントで合わせる必要がある
	private static bool received = false;

	private struct UdpState {
		public System.Net.IPEndPoint e;
		public System.Net.Sockets.UdpClient u;
	}

	public static void ReceiveCallback(System.IAsyncResult ar) {
		System.Net.Sockets.UdpClient u = (System.Net.Sockets.UdpClient)((UdpState)(ar.AsyncState)).u;
		System.Net.IPEndPoint e = (System.Net.IPEndPoint)((UdpState)(ar.AsyncState)).e;
		var receiveBytes = u.EndReceive(ar, ref e);
		var receiveString = System.Text.Encoding.ASCII.GetString(receiveBytes);

		Debug.Log(string.Format("Received: {0}", receiveString)); // ここに任意の処理を書く

		received = true;
	}

	IEnumerator receive_loop() {
		var e = new System.Net.IPEndPoint(System.Net.IPAddress.Any, listenPort);
		var u = new System.Net.Sockets.UdpClient(e);
		u.EnableBroadcast = true;
		var s = new UdpState();
		s.e = e;
		s.u = u;
		for (;;) {
			received = false;
			u.BeginReceive(new System.AsyncCallback(ReceiveCallback), s);
			while (!received) {
				yield return null;
			}
		}
	}

	void Start () {
		StartCoroutine(receive_loop());
	}
}
```


これを GameObject にアタッチして実行します。実行というかエディタの再生ですね。
Edison は先ほどのを実行させた状態です。

![スクリーンショット 2015-08-25 20.58.06.png](https://qiita-image-store.s3.amazonaws.com/0/90046/189602dd-ac77-0f35-1407-4b04da53f70b.png "")

見事、受信しています。これで動作確認ができました。めでたしめでたし。

#Unity プログラムを Intel Edison で自動起動する
というエントリを近日アップします。こちらのほうが難易度が高いです実は。
これができると、本格的に何かに利用したくなってきます。

----
追記：書きました。
[[Unity] Unity プログラムを Intel Edison で自動起動する](http://qiita.com/dsedb/items/0ed8f58536f3fa6ab026)