【Unity】’COM〇' does not exist

Unityでシリアル通信を使用するアプリを制作したのちに別PCにビルド後のファイル群を送信して使用を試みたところ,タイトルのようなエラーが出てセンサとの接続が上手くいきませんでした.

 

COM8とプログラム中に設定してあったため,接続をしたのちにデバイスマネージャーからCOM番号を手動で変更していたのが悪かったようです.

接続されたままのCOM番号をプログラム側で設定し,再度ビルドすると適切に動くようになりました.

【Unity】Textオブジェクトの内容を別ファイルから変更する

ソースコード

github.com

やりたいこと

プログラムの進行具合に応じて画面に表示するテキストを変更します.

プログラム

// ジャイロの接続状態
private Text gyroState;
void Start()
{
  Debug.Log("Start");
  this.gyroState = GameObject.Find("gyroState").GetComponent<Text>();
  this.gyroState.text = "Start";
  StartCoroutine("Open");
}

後はお好きな場所で,

this.gyroState.text = "Reconnecting";

などと,テキストの変更を行えば良いです.

【Vue.js】'rm' is not recognized as an internal or external command,

yarn buildをしようとしたときに以下のエラーが出力され,ビルドができなかった.

対処法の一つとしては,ターミナルを変更してみることです.

私の場合,gitbashに変えてyarn buildをするとできました.

 

【Unity】アプリ実行中にセンサとの接続が切れた場合に再起動せずに接続しなおす

背景と今回やること

以下の記事と同じコードを使用しています.

challenge-think.hatenablog.com

Unityで外部センサからの値を使用するアプリを制作しました.

アプリを実行して遊んでいる最中にセンサとの接続が切れて動かなくなった場合,今まではアプリの再起動をするしかありませんでした.

しかし,これは非常に面倒くさかったので,アプリを再起動せずにその場で再起動できるようにしてみました.

上の記事からの変更点はほとんどなく,Updateメソッドのみです.

コード

github.com

void Update()
{
    if(serialStatus == 1){
        if (Input.GetKey(KeyCode.Space)){
            Close();
            StartCoroutine("Open");
        }
    }
}

>今回はスペースキーを押すと接続をし直すように作りました. スペースキーを押すと,以下の内容のCloseメソッドが発火し,一度すべてが切断されます. その後,Openメソッドによって再接続されるという流れです.


private void Close()
{
    isRunning_ = false;

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

    if (serialPort_ != null && serialPort_.IsOpen) {
        serialPort_.Close();
        serialPort_.Dispose();
    }
    Debug.Log("CLOSE!");
}

【Unity】アプリ実行後センサが見つかるまで探し続ける

背景と今回やること

シリアル通信を使用して外部センサの値を読み込むアプリケーションをUnityで製作した際に,アプリ実行直後のタイミングでセンサがつながらなかった場合,アプリを再起動する必要がありました.

それが面倒くさかったため,アプリ実行後センサが接続するまで待機し続けるというプログラムを作成しました.

使用するのは以下と同じプログラムです.

challenge-think.hatenablog.com

コード

github.com

説明なんてどうでもいいという方は以下のコードを参考にして自分なりに改変してください.

// シリアルポート
private SerialPort serialPort_;
private Thread thread_, thread2_;
private bool isRunning_ = false;
// スレッド監視
public int serialStatus = 0;  // 0 : waiting, 1 : success, 2 : failure
private bool thread2_status = false;

void Start()
{
    Debug.Log("Start");
    StartCoroutine("Open");
}

void SerialOpen(){
    thread2_status = true;
    serialStatus = 0;
    serialPort_ = new SerialPort(portName, baudRate, Parity.None, 8, StopBits.One);
    try{
        serialPort_.Open();
    }
    catch (Exception e){
        serialStatus = 2;
    }
    if (serialStatus == 0) serialStatus = 1;
    thread2_status = false;
}

private IEnumerator Open()
{
    serialStatus = 2;
    while (serialStatus != 1){
            Debug.Log("センサ接続トライ中");
        if (serialStatus == 2){
            thread2_ = new Thread(SerialOpen);
            thread2_.Start();
            yield return null;
        }
        while (thread2_status) { yield return null; }
        thread2_.Join();
        yield return null;
    }
    serialPort_.ReadTimeout = 50;
    isRunning_ = true;
    thread_ = new Thread(Read);
    thread_.Start();

    // オフセット角も更新する
    rotate Rotate;
    GameObject obj = GameObject.Find("Main Camera");
    Rotate = obj.GetComponent();
    Rotate.offsetFlag = true;

    // check
    Debug.Log("OPEN!");
}

コードの説明

それでは解説をしていきます.

Step1

まずOpenメソッドの中にあるシリアル通信を確立する部分で接続が確認できなかった場合に処理を止めて最初から戻るという動作ができるようにコルーチンを使用します.


void Start()
{
    Debug.Log("Start");
    StartCoroutine("Open");
}
private IEnumerator Open()
{
    ....
}

Step2

ここで,シリアルポートをつなぐ部分(serialPort.Open())は何度もループする場所なので,わかりやすいように別関数として定義しておきます.

後で使いますが,接続が確立されたのかどうかを判断するために,変数を二つ定義しています.

thread2_statusは,プログラムがこの関数を処理している最中なのか,全て処理が完了しているのかどうかを判断するために使用します.

serialStatusは,今接続がどのような状態かを判断するために使用します.

void SerialOpen(){ // 0 : waiting, 1 : success, 2 : failure
  thread2_status = true; serialStatus = 0; serialPort_ = new SerialPort(portName, baudRate, Parity.None, 8, StopBits.One); try{ serialPort_.Open(); } catch (Exception e){ serialStatus = 2; } if (serialStatus == 0) serialStatus = 1; thread2_status = false; }

Step3

次は,センサを見つけてシリアル通信を確立するまでの間,他のメインスレッドの処理を待たせてしまうことを防ぐために別スレッドとして使用していきます.

以下のwhileループ中の処理としては,シリアル通信が確立できなかった場合は,(1)スレッドを初期化し,(2)初期化した状態で再度接続確立を試みる.(3)1フレーム待機.(4)定義した接続を確立するための関数の処理をすべて実行するまで待つ.(5)一通り終了したらスレッドを消す.という順でループし,接続が確立した場合(serialStatus != 1)ループを抜け出すという形になっています.

 

serialStatus = 2;
while (serialStatus != 1){
        Debug.Log("センサ接続トライ中");
    if (serialStatus == 2){
        thread2_ = new Thread(SerialOpen);(1)
        thread2_.Start();(2)
        yield return null;(3)
    }
    while (thread2_status) { yield return null; }(4)
    thread2_.Join();(5)
    yield return null;
}
最後にコルーチンを使用しているので,Start()の部分をStartCoroutine("Open")
と書き換えることも忘れずに.
以上で終了です.

【Unity】センサ値(ジャイロセンサ,WitMotion)を的確に取得する

背景と今回やること

以下のページで記述したものの改良版になります.

challenge-think.hatenablog.com

上の記事では,センサ値を取得する際にserialPort.Read()メソッドを使用していました.

これは,初めはserialPort.ReadLineメソッドを使用していたのですが,上手くいかずに変更し,使いました.

ReadLine()は改行文字まで一気に読み込んでしまおうというもので,センサからの値の中に改行文字が見つからない場合は永遠に読み込み続けてしまいます.

そのため,Read()関数にして,初めに決めたデータ数だけ格納するという形式をとっていました.

ただ,初めに決めたという部分でもわかる通りセンサからどれくらいの長さのデータが送られてきているのかがあらかじめわかっていなければいけません.また,もしセンサ値の取得を開始する位置がずれてしまうとその後もずっとずれたままデータを読み取り続けることとなります.

こういうことが重なり,接続はできているのに適切なセンサ値を取得できないという問題が生じてしまいます.

以上より今回は,全てのデータを一つずつ丁寧に取得していくというプログラムに変更していきます.

コード

github.com

説明なんかどうでもいいという人は以下をコピペしてください.

前回のReadメソッドを上書きすれば完了です.

※足りない変数の定義は適宜補ってください


// センサ値格納
public double roll=0.0, pitch=0.0, yaw=0.0;
// rawデータから各値抽出
private bool IsGyrodata = false;
private bool gyroFlag = false;
private int countGyrodata = 0;

private void Read()
{
    int rcv = 0;
    int pastrcv = 0;
    int plus       = 0;
    int minus      = 0;
    double preRoll = 0;
    double preYaw  = 0;
    double prePitch= 0;

    while (isRunning_ && serialPort_ != null && serialPort_.IsOpen) {
        try {
            rcv = serialPort_.ReadByte();
        } catch (System.Exception e) {
            Debug.LogWarning(e.Message);
        }

        // 生データからroll,pitch,yawを計算する
        if(IsGyrodata){
            countGyrodata++;
            switch(countGyrodata){
                case 1: 
                    this.roll = rcv; 
                    break;
                case 2: 
                    this.roll += rcv*Math.Pow(2,8); 
                    this.roll = this.roll/32768.0*180;
                    break;
                case 3:
                    this.pitch = rcv;
                    break;
                case 4:
                    this.pitch += rcv*Math.Pow(2,8);
                    this.pitch = this.pitch/32768.0*180;
                    break;
                case 5:
                    this.yaw = rcv;
                    break;
                case 6:
                    this.yaw += rcv*Math.Pow(2,8);
                    this.yaw = this.yaw/32768.0*180;
                    break;
                case 7: 
                    gyroFlag = true;
                    IsGyrodata = false;
                    break;
            }
            // roll,pitch,yawを計算し終えたら各値を加工する
            if(gyroFlag){
                gyroFlag = false;
                countGyrodata = 0;
                // センサ値を[-180,180]に変換する
                if(this.roll > 180){
                    this.roll -= 360;
                }
                // センサ値を[0→90→0],[0→-90→0]に変換する
                if (this.pitch > 180) {
                    this.pitch -= 360;
                }
                // センサ値を[-180,180]に変換する
                // if (yaw > 180) {
                //     yaw -= 360;
                // }

                if(preRoll*this.roll <= 0){
                    if(Mathf.Abs((float)this.roll) > 150){
                        if(preRoll < 0){
                            minus++;
                        }else if(preRoll > 0){
                            plus++;
                        }
                    }
                    preRoll=this.roll;
                }
                this.roll += (plus-minus)*360;

                prePitch = this.pitch;
                preYaw   = this.yaw;

                // Check
                // Debug.Log($"roll:{roll}, pitch:{pitch}, yaw:{yaw}");
            }
        }else{
            // 取り出したい値がある位置を探す
            if(rcv==0x53 && pastrcv==0x55){
                IsGyrodata = true;
                // Debug.Log("Gyrodata is comming!");
            }
            pastrcv = rcv;
        }
    }
}

コードの説明

説明といってもおそらくコードをみたらすぐわかると思います.

先ほど説明をしたReadByteで一つずつセンサ値を読んでいっています.

欲しいロールピッチヨー角のデータの始まりを検出し


// 取り出したい値がある位置を探す
if(rcv==0x53 && pastrcv==0x55){
    IsGyrodata = true;
    // Debug.Log("Gyrodata is comming!");
}

そこからSWITCH分で各値を計算していきます.

最後にセンサの値を加工して使いやすくしています.


// roll,pitch,yawを計算し終えたら各値を加工する
if(gyroFlag){
    gyroFlag = false;
    countGyrodata = 0;
    // センサ値を[-180,180]に変換する
    if(this.roll > 180){
        this.roll -= 360;
    }
    // センサ値を[0→90→0],[0→-90→0]に変換する
    if (this.pitch > 180) {
        this.pitch -= 360;
    }
    // センサ値を[-180,180]に変換する
    // if (yaw > 180) {
    //     yaw -= 360;
    // }
    if(preRoll*this.roll <= 0){
        if(Mathf.Abs((float)this.roll) > 150){
            if(preRoll < 0){
                minus++;
            }else if(preRoll > 0){
                plus++;
            }
        }
        preRoll=this.roll;
    }
    this.roll += (plus-minus)*360;
    prePitch = this.pitch;
    preYaw   = this.yaw;
}

以上です.

【PHP】aタグ(href)を用いた遷移とform submitを同時に行いたい場合

 

<a href="#">
    <form method="post">
        <input type="hidden" name="test" value="1">
        <input type="submit">
    </form>
</a>


例えば,上記のようなコードを書いた場合,送信ボタンが現れそのボタン以外の部分をクリックし別ページに遷移するとformで送信したい値(value)を送信できません.

※submitではなく,単なるhrefの遷移であるため.

そのため,今回はaタグのhrefによる遷移とsubmitの操作を一緒に行えるようにしたいと思います.

 

ただ,やることは簡単で,以下の様に書き直すだけです.

    <form method="post">
    <a href="#" onclick="document.forms[0].submit(); return false;">
        <input type="hidden" name="test" value="1">
        <input type="submit">
    </a>
    </form>

ここで,もしformをfor文やforeach文によって繰り返し表示させている場合,form[num]のnumを繰り返しに合わせて変更しなければなりません. これに関しては,for文ならfor($i=0;$i<max;$i++)のiを入れてform[$i]とし,foreach文ならforeach($dataList as $key=>$data)の$keyを使用してform($key)とすればよいです.