前の続きです。前回は導通チェック程度だったけど、今回はちゃんとメッセージをOSCで送受信します。今回通信するのはUnityとArduino(イーサネットシールド付き)の間でやります。
#junki::acoustic UnityでOSCを使う(UnityOSC)
Unity側の準備
セットアップは上の記事を参考に済ませてください。前回と変更した部分を書きます。
OSCHandler.cs
CreateClientのclientID部分の名前をArduinoに変えただけです。
public void Init(string targetAddr, int outGoingPort, int inComingPort) { //Initialize OSC clients (transmitters) // Sender CreateClient("Arduino", IPAddress.Parse(targetAddr), outGoingPort); //Initialize OSC servers (listeners) // Receiver CreateServer("SomeOscClient", inComingPort); }
OSCController.cs
OSCを実際に使う部分です。UpdateLogsで受信したパケットをログに突っ込んでます。とりあえずアップデートがあったときだけ表示するように変更しました。また送信部分はマウスのクリック時のXYポジションを送るように変更しました。
注意点としてUpdateLogsはUnity側のアプリのフレームレートで呼ばれるので相手側のフレームレートと合わせないとOSCパケットを取りこぼす可能性があります。あんまりシビアなものでなければとりあえず今のところ気にしなくても良いけど。
using UnityEngine; using System; using System.Collections; using System.Collections.Generic; using System.Text; using UnityOSC; public class OSCController : MonoBehaviour { #region Network Settings public string TargetAddr; public int OutGoingPort; public int InComingPort; #endregion private Dictionary<string, ServerLog> servers; private int lastPacketIndex; // Script initialization void Start() { OSCHandler.Instance.Init(TargetAddr, OutGoingPort, InComingPort); servers = new Dictionary<string, ServerLog>(); } // NOTE: The received messages at each server are updated here // Hence, this update depends on your application architecture // How many frames per second or Update() calls per frame? void Update() { // must be called before you try to read value from osc server OSCHandler.Instance.UpdateLogs(); // Recieve servers = OSCHandler.Instance.Servers; foreach( KeyValuePair<string, ServerLog> item in servers ) { // show the latest osc packet if(item.Value.packets.Count > 0 && lastPacketIndex != item.Value.packets.Count - 1) { lastPacketIndex = item.Value.packets.Count - 1; UnityEngine.Debug.Log(String.Format("SERVER: {0} ADDRESS: {1} VALUE 0: {2} VALUE 1: {3} VALUE 2: {4}", item.Key, // Server name item.Value.packets[lastPacketIndex].Address, // OSC address item.Value.packets[lastPacketIndex].Data[0].ToString(), //First data value item.Value.packets[lastPacketIndex].Data[1].ToString(), item.Value.packets[lastPacketIndex].Data[2].ToString())); } } // Send if(Input.GetMouseButtonDown(0)) { Debug.Log("SendMessage"); var clickPos = new List<float>(){Input.mousePosition.x, Input.mousePosition.y}; OSCHandler.Instance.SendMessageToClient("Arduino", "/click/pos", clickPos); } } }
Arduino側の準備
ArdOSCのExample/SimpleRecieve
ArduinoでOSCを使うには最初oscuinoとかいうのを使おうと思ったけど、うまくOSCのアドレスマッチングが動かなかったためこちらの方のOSCライブラリを使った。ありがたや。
ArdOSCを使う手順はこんな感じ。
- これをzipで落として来て、フォルダをArdOSCにリネーム
- Arduino/libraries/ArdOSC/という感じに直置きする
- Arduino IDEを再起動して、[Sketch] -> [Import Library...] -> [ArdOSC]
OSCSample.ino
MACアドレスはイーサネットシールド裏側に記載があります。myIpはArduinoに設定するIPアドレス、destIpは通信先(今回はUnity側)で設定します。ライブラリがシンプルなので説明すべき部分はほぼないですが、取り扱うOSCのアドレスに対してその処理を行う関数を関連づけておくと、OSCパケットを受信するとその関数がコールされます。パケットをArduino側で受信したらEcho的にUnity側にも"/ard/echo"として適当に3つの値を返すようにしました。
#include <SPI.h> #include <Ethernet.h> #include <ArdOSC.h> byte myMac[] = { 0xXX, 0xXX, 0xXX, 0xXX, 0xXX, 0xXX }; byte myIp[] = { 192, 168, x, x }; byte destIp[] = { 192, 168, x, x }; int inPort = 5555; // 受信ポート int outPort = 6666; // 送信ポート OSCServer server; OSCClient client; void setup(){ Serial.begin(19200); Ethernet.begin(myMac ,myIp); server.begin(inPort); //set callback function server.addCallback("/click/pos", &clickEvent); } void loop(){ if(server.aviableCheck()>0){ Serial.println("alive! "); } } void clickEvent(OSCMessage *_mes){ Serial.println("clickEvent"); int posX = _mes->getArgFloat(0); int posY = _mes->getArgFloat(1); Serial.print(posX); Serial.print(", "); Serial.println(posY); sendMessage(); } void sendMessage() { Serial.println("sendMessage"); OSCMessage msg; msg.setAddress(destIp, outPort); // 適当なメッセージを作成 msg.beginMessage("/ard/echo"); msg.addArgInt32(10); msg.addArgFloat(10.0f); msg.addArgString("i am arduino."); client.send(&msg); msg.flush(); //object data clear }
実行
Unity側で適当に画面をクリックするとArduino側のシリアルコンソールにマウス座標が飛ばされ、そのエコーとしてサンプルのデータをUnityのコンソールログでも確認できます。これで開発が捗ります。
Unityのコンソール
SendMessage UnityEngine.Debug:Log(Object) OSCController:Update() (at Assets/Script/OSCController.cs:68) SERVER: SomeOscClient ADDRESS: /ard/echo VALUE 0: 10 VALUE 1: 10 VALUE 2: i am arduino. UnityEngine.Debug:Log(Object) OSCController:Update() (at Assets/Script/OSCController.cs:57)