Quantcast
Channel: C#タグが付けられた新着記事 - Qiita
Viewing all articles
Browse latest Browse all 9525

C# .net、Unityアプリケーション間でMemoryMappedFileを用いてデータを共有する

$
0
0

やりたいこと

弊社の業務では、なんらかのセンサーを用いて情報を読み取り、その状況によって演出を行うといったことがよくあります。
演出をするアプリケーションはUnityで作成するとして、センサーを読み取る部分をどのように実装するかが問題になります。

ネイティブプラグインもしくはライブラリとして実装する手もありますが、センサーの調整や制御が少し面倒になるので、
- アプリケーションをC#.NETのWindowsフォームアプリケーションで作成
- 演出アプリケーションをUnityで作成
というように、2つのアプリケーションで分けて行うことにしました。

次のような恩恵がありました。
- センサー機器の担当者と演出の担当者の作業を分けることが出来る
- センサーの種類が変わっても対応できる

やりかた

MemoryMappedFileを用いて、メモリ内でファイルのようなものを扱い、アプリ間でデータのやりとりを行います。
MSDNのMemoryMappedFileクラスドキュメント
構造体を直接バイナリファイルに書き込んでポインタで参照したり、BinaryFormatterで書き込む手もあると思います。それだとパフォーマンスは間違いなく出ると思いますが怖いので……
今回は安全にxmlにシリアライズしたデータをやりとりします。

仕組みは単純で、センサーアプリ側ではデータを1つのクラスにまとめてxmlの形式にシリアライズし、メモリマップトファイルに書き込みます。演出アプリ側ではそれを読み取り、デシリアライズして元のクラスのインスタンスを復元することが出来ます。
ただし注意点として、2つのアプリでメモリを同時に読み書きしようとすると何らかの不具合が出るのは必至ですので、排他制御を確実に行います。

共通(データ定義)

以下が受け渡しを行うデータの例です。ここではSensorDataクラスがシリアライズするクラスです。
SerializableAttribute属性を付与していますが、実はXmlSerializerではSerializableAttribute属性は不要でした。
念のため残しております。

CensorData.cs
[Serializable]structSensorPoint{publicfloatx;publicfloaty;}[Serializable]classSensorData{publicfloattime;publicList<SensorPoint>points;}

書き込み側

Censor.cs
classhogeClass{privateMutexmutex;// 排他制御に使用MemoryMappedFilemmfile=null;// Formの作成時などに呼ばれるpublicvoidInitialize(){stringmutexName="SensorAppMutex";boolcreatedNew=false;mutex=newMutex(false,mutexName,outcreatedNew);}// アプリ終了時に呼ばれるpublicvoidFinalize(){mmfile?.Dispose();mutex?.Dispose();}// 共有するデータを保存したいタイミングで呼ぶpublicvoidSaveSensorData(constSensorDatadata){// シリアライザーの作成System.Xml.Serialization.XmlSerializerserializer=newSystem.Xml.Serialization.XmlSerializer(typeof(SensorData));boolgetMutex=false;try{if(getMutex=mutex.WaitOne(5000)){mmfile?.Dispose();mmfile=MemoryMappedFile.CreateNew("SensorAppData",1024*1024*1);using(MemoryMappedViewStreamstream=mmfile.CreateViewStream()){serializer.Serialize(stream,data);}}}finally{if(getMutex)mutex.ReleaseMutex();}}}

読み込み側

Unityアプリ側の処理です。
センサーアプリから渡された座標に、オブジェクトを生成しています。

Spawner.cs
// センサーアプリから渡された座標に、オブジェクトを生成するpublicclassSpawner:MonoBehaviour{Mutexmutex;// 排他制御// 下記のSampleObjectコンポーネントを取り付けたゲームオブジェクトのプレハブ[SerializeField]GameObjectsampleObject;voidStart(){stringmutexName="SensorAppMutex";boolcreatedNew=false;mutex=newMutex(false,mutexName,outcreatedNew);}voidOnDestroy(){mutex?.Dispose();}voidUpdate(){System.Xml.Serialization.XmlSerializerserializer=newSystem.Xml.Serialization.XmlSerializer(typeof(SensorData));// センサーから取得した結果SensorDatadata=null;boolgetMutex=false;try{if(getMutex=mutex.WaitOne(3000)){using(MemoryMappedFilemmfile=MemoryMappedFile.OpenExisting("SensorAppData"))using(MemoryMappedViewStreamstream=mmfile.CreateViewStream()){result=(SensorData)serializer.Deserialize(stream);}}catch(Exceptionex){// なにかまずいことが起こった場合、ログに残すDebug.LogError($"exception type: {ex.GetType()} msg: {ex.Message}");throwex;}finally{if(getMutex)mutex.ReleaseMutex();}}// データが取得できたので、その位置にオブジェクトを生成するif(data!=null){foreach(varpointindata.points){Vector3pos=newVector3(point.x,point.y,0);GameObjectobj=Instantiate<GameObject>(samplePrefab,pos,Quaternion.identity,this.transform);}}}}
SampleObject.cs
// 生成されて一定時間で消えるゲームオブジェクトの例publicclassSampleObject:MonoBehaviour{constfloatlifeTimeMax=0.5f;floatlifeTime=lifeTimeMax;voidUpdate(){lifeTime-=Time.deltaTime;if(lifeTime<0){Destroy(this.gameObject);}}}

Viewing all articles
Browse latest Browse all 9525

Trending Articles