Unity

安価なモーショントラッカーを作ってみた

 VRのあれこれが高いので自分で作りました。秋月とダイソーで合計260円[税込み]しかかかりません。(3Dプリンター,導線等はプライスレスです。)

 原理としては、ボリュームの値をArduinoで読み取ってUnityに送りコネコネするだけです。
 ↓に動かしてみた動画を載せときます。


使用モデル:ユニティちゃん© UTJ/UCL

 思っていたよりちゃんと動いてよかったです。指1本当たり1センサで、しかも薬指はスペースの都合上センサーがついてないんですが、結構まともに動いてくれました。Unityの仕組みもなんとなく勉強になってよかったです。

Unityにユニティちゃんを召還した話





Unityで3Dモデルを動かそうと思ったので、公式で用意してくれているモデルをUnityで使えるようにしてみました。

 動作環境
  • Windows10
  • Unity 2018.4.31f1 Personal
  • Visual Studio Community 2019 16.8.4
  • Arduino1.8.10
  • ユニティちゃん 3Dモデルデータ Version:1.2.1
①ユニティちゃんをダウンロードする
 (https://unity-chan.com/contents/guideline/)ここからモデルをダウンロードしてきます。

②Unityに入れる
 ダウンロードしてきたファイルを"Assets"にドラックアンドドロップします。
 ↓のような画面が出てくるので右下の"Import"を押してください。
スクリーンショット 2021-02-06 134645

③エラーを取り除く
 今回の環境ではそのまま実行するとエラーが出ます。↓の画像のように"Console"にエラーが出てきます。
スクリーンショット 2021-02-06 140035

 これは"Assets\UnityChan\Scripts\AutoBlik.cs(8,23):error CS0234: The type of namespace name 'Policy' does not exist in the namespace 'System.Security' (are you missing an assembly reference?)"と出るはずです。読めばわかる通り"Assets\UnityChan\Scripts\AutoBlik.cs"というファイルの8行目の"Policy"が存在しないという意味です。
 それなので、このエラー文をダブルクリックして出てきたコードの8行目をコメントアウトしてしまいます。ここでコメントアウトしても変わらずに使えます。
スクリーンショット 2021-02-06 141112

④Unityに地面を設置する
 このままユニティちゃんを配置することもできるのですが、床がないため永遠に落ちて行ってしまいます。そのため地面を設置していきます。"HIerarchy"->"Create"->"3D Object"->"Plane"を選択します。そして"Inspector"で設定をしていくのですが、今回は↓のように設定しました。
スクリーンショット 2021-02-06 141714

⑤ユニティちゃんを召還する
 "Assets"->"UnityChan"->"Prefabs"->"for Locomotion"->"Unitychan"を"Hierarchy"にドラッグ&ドロップします。また、このとき初期位置を適当に設定しておきます。

⑥動かしてみる
 最後にカメラの位置を適当にセッティングして実行すると↓のように動かせます。
 操作は右上に出ているように、十字キーで移動、前進中にスペースでジャンプなどです。また、左のUIをクリックすると表情が変わります。

Unityと現実の直方体の角度をリンクさせた話

 今回は↓の動画のように、現実での直方体とUnity上の直方体の動きをリンクさせました。

 動作環境
  • Windows10
  • Unity 2018.4.31f1 Personal
  • Visual Studio Community 2019 16.8.4
  • Arduino1.8.10

手順
①前回の状態を再現する
 今回は、前回(http://blog.livedoor.jp/chisato_tofu/archives/26199124.html)の発展形なので前回の状態を再現できていることを前提とします。

②センサーを作る
 今回用いたセンサーは可変抵抗を直方体のxyz軸に配置し、その値を角度(°)に変換しUnityに送っています。そのため今回は、三軸に可変抵抗を配置する構造をFusion360で設計し3Dプリンタで制作しています。↓の図のようなモデルをFusion360で作り、赤円の場所に可変抵抗を配置します。
a
 これを3Dプリンターで印刷し組み立て配線を行い下のような状態にしました。
IMG_3008

②Arduinoのコードを書く
 前回のコードを発展させる形で3つのデータを送れるようにします。Unity側で","区切りで読み取ることができるため、データの切れ目に","を挿入して送ります。
  1. int val1;
  2. int val2;
  3. int val3;

  4. void setup() {
  5.   Serial.begin(115200) ;
  6. }

  7. void loop() {
  8.   val1 = analogRead(0);
  9.   val2 = analogRead(1);
  10.   val3 = analogRead(2);

  11.   val1 = map(val1, 880, 160, -90, 90);
  12.   val2 = map(val2, 840, 145, 0, -180);
  13.   val3 = map(val3, 873, 176, -90, 90);

  14.   Serial.print(val1);
  15.   Serial.print(",");
  16.   Serial.print(val2);
  17.   Serial.print(",");
  18.   Serial.println(val3);
  19.   delay(50) ;
  20. }
③C#のコードを書く
 シリアル入力を","で区切りそれぞれをcubeの角度に対応させるようなプログラムを書きました。これを"SerialCube"というC#のファイルに格納しています。
  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. using UnityEngine.UI;

  5. public class SerialCube : MonoBehaviour{

  6. public SerialHandler serialHandler;
  7. public Text text;
  8. public GameObject Cube1;

  9. void Start(){
  10. serialHandler.OnDataReceived += OnDataReceived;
  11. }

  12. void Update(){
  13. }

  14. void OnDataReceived(string message){
  15. try{
  16. string[] angles = message.Split(',');
  17. text.text = "x:" + angles[0] + "\n" + "y:" + angles[1] + "\n" + "z:" + angles[2] + "\n"; 

  18. Vector3 angle = new Vector3(float.Parse(angles[1]), float.Parse(angles[0]), float.Parse(angles[2]));
  19. Cube1.transform.rotation = Quaternion.Euler(angle);
  20. }
  21. catch (System.Exception e){
  22. Debug.LogWarning(e.Message);
  23. }
  24. }
  25. }
④C#とCubeの対応
 ③で書いたコードをCubeに対応させていきます。
 まず、前回と同じように"Hierarchy"->"Creare"->"Create Empty"を行い、"SerialCube"という空オブジェクトを作成し、③で書いたコードをドラッグ&ドロップし対応させておきます。
 "Hierarchy"->"Create"->"3D Object"->"Cube"で直方体を作成し、↓の画像のように"inspector"で調整します。今回は上から二項目目の"Rotation"をシリアル通信で変えていきます。
スクリーンショット 2021-02-05 141526
 作成したCubeに③で作ったファイルをドラッグアンドドロップして関連付けを行います。そうしたら↓の画像のように"Inspector"->Serial Cube (Script)の項目を設定します。
スクリーンショット 2021-02-05 142023

④実行する
 Arduinoにプログラムを書き込み実行させると動作します。
スクリーンショット 2021-02-05 142218

無課金でArduinoとUnityでシリアル通信

ArduinoのデータをUnityで使おうとしたときに"Uniduino"という便利なアセットがあるそうです。しかしこのアセットは有料らしいので、今回は無料で実現する方法です。

 動作環境
  • Windows10
  • Unity 2018.4.31f1 Personal
  • Visual Studio Community 2019 16.8.4
  • Arduino1.8.10
です。Unity2019.4.18f1では動作しませんでした。

Arduino側
 普通にSerial.printで出力します。末尾は改行します。
 シリアルのビットレート(115200)とシリアルポート(今回はCOM7)は後で使うので覚えておいてください。
  1. int val1 = analogRead(0);
  2. void setup() {
  3.   Serial.begin(115200);
  4. }

  5. void loop() {
  6.   val1 = analogRead(0);
  7.   Serial.print(val1);
  8.   Serial.println("");
  9.   delay(1000);
  10. }


Unity側
 まず、前提として"File"->"Build Setting"->"Player Settings"->"OtherSettings"->"Configuration"->"Api Compatibility Level*"を".NET 4.x"にします。
b


①表示部の設定
 Hirerarchyで右クリック"UI"->"Text"を選択します。
 作成できたTextをクリックし、見やすいように各パラメータを設定します。今回自分は↓の画像のように設定しました。
スクリーンショット 2021-02-02 221748
②C#の設定
 シリアルを行うためのプログラムをC#で実装します。"Project"->"Assets"で右クリック"Create"->"C# Script"でC#のファイルを作成しそれぞれ"SerialHandler","SerialLight"と名付けます。それぞれのファイルには↓のコードを入れます。
SerialHandler
  1. using UnityEngine;
  2. using System.Collections;
  3. using System.IO.Ports;
  4. using System.Threading;

  5. public class SerialHandler : MonoBehaviour{
  6. public delegate void SerialDataReceivedEventHandler(string message);
  7. public event SerialDataReceivedEventHandler OnDataReceived;

  8. public string portName = "COM7"; // Arduino IDEで設定したポート
  9. public int baudRate = 115200;  // ビットレート

  10. private SerialPort serialPort_;
  11. private Thread thread_;
  12. private bool isRunning_ = false;

  13. private string message_;
  14. private bool isNewMessageReceived_ = false;

  15. void Awake(){
  16. Open();
  17. }

  18. void Update(){
  19. if (isNewMessageReceived_){
  20. OnDataReceived(message_);
  21. }
  22. isNewMessageReceived_ = false;
  23. }

  24. void OnDestroy(){
  25. Close();
  26. }

  27. private void Open(){
  28. serialPort_ = new SerialPort(portName, baudRate, Parity.None, 8, StopBits.One);
  29. serialPort_.Open();

  30. isRunning_ = true;

  31. thread_ = new Thread(Read);
  32. thread_.Start();
  33. }

  34. private void Close(){
  35. isNewMessageReceived_ = false;
  36. isRunning_ = false;

  37. if (thread_ != null && thread_.IsAlive){
  38. thread_.Join();
  39. }

  40. if (serialPort_ != null && serialPort_.IsOpen){
  41. serialPort_.Close();
  42. serialPort_.Dispose();
  43. }
  44. }

  45. private void Read(){
  46. while (isRunning_ && serialPort_ != null && serialPort_.IsOpen){
  47. try{
  48. message_ = serialPort_.ReadLine();
  49. isNewMessageReceived_ = true;
  50. }
  51. catch (System.Exception e){
  52. Debug.LogWarning(e.Message);
  53. }
  54. }
  55. }

  56. public void Write(string message){
  57. try{
  58. serialPort_.Write(message);
  59. }
  60. catch (System.Exception e){
  61. Debug.LogWarning(e.Message);
  62. }
  63. }
  64. }
SerialLight
  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. using UnityEngine.UI;

  5. public class SerialLight : MonoBehaviour{

  6. public SerialHandler serialHandler;
  7. public Text text;

  8. void Start(){
  9. serialHandler.OnDataReceived += OnDataReceived;
  10. }

  11. void Update(){

  12. }

  13. void OnDataReceived(string message){
  14. try{
  15. text.text = message;
  16. }
  17. catch (System.Exception e){
  18. Debug.LogWarning(e.Message);
  19. }
  20. }
  21. }

③プログラムの結び付け
 前項で書いたコードを実際に動くように結び付けていきます。まず。"SerialHandler"を①で作った"Hierarchy"->"Canvas"->"Text"にドラッグアンドドロップします。
 そうしたら"Text"をクリックし"Inspector"で開きます。そうしたら下の方にあるSerial Light (Script)で↓の画像のように設定します。
a

 また、"Hierarchy"->"Creare"->"Create Empty"を行い、"SerialHandler"という空オブジェクトを作成します。そうしたら、その空オブジェクトに②で作った"SerialHandler.cs"をドラッグアンドドロップをします。


④実行する
 Arduinoに書き込み、Unityのスタートボタンを押すと実行され画面に送られてきた数字が表示されます。
アクセスカウンター
  • 今日:
  • 昨日:
  • 累計: