おもちゃラボ

Unityで遊びを作ってます

【Unity】Arduinoを使ってiOS・Android用の無線コントローラを作る

iOSやAndroidと通信するコントローラをつくるにはBLEを使うのが定番のようですが、Unityを使う場合、下回りがややこしくなりがちです。また、BLEモジュールも安くはない(4〜5000円)ので気軽に実験、というわけにはいきません。

www.switch-science.com

そこで、この記事ではBLEではなくインターネット無線通信(UDP通信)を使ってスマートフォンとコントローラを接続する方法を紹介したいと思います。

f:id:nn_hokuson:20171106195221j:plain

UDP通信をするにはArduinoと無線通信モジュールであるESP8266(EPS-WROOM-02)が必要になります。また、コントローラからUDP通信を使って送られてくるデータの受け側(サーバ側)はUnityを使います。構成としては次のような感じです。

f:id:nn_hokuson:20171104211453p:plain:w450

今回のコンテンツは次のとおりです。まずは無線モジュールのESP-WROOM-02のセットアップから始めましょう。

ESP-WROOM-02のセットアップ

ESP-WROOM-02を動かすためには、通信速度の設定など、いくつかの作業が必要になります。前回の記事にまとめているので、こちらを参考にして下さい。

nn-hokuson.hatenablog.com

手順としては「ESP8266のライブラリをインストール」のステップまで終わらせて下さい。

上の記事ではTCPを使ってPCとESP8266を接続しています。ただ、Unityなどで使うゲームコントローラを作る場合、シビアな通信速度が求められるため、TCPではなくUDPを使うことになります。TCPとUDPの違いは、次のサイトが参考になります。

gihyo.jp

そこで、今回はESP8266とUnityの間でUDP通信するようなシステムを作っていきます。

無線の送信回路を作る

今回はコントローラ側(Arduino側)のボタンを押すと、その情報が無線経由で送信され、Unityでその情報を受け取るという構成です。

ESP8266を使った回路図は次のようになります。前回の回路に追加する形で4番ポートにボタンを接続しています。プルアップ抵抗はArduino側でソフトから指定出来るので不要です。

f:id:nn_hokuson:20171104203931j:plain:w500

ちなみに・・・この回路図はTinkercadというサイトを使って作っています。もともとはAutodeskの「123d circuits」だったものが進化(?)してTinkercadになったようです。回路図作成だけでなく、シミュレーションもできるのがすごい。

www.tinkercad.com

ArduinoでUDP送信プログラムを作る

ESP-WROOM-02を使った基本的な無線通信プログラムは前回と同じです。ESP8266用ライブラリのインストールなどは、前回の記事を参考にして下さい。

次のプログラムを作成し、Arduinoに書き込んで下さい。

#include "ESP8266.h"
#include <SoftwareSerial.h>

// Wi-Fi SSID
#define SSID "Buffalo-G-xxxx"
#define PASSWORD "xxxxxxxxxxxxxx"

#define HOST_NAME "192.168.11.23"
#define HOST_PORT   5432
 
SoftwareSerial mySerial(2, 3);
ESP8266 wifi(mySerial);

/**
 * 初期設定
 */
void setup(void)
{
  pinMode(4, INPUT_PULLUP);
  Serial.begin(9600);

  if (wifi.setOprToStationSoftAP()) {
    Serial.println("to station ok");
  } else {
    Serial.println("to station error");
  }

  if (wifi.joinAP(SSID, PASSWORD)) {
    Serial.println("connect success");
  } else {
    Serial.println("connect error");
  }

  if (wifi.disableMUX()) {
    Serial.println("disable mux success");
  } else {
    Serial.println("disable mux error");
  } 

  if (wifi.registerUDP(HOST_NAME, HOST_PORT)) {
      Serial.print("register udp ok\r\n");
  } else {
      Serial.print("register udp err\r\n");
  }   
}

void loop(void)
{
    int val = digitalRead(4); 

    char message[8];
    message[0] = ( val == LOW ) ? '0' : '1';
    
    wifi.send((const uint8_t*)message, strlen(message));
    delay(33);
}

このプログラムではUDP通信をするためにregisterUDP関数を使っています。setup関数の中でUDP通信の準備をし、loop関数の中でHOST_NAMEで指定したIPアドレスのHOST_PORTに向けて毎回データを送信しています。

送信するデータはボタンが押されている場合は「0」、ボタンが押されていない場合は「1」を送信しています。UDP通信で送信するデータは文字列なので、ボタン数が増えたときは良い感じに圧縮して送ればOKです。

これで、ESP-WROOM-02とUnityでUDP通信する仕組みのうち、ハード側の部分が完成しました。次はUnityでデータを受け取る部分を作っていきます。

UnityでUDP通信のデータを受信する

ArduinoからUDP通信で送られてくるデータを受け取るため、Unity上にUDPサーバを作成します。

プロジェクトウィンドウで右クリックして「Create」→「C# Script」を選択し、UDPServerという名前で保存して下さい。

保存できたらUDPServerに次のプログラムを入力して下さい。

using UnityEngine;
using System.Collections;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;

public class UDPServer : MonoBehaviour
{
    int LOCA_LPORT = 5432;
    static UdpClient udp;
    Thread thread;
    public static string message = "";

    void Start ()
    {
        udp = new UdpClient(LOCA_LPORT);
        thread = new Thread(new ThreadStart(ThreadMethod));
        thread.Start(); 
    }

    void OnApplicationQuit()
    {
        thread.Abort();
    }

    private static void ThreadMethod()
    {
        while(true)
        {
            IPEndPoint remoteEP = null;
            byte[] data = udp.Receive(ref remoteEP);
            string text = Encoding.ASCII.GetString(data);
            message = text;
            Debug.Log(text);
        }
    } 
}

UnityでUDPサーバを作る方法はこちらの記事を参考にさせていただきました。

qiita.com

スレッドの中でUDPで送られてくるデータを監視しておき、データが来た場合は文字列型に変換してmessage変数に代入しています。

スクリプトを保存できたら、空のオブジェクトにスクリプトをアタッチしてから実行してみて下さい。

f:id:nn_hokuson:20171104212620j:plain

コンソールに「0」のデータが表示されると思います。Arduino側のボタンを押すとコンソールの表示も「1」になります。

f:id:nn_hokuson:20171104213059g:plain

無線コントローラのボタンが押されたらUIを書き換える

最後はおまけです(笑)送られてきたデータはUDPServerクラスのstatic変数(message)に代入しているので、他のクラスから使いたい場合はUDPServer.messageでアクセスできます。

例えば、Arduino側のボタンを押したときに、UnityでUIを書き換えたい場合は、次のようなプログラムを作成して下さい。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class MessageController : MonoBehaviour {

    public Text message;

    void Update () 
    {        
        if (UDPTest.message.Length != 0)
        {
            message.text = (UDPTest.message [0] == '1') ? "OFF" : "ON";
        }
    }
}

Text変数にはインスペクタからUIのTextを指定します。Updateメソッドの中ではメッセージが来ているか(長さが0ではないか)を確認し、そのデータをもとに表示する文字列を切り替えています。

MessageControllerスクリプトが作成できたらcanvasオブジェクトなどにアタッチして、インスペクタからMessage変数の欄にTextオブジェクトをドラッグ&ドロップします。

f:id:nn_hokuson:20171104213847j:plain

実行すると次のようにボタンのON/OFFによってUnityの表示も変化します。

www.youtube.com

まとめ

今回はESP-WROOM-02(ESP8266)を使ってUnityとUDP通信する方法を紹介しました。Unityで使う無線コントローラを作りたい場合には、役に立つ・・・ような気がします。

SWITCHの映像がTVモードで途切れるときに試すべき5つのこと

SWITCHをテレビにつないでTVモードで遊んでいると、画面が真っ暗になったり、一瞬だけ暗くなってすぐにゲーム画面に戻ることが良くありました。うちの場合はSHARPのAQUOSを使っていて、この映像が途切れる現象が頻発していました。

f:id:nn_hokuson:20171031194744j:plain

そこで、この映像が途切れる問題を改善するために試した5つのことをまとめておきます。ちなみに・・・うちの場合は「HDMIケーブルの交換」が有効でした。

ドッグに本体がちゃんと差し込まれているかを確認する

ドッグとSWITCHの接触不良で映像が途切れることがあるようです。しっかりとSWITCH本体がドッグに差し込まれているかを確認してみましょう。

無線LANルータなど電波環境の悪いところには置かない

無線LANルータや電子レンジなど、比較的強い電波を発するモノの近くにSWITCHのドッグを置くとHDMIの信号線にノイズが乗って映像が乱れることがあるようです。これらの機器からは少し距離を離してドッグを設置してみましょう。

f:id:nn_hokuson:20171029115346p:plain:w120

HDMIケーブルを交換してみる

SWITCHに標準で付いてくるHDMIケーブルはフェライトコアの付いていないHDMIケーブルです。フェライトコアとはHDMIケーブルの先端付近についている円柱状のもので、これがあることで、HDMIの信号に乗るノイズをカットすることができます。
うちでは、このフェライトコア付きのHDMIケーブルを使うことで映像が途切れる現象が全くなくなりました。

映像の解像度を変更してみる

SWITCH本体の「設定」⇢「テレビ出力」⇢「テレビの解像度」から解像度を変更してみてください。オーバースペックな解像度を使っていた場合、SWITCHの処理が追いつかずに映像が途切れることがあるようです。

f:id:nn_hokuson:20171029113954j:plain

別のテレビでも映像が途切れるか試してみる

現在使用しているテレビとは別にテレビやモニタがをお持ちの場合は、そちらを使って遊んでみましょう。別のテレビでは映像が途切なかった場合、ご使用中のテレビとSWITCHの相性が悪いことが考えられます。SHARPのAQUOSとは相性が悪いことがあるようです。

逆に、どのテレビでも映像が途切れる場合にはSWITCH本体やSWITCHドッグの初期不良が考えられます。任天堂のカスタマーセンターに連絡してみるのが良いかもしれません。

www.nintendo.co.jp

【Unity】パーティクルに複数の種類のテクスチャを使う

Unityのパーティクルには、テクスチャだけでなく3Dモデルも指定できるようになっています。

tsubakit1.hateblo.jp

3Dモデルであれば、1つのParticle Systemで複数種類のパーティクルを発生させる事ができますが、テクスチャの場合は基本的に1種類しか指定できないようになっています。

つまり、円なら円のパーティクル、星なら星のパーティクルというように、何種類もテクスチャを混ぜたパーティクルが作れないのです。

(↓普通のパーティクル・・・)
f:id:nn_hokuson:20171024195041g:plain

といっても、複数種類のパーティクルが全く作れないわけではなく、すこしトリッキーですが、複数種類のテクスチャを混ぜたパーティクルを作る方法がUnityには用意されています。

ここではその方法を紹介したいと思います。

テクスチャを用意する

複数種類のパーティクルテクスチャを使うにはテクスチャアニメーションに使う仕組みを利用します。まずは使いたいテクスチャを縦に並べた1枚の大きなテクスチャを作成して下さい。

f:id:nn_hokuson:20171024195633p:plain:w100

上の図で細い枠線は分かりやすいように入れていますが、実際には不要なので、Photoshopなどで背景は透明にしておいて下さい。

Unityでパーティクルを作成する

作成したテクスチャをUnityのプロジェクトウィンドウにドラッグ&ドロップして下さい。背景を透過する場合は、テクスチャのインスペクタで「Alpha Is Transparency」にチェックを入れておきます。

f:id:nn_hokuson:20171024200620j:plain:w280

次にテクスチャに対応するマテリアルを作成します。プロジェクトウィンドウで右クリックし、「Create」→「Material」を選択し、マテリアルを作成して、いま登録したテクスチャを指定して下さい。

マテリアルのShaderは「Particles/Additive」とか「Unlit/Transparent」など、お好みのものを選択しておきます。

f:id:nn_hokuson:20171024200819p:plain:w280

複数テクスチャを指定するパーティクルの設定

最後にParticle Systemのインスペクタで、パーティクルに複数のテクスチャを指定できるように設定します。Particle Systemの「Texture Sheet Animation」の項目を次のように設定して下さい。

項目 設定値
Tiles X=1、Y=テクスチャの種類数
Animation Single Row
Random Row オン

また、「Renderer」のMaterialに上で作成したマテリアルを指定します。設定後、Particle Systemのインスペクタは次のようになりました。

f:id:nn_hokuson:20171024201404j:plain:w280

実行してみる

上のように設定後、実行してみて下さい。次のような感じで、1つのParticle Systemを使って複数種類のテクスチャを表示できました。

f:id:nn_hokuson:20171024201810g:plain

このように、Texture Sheet Animationを使うことで、複数の種類のパーティクルを発生させることができます。覚えておくと使える・・・かもです!