Unity OSCを使いOSCを送受信する

#Unity OSC
[以前書いた記事](http://qiita.com/ya7gisa0/items/0bcad395f9e8e8935f71)は間違っていた箇所もあったので、もう一度[UnityOSC](https://github.com/jorgegarcia/UnityOSC)に関する投稿。
トリガーによって流れる映像を二つのアプリケーションで同期するためにOSCを使う。同時に音は8chで出力するためにMAXにも信号を送る。

![VR_System.png](https://qiita-image-store.s3.amazonaws.com/0/53282/71558011-94c3-c006-54cc-606fb08d93fd.png)

##IP, Port
受信するならCreateServer()、送信するならCreateClient()
複数の送信先を作れるのがoFのOSCSenderSampleなどより便利かもしれない。

```csharp:OSCHandler.cs
	public void Init()
	{
		// this is for the receiver app
        CreateServer("UnityReceiver", 12345);

		// those are for the sender app
		//CreateClient("Unity", IPAddress.Parse("169.254.182.100"), 12345);
		//CreateClient("MAX", IPAddress.Parse("169.254.182.100"), 12346);
	}
```
##Code
Start()で

```
OSCHandler.Instance.Init ();
```
後はUpdate中に作ったメソッド入れるとわかりやすいのでは(受信側だととくに)。

```csharp
public class SOMECLASS : MonoBehaviour {
    void Start() {
		OSCHandler.Instance.Init ();
    }
    void Update() {
		// if sender app..
		CheckOSCSend();
		// if receiver app..
		//ListenOSC();
    }
}
```
###Sender
任意のMonoBehaviour継承クラスのUpdate()メソッドにListenToOSC()などを追加。
キーが一度押されただけでも複数のOSCを送ってしまっていたのでPreventRedundantOSC()という関数を追加。一度OSCを送ったら送信不可能にしている。
SendReset()はMAX側だけにkeydownから一定時間経過したらOSCを送る処理。

```csharp
	bool isOSCReady = true;

	void CheckOSCSend(){
		if (
			(Input.anyKey) &&
			(isOSCReady)
		) {
			string inputStr = Input.inputString;
			Debug.Log (inputStr);
			string allCharChunk = "123456789";
			if (allCharChunk.Contains(inputStr)) {
				Debug.Log (inputStr + " is pressed.");
				int sentInt;
				int.TryParse (inputStr, out sentInt);
				OSCHandler.Instance.SendMessageToClient ("Unity", "/Audio", sentInt);
				OSCHandler.Instance.SendMessageToClient ("MAX", "/Audio", sentInt);
				// set timer
				StartCoroutine(SendReset(sentInt));
				StartCoroutine (PreventRedundantOSC());
			}
			Debug.Log (inputStr);
		}
	}

	// prevent sending OSC multiple times with just one keydown
	IEnumerator PreventRedundantOSC() {
		isOSCReady = false;
		yield return new WaitForSeconds (2.0f);
		isOSCReady = true;
	}

	IEnumerator SendReset(int movieType) {
		float[] waitTimes = new float[9] {4.7f, 4.0f, 5.2f, 5.55f, 4.3f, 9.0f, 4.6f, 4.5f, 4.5f};
		int targetNum = movieType - 1;
		yield return new WaitForSeconds (waitTimes [targetNum]);
		OSCHandler.Instance.SendMessageToClient ("MAX", "/Audio", 0);
	}

```

###Receiver
任意のMonoBehaviour継承クラスのUpdate()メソッドにListenToOSC()などを追加。
他のクラスに渡す処理を考えたりするとOSCController.csなどを作るよりも、OSCを送る・受ける処理が必要なクラスにそのまま追加してもいいのかもしれない。今回はキーボードの1~9を押されたらその値を送るというものだった。その値によって流れる映像が変わるというもの。(映像処理はRenderPitcher()が実行している)

```csharp
	// methods related to osc goes from here.
	void listenToOSC(){
		OSCHandler.Instance.UpdateLogs ();
		Dictionary<string, ServerLog> servers = new Dictionary<string, ServerLog>();
		servers = OSCHandler.Instance.Servers;

		foreach (KeyValuePair<string, ServerLog> item in servers) {
			Debug.Log (item.Value.log.Count);
			if (item.Value.log.Count > 0) {
				Debug.Log ("count is more than zero");
				int lastPacketIndex = item.Value.packets.Count - 1;

				string s1 = "123456789";
				string s2 = item.Value.packets [lastPacketIndex].Data [0].ToString ();
				if (
					(s1.Contains(s2)) &&
					(item.Value.packets[lastPacketIndex].Address == "/Audio")
				) {
					RenderPitcher(s2);
					// reset
					item.Value.packets[lastPacketIndex].Address = "/reset";
					Debug.Log ("change view");
				}
			}
		}
		OSCHandler.Instance.UpdateLogs ();
	}
```

##Tips
###Wifi
ここが一番意味がありそうだが、Wifi経由だとMAXにはOSC受けれるがUnityでは受信できないという問題があった。有線にしたら解決した。UE4でもOSC使っていたりするのだが普通に無線通信で機能していた。UnityよりもPlugin(UnityOSC)の問題なのかもしれない。
###Version
UnityVersion最新(たしか5.34)で作ったプロジェクトを他PCのUnityEditor(ver5.0)でrebuildしたらOSC通信できないという問題があった。複数PC使うシステムなんかではとくにversion気にしていないと無駄に時間をかけてしまいそう。