Arduinoその他工作

Arduinoと加速度センサで作るデジタルボール転がし迷路 【後編】

Arduinoを使ったユニークな電子工作を前編、後編の2回に分けてご紹介しているこの企画。この興味深い電子工作を紹介してくれるのは、物事の関係性をテーマに活動するアーティストである平原真さん。大阪芸術大学准教授でもある平原さんは、これまでもコンピュータや電子デバイスを使ったメディアアートを多数制作されており、Device Plusでも「Arduinoを使ったソーラーパネルで動くデジタル飼育箱」や「ArduinoとTOF距離センサでつくるドーナツプレーヤ」といったセンス溢れる電子工作を制作してくれています。今回はデジタルボール転がし迷路の完成までを見ていきたいと思います。

 

はじめに

みなさん、こんにちは!平原です。「Arduinoと加速度センサで作るデジタルボール転がし迷路」の後編になります。前編では電子回路を組み立て、Arduinoのプログラムを書き込んで、電子部品の動作確認までおこないました。これからいよいよ迷路を制作し、ゲームエンジンUnityで実際にゲームを作っていきます。ぜひ皆さんもチャレンジしてください!

前編をまだ見ていない方は、こちらの動画をぜひご覧ください。

 

記事の構成について

この記事は次の6つのパートに分かれています。
前編では作品のプラン、電子回路、Arduinoのプログラムを作成しました。
後編は次の1~3の順番で制作を進めていきたいと思います。

  1. 筐体の作成
  2. 迷路の3Dモデルの作成
  3. Unityのプログラム

 

1. 筐体の作成

迷路の設計は、ゲーム性を決める重要なポイントです。ボールがどんなふうに転がるか想像しながら迷路を作りましょう。

筐体の作成は次の手順で行います。

  1. 箱の設計
  2. 迷路の設計
  3. 材料の切り出し
  4. 箱と迷路の組み立て
  5. 電子部品の固定
  6. マーカーの貼り付け

 

arduino-digital-ball-maze-02_01

 

手順1:箱の設計

箱は5枚のシナベニヤの板を張り合わせて作ります。箱の大きさは、中に入れる部品に余裕をもたせて、外寸で横145mm、幅145mm、高さ70mmとします。板の厚みは4mmです。角が組み合うように厚みを考慮して設計します。背面の板にはUSBケーブルを通す穴を空けます。Illustratorを使って、切断する形状を設計してください。配布ファイルのCutBox.aiを参照してください。

>> 配布ファイルのダウンロード(50.4MB)

arduino-digital-ball-maze-02_02

 

手順2:迷路の設計

今回の迷路は、正方形の盤面を10マス x 10マスに区切って壁を立てます。左下角をスタート、右上角をゴールとします。難易度の違う3種類の迷路を作ります。なお、ここで作った迷路データは、レーザ加工機での切断用データとして使うだけでなく、Unityで使用する3Dモデルを作成するためにも使用します。

Illustratorで正方形を10マス x 10マスに区切って、壁を描いていきます。グリッド機能を使うと便利です。ボールを転がす事を想像しながら迷路を考えてみましょう。最初から作るのが面倒な場合は、下記のような迷路の自動生成を参考にしてもいいでしょう。

>>迷路自動作成

完成したら、線の太さを調整し、全体をアウトライン化したあとで、145mm x 145mmに収まるように大きさを調整してください。

arduino-digital-ball-maze-02_03

 

箱の底面と同じ形状が迷路の床です。床と壁を貼り合わせて1つの迷路になります。スタート(S)とゴール(G)は彫刻します。

3段階の難易度の迷路を作ります。レベル1は特に仕掛けはありません(左)。レベル2はスタートとゴールがつながっておらずそのままではたどり着けませんが、途中の穴に落ちると反対側にワープします(中)。レベル3は中央が丸くくり抜かれ、その部分が回転ドアのように動きます(右)。配布ファイルのCutMaze.aiを参照してください。

arduino-digital-ball-maze-02_04

 

手順3:材料の切り出し

作成したデータを使ってレーザ加工機でシナベニヤを切断してください。シナベニヤの厚みは4mmです。使用する機器に合わせて、切断に必要な設定をおこなってください。

arduino-digital-ball-maze-02_05

 

迷路を切断したところ。迷路の壁は細いので折れないように慎重に扱ってください。

arduino-digital-ball-maze-02_06

arduino-digital-ball-maze-02_07

 

手順4:箱と迷路の組み立て

箱を木工用ボンドで貼り合わせます。ボンドは少量でもしっかり固定されます。付けすぎないようにしましょう。

arduino-digital-ball-maze-02_08

 

同じように迷路の床と壁を貼り合わせます。迷路の壁の裏側に、爪楊枝などでボンドを少量ずつ付け、床と壁の位置を合わせて貼り付けてください。裏表やスタートとゴールの位置を間違わないように注意しましょう。

arduino-digital-ball-maze-02_09

 

手順5:電子部品の固定

フォトリフレクタが、蓋の裏側に接する高さにするため、28mm底上げするブレッドボードの台座を3Dプリンタで作ります。配布ファイルのBreadboadHolder.stlを使用してください。丁度いい高さの箱などがあればそれで代用しても構いません。

arduino-digital-ball-maze-02_10

 

ブレッドボードをホルダに嵌め込み、ホルダとArduino UNOを両面テープで箱に固定します。

arduino-digital-ball-maze-02_11

 

手順6:マーカーの貼り付け

フォトリフレクタで読み取るためのマーカーを作ります。30mm x 30mm の四角形を2個並べた図をシール用紙にプリントしてください。インクジェットプリンタのインクでは、フォトリフレクタが反応しないため、黒いペンで白黒、白黒、黒黒となるように塗りつぶしてください。

arduino-digital-ball-maze-02_12

 

蓋の裏画のフォトリフレクタの位置にマーカーを貼ります。下の写真の左からレベル1、2、3です。レベル1はフォトリフレクタ1が黒、フォトリフレクタ2が白となります。白と黒の組み合わせでレベルの違いを判定します。

arduino-digital-ball-maze-02_13

 

2. 迷路の3Dモデルの作成

迷路の3Dモデルを作るには次の手順でおこないます。

  1. IllustratorからSVGファイルを出力
  2. Fusion360でSVGファイルを読み込み
  3. 押し出しで立体化
  4. OBJファイルを出力

 

手順1:IllustratorからSVGファイルを出力

Illustratorから迷路のデータをSVGファイルとして出力します。「1.筐体の作成」の手順2で作成した迷路のアウトライン化したデータを中心が座標0,0となるように移動してください。Illustratorのメニューから[ファイル] > [別名で保存] を選択し、ファイルの種類でSVGを選択してください。レベル1~3まで3つのファイルを作ってください。

arduino-digital-ball-maze-02_14

 

手順2:Fusion360でSVGファイルを読み込み

3DCADソフトFusion360に、手順1で出力したSVGファイルを読み込みます。Fusion360のインストール方法は公式サイトを参照してください。

>> Fusion360

Fusio360のメニューから、[挿入] > [SVGの挿入]を選択し、手順1で書き出したSVGファイルを読み込んでください。読み込んだ形状はスケッチになります。

arduino-digital-ball-maze-02_15

 

手順3:押し出しで立体化

迷路の形状を押し出して立体化します。スケッチ全体を下方向に5mm押し出して床にしてください。次に壁部分だけを上方向に5mm押し出してください。

arduino-digital-ball-maze-02_16

 

レベル2は床にワープの穴を開けてください。穴の大きさはΦ8mmくらいです。
レベル3は、中央をΦ48mmの円でくり抜き、2つのボディを作成してください。

 

手順4:OBJファイルを出力

Fusion360からOBJファイルとして出力します。Fusion360のメニューから[ファイル] > [エクスポート] を選択し、ファイルのタイプをOBJとして書き出してください。処理がクラウドで行われるので少し時間がかかります。完了するとダウンロードフォルダにファイルが入ります。配布ファイルのMazeLv1.obj、MazeLv2.obj、MazeLv3-A.obj、MazeLv3-B.objが、完成したデータです。

arduino-digital-ball-maze-02_17

 

3. Unityのプログラム

    いよいよUnityでのプログラムです。次の手順でおこないます。

  1. Unityの新規プロジェクト作成
  2. 3Dモデルの読み込み
  3. ボールの配置
  4. 物理演算の適用
  5. Serial Port Utility Pro のインストール
  6. メインプログラム
  7. ゴールの設置
  8. レベル2のギミック
  9. レベル3のギミック
  10. 迷路の切り替え

 

手順1:Unityの新規プロジェクト作成

今回使用するUnityのバージョンは、2019.4.8f1のPersonalです。Unityのインストール方法は公式サイトを参照してください。

>> Unity

Unityの新しいプロジェクトを3Dモードで作成してください。

arduino-digital-ball-maze-02_18

 

手順2:3Dモデルの読み込み

迷路の3Dモデルの読み込みをおこないます。Fusio360から書き出したOBJファイルをUnityのプロジェクトパネルのAssetフォルダにドラッグしてください。

迷路をまとめるための空のゲームオブジェクトを作成します。Unityのメニューから、[ゲームオブジェクト] > [空のオブジェクトを作成]を選択してください。 新しくできたゲームオブジェクトの名前を「MazeObject」に変更し、Transform の位置を0にしてください。

3Dモデルをステージに配置します。先程読み込んだ地図の3Dモデル「MazeLv1」を、ヒエラルキーパネルの「MazeObject」までドラッグしてください。そのままだと大きさと回転がおかしいので、正しい位置になるように調整します。インスペクタパネルのTransformから回転をX270、Y180、拡大縮小を0.2にしてください。レベル2の3Dモデルも同様に配置してください。レベル3は「MazeLV3-A」と「MazeLV3-B」2つの3Dモデルからできているので、「MazeLv3」という名前の空のゲームオブジェクトを作り、その中に2つの3Dモデルを入れてください。

arduino-digital-ball-maze-02_20

 

レベルごとに色を変えます。Unityのメニューから[アセット] > [作成] > [マテリアル] を選び、マテリアルを3つ作成して ください。名前は、「MatLv1」「MatLv2」「MatLv3」とします。マテリアルのインスペクタパネルから、アルベドの項目で色を変えてください。「MatLv1」は水色、「MatLv2」は黄色、「MatLv3」はオレンジにします。作成したマテリアルを「MazeLv1」「MazeLv2」「MazeLv3-A」「MazeLv3-B」の中のメッシュにドラッグしてください。迷路に色が付きます。

arduino-digital-ball-maze-02_21

 

現在は3つの迷路が重なって表示されていますが、「MazeLv2」と「MazeLv3」は初期状態で非アクティブにします。インスペクタパネルの名前の左のチェックを外してください。

 

手順3:ボールの配置

スタート地点にボールを配置します。Unityのメニューから、[ゲームオブジェクト] > [3Dオブジェクト] > [スフィア]を選択してください。オブジェクトの名前は「Sphere」です。真上から見下ろす視点に変更すると位置を合わせやすいです。空中から落下させたいので、yは5.0くらいにします。

arduino-digital-ball-maze-02_22

 

手順4:物理演算の適用

3Dモデルに衝突判定をする機能を付与します。「MazeObject」の中の「MazeLv1」を選択して、Unityのメニューから[コンポーネント] > [物理] > [リジッドボディ]を選択してください。リジッドボディの設定項目から、Use Gravity のチェックを外し、Is Kinematicにチェックを入れてください。衝突判定の項目は「衝突判定を連続的かつ動的」を選択してください。壁のすり抜けを軽減します。次にUnityのメニューから[コンポーネント] > [物理] > [メッシュコライダー]を選択してください。メッシュコライダーの設定項目のメッシュは、「MazeLv1」に含まれるメッシュを選択してください。

「MazeLv2」「MazeLv3-A」「MazeLv3-B」にもリジッドボディとメッシュコライダーをアタッチして、同様の設定をおこなってください。

arduino-digital-ball-maze-02_23

 

最後にボールにもリジッドボディをアタッチします。「Sphere」を選択し、Unityのメニューから[コンポーネント] > [物理] > [リジッドボディ]を選択してください。「Sphere」は、重力に従って動いてほしいので、UseGravityのチェックは入ったままです。

この段階で実行すると、ボールが落下して迷路に衝突するはずです。動作確認のため、試してみましょう。

arduino-digital-ball-maze-02_24

 

手順5:Serial Port Utility Pro のインストール

UnityとArduinoの通信は、標準の機能だけでも実現できますが、今回はSerial Port Utility Pro(SPUP)というアセットを利用します。SPUPを使うと実行環境の違いを気にすることなく手軽に通信することができます。アセットストアでの販売価格は$79ですが、公式サイトから試用版をダウンロードすることができます。試用版は、通信量が一定上になるとUnityの再起動が必要になります。開発中は試用版を使い、長時間動作させ続けたい場合に購入するといいでしょう。

>> Serial Port Utility Pro

使用版をダウンロードしたら、ZIPファイルを展開してください。Unityのメニューから、[アセット] > [パッケージをインポート] > [カスタムパッケージ] を選び、ZIPファイルに含まれるspup_v2_free.unitypackageを選択してください。次に表示されるウィンドウで、パッケージの全てのファイルを選びインポートしてください。

次に空(から)のゲームオブジェクトを作りSPUPコンポーネントをアタッチします。Unityのメニューから、[ゲームオブジェクト] > [空のオブジェクトを作成]を選択してください。 新しくできたゲームオブジェクトの名前を「SPUPObject」に変更してください。「SPUPObject」を選択した状態で、Unityのメニューから、[コンポーネント] > [SerialPort] > [SerialPort Utility Pro]を選択して、コンポーネントをアタッチしてください。

arduino-digital-ball-maze-02_25

 

Arduino UNOを接続する設定を行います。Arduino UNOをUSBケーブルでPCと接続した状態で、SerialPortUtilityPro の「Show the devices connected to this PC」と書いてある黄色いボタンを押してください。別ウィンドウで開いたボタンを押すと、自動的にVenderIDとProductID、Serial Numberが入力されます。通信速度をArduinoのプログラムで設定した値と合わせます。BaudRateのプルダウンから57600bpsを選択してください。Arduinoから送られたデータの受け取り方を設定します。Read Protocol を Line Feed Data To String に設定してください。

arduino-digital-ball-maze-02_27

 

手順6:メインプログラム

SPUPを使ってシリアル通信をおこない、加速度センサモジュールの値に応じて迷路を動かすプログラムを作ります。今回のプロジェクトの中心的なプログラムです。

Unityのメニューから[アセット] > [作成] > [C#スクリプト]を選び、C#ファイルを作成し、名前を「MazeScript 」としてください。C#ファイルをダブルクリックしてエディタを開き、下記のコードを記入してください。

MazeScript.cs

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

public class MazeScript : MonoBehaviour
{
    public SerialPortUtility.SerialPortUtilityPro serialPort = null;

    Vector3 accSensor;//加速度センサー
    int photoRef1 = 0;//フォトリフレクタ1
    int photoRef2 = 0;//フォトリフレクタ2
    int threshold = 700;//フォトリフレクタのしきい値
    int mazeType = 0;//0:無い

    public GameObject mazeLv1;//迷路Lv1のゲームオブジェクト
    public GameObject mazeLv2;//迷路Lv2のゲームオブジェクト
    public GameObject mazeLv3;//迷路Lv3のゲームオブジェクト
    public GameObject ball;//ボールのゲームオブジェクト
    public GameObject goalText;//ゴールの表示
    public Vector3 cameraPosOffset;//ボールとカメラの距離

    Vector3 ballDefaultPos;

    void Start ()
    {
        ballDefaultPos = ball.transform.position;//ボールの初期位置を記録
        reset();
    }

    void Update ()
    {
        //カメラ
        Camera.main.transform.position = ball.transform.position + cameraPosOffset;
        Camera.main.transform.LookAt(ball.transform.position);

        // 回転
        Vector3 rotationEuler = new Vector3(Mathf.Atan2(accSensor.x, accSensor.z) / Mathf.PI *180,
                                    0,
                                    Mathf.Atan2(accSensor.y, accSensor.z) / Mathf.PI *180);
        Quaternion rotation = Quaternion.Euler(rotationEuler);
        gameObject.transform.rotation = Quaternion.Lerp(gameObject.transform.rotation,rotation, 0.1f);

        // リセット
        if(Input.GetKeyDown(KeyCode.Space))
        {
            reset();
        }
        
    }

    public void reset()
    {
        setMaze();//迷路の初期化

        ball.transform.position = ballDefaultPos;
        ball.GetComponent<Rigidbody>().velocity = Vector3.zero;
        ball.GetComponent<Rigidbody>().angularVelocity = Vector3.zero;
        
        goalText.SetActive(false);

        stopVibe();//振動モーター停止
    }

    public void setMaze()
    {
        Debug.Log("photoRef1:" + photoRef1 +"  photoRef2:" + photoRef2);

        if( (photoRef1 == 0) && (photoRef2 == 0))
        {
            //値が0の場合何もしない
            return;
        }else if( (photoRef1 < threshold) && (photoRef2 > threshold) )
        {
            //レベル1
            mazeType = 1;
            mazeLv1.SetActive(true);
            mazeLv2.SetActive(false);
            mazeLv3.SetActive(false);
        }else if( (photoRef1 > threshold) && (photoRef2 < threshold) )
        {
            //レベル2
            mazeType = 2;
            mazeLv1.SetActive(false);
            mazeLv2.SetActive(true);
            mazeLv3.SetActive(false);
        }else if( (photoRef1 < threshold) && (photoRef2 < threshold) )
        {
            //レベル3
            mazeType = 3;
            mazeLv1.SetActive(false);
            mazeLv2.SetActive(false);
            mazeLv3.SetActive(true);
        }
    }

    public void ReadComplete(object data)
    {
        var text = data as List<string>;
        if(text.Count != 5) return;
        accSensor = new Vector3( float.Parse(text[0]), float.Parse(text[1]), float.Parse(text[2]));
        photoRef1 = int.Parse(text[3]);
        photoRef2 = int.Parse(text[4]);
    }

    public void startVibe()
    {
        //振動モーターを動かすコマンド
        serialPort.WriteCRLF("start");
    }

    public void stopVibe()
    {
        //振動モーターを動かすコマンド
        serialPort.WriteCRLF("stop");
    }

}

 

「MazeScript」を「MazeObject」にドラッグしてください。

「MazeObject」のインスペクタパネルから、「MazeScript」の設定をおこないます。Serial Port には、「SPUPObject」をドラッグしてください。「Maze Lv1、Lv2、Lv3」の項目には「MazeObject」の中の「MazeLv1、Lv2、Lv3」をそれぞれドラッグしてください。Ballには「Sphere」をドラッグしてください。Goal Textは、後で設定をおこなうので今は空欄にしておきます。Camera Pos Offsetは、カメラとボールをどれくらい距離を取るかの設定です。x 0、y 5、z -3と入力してください。

arduino-digital-ball-maze-02_26

 

「SPUPObject」のインスペクタパネルのSerialPortUtilityProの設定に、Read Complete Event Object (Object)という項目があります。シリアル通信を受信した時に呼び出す関数を指定する設定です。「MazeScript」のReadComplete関数をしています。

項目の右下のプラスアイコンをクリックしてください。「なし(オブジェクト)」の欄にMaze Objectをドラッグしてください。右上のNofunctionとなっているプルダウンをクリックして、[MazeScript] > [ReadComplete]を 選択してください。

arduino-digital-ball-maze-02_28

 

ここで実行すると、Arduinoから加速度センサモジュールのデータを受信し、迷路が動くようになったはずです。動作確認のために試してみましょう。ここまでくれば、あと一息です!

arduino-digital-ball-maze-02_29

 

手順7:ゴールの設置

ゴールにたどり着いた時に反応する仕掛けを作ります。ゴールに当たり判定用の透明なオブジェクト(トリガー)を設置し、ボールがトリガーと接触したら、テキストオブジェクトを表示します。

Unityのメニューから[ゲームオブジェクト] > [3Dオブジェクト] > [キューブ]を選んで、立方体を作ってください。名前を「GoalTrigger」に変更し、ヒエラルキーパネルで「MazeObject」の中に配置してください。地図の右上のゴール付近に移動し、拡大縮小で大きさを調整してください。Mesh Rendererのチェックを外し、非表示にしてください。BoxCliderの「トリガーにする」のチェックを入れてください。

arduino-digital-ball-maze-02_30

 

テキストオブジェクトを配置します。Unityのメニューから[ゲームオブジェクト] > [3Dオブジェクト] > [3DText]を選んでください。名前を「GoalText」に変更し、ヒエラルキーパネルで「MazeObject」の中に配置してください。TransformのXを90度回転させるとHelloWorldという文字が見えると思います。インスペクタパネルのテキストの項目に「GOAL!!!」と入力してください。Transformの位置を変更しゴール付近に移動し、拡大縮小0.2に設定し、フォントサイズを100にします。

arduino-digital-ball-maze-02_31

 

ゴールの判定をするためのプログラムをGoalObjectにアタッチします。
Unityのメニューから[アセット] > [作成] > [C#スクリプト]を選び、C#ファイルを作成し、名前を「GoalScript 」にしてください。C#ファイルをダブルクリックしてエディタを開き、下記のコードを記入してください。

GoalScript.cs

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

public class GoalScript : MonoBehaviour
{
    public GameObject goalText;
    MazeScript MazeScript;

    void Start()
    {
        MazeScript = transform.root.GetComponent<MazeScript>();
    }

    void OnTriggerEnter(Collider other)
    {
        //接触した時に呼ばれるイベント
        if(other.name == "Sphere"){//名前がShpereだったら、
            goalText.SetActive(true);//ゴールテキストを表示
            MazeScript.startVibe();//振動モーターを動かすコマンドを送る
        }
    }
}

「GoalScript 」を「GoalTrigger」にドラッグし、「GoalText」をGoal ScriptのGoal Textの項目にドラッグしてください。

「MazeObject」のインスペクタパネルから、MazeScriptのGoalTextの欄に「GoalText」をドラッグしてください。

ここで実行すると、ゴールした時に「GOAL!!!」というメッセージが表示され、振動モータが震えるはずです。振動を止めるにはスペースキーを押して、ゲームをリセットしてください。やっとゲームらしくなってきましたね!

arduino-digital-ball-maze-02_32

 

手順8:レベル2のギミック

レベル2の迷路にワープの機能を加えます。床に開いた穴の下にトリガーを置き、ワープ先のオブジェクトの座標にボールを移動します。

まず、「MazeLv1」を非アクティブにして、「MazeLv2」をアクティブにしてください。ゴールの当たり判定を作ったのと同じように、キューブを作成し床の穴の下に置き、BoxClider の「トリガーにする」にチェックを入れてください。オブジェクトの名前は「WarpIn1」「WarpIn2」とします。
次にワープ先を指定するオブジェクトを配置します。Unityのメニューから[ゲームオブジェクト] > [空のオブジェクト]を選択してください。オブジェクトの名前は「WarpOut1」「WarpOut2」とします。反対側の穴の近くに配置してください。

「WarpIn1」「WarpIn2」「WarpOut1」「WarpOut2」は、ヒエラルキーパネルの「MazeLv2」の中に入れてください。

arduino-digital-ball-maze-02_33

ワープの制御をするスクリプトを書きます。C#ファイルを作り、エディタで下記のコードを記入してください。

WarpScript.cs

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

public class WarpScript : MonoBehaviour
{
    public GameObject warpOut;

    void OnTriggerEnter(Collider other)
    {
        if(other.name == "Sphere"){
            other.transform.position = warpOut.transform.position;
        }
    }
}

WarpScript.csを「WarpIn1」「WarpIn2」にアタッチし、Warip Outの項目に「WarpOut1」「WarpOut2」をドラッグしてください。

実行してボールが穴に落ちると、反対側の穴の付近にワープします。

arduino-digital-ball-maze-02_34

 

手順9:レベル3のギミック

レベル3の迷路は、2つのパーツでできています。迷路の外側が「MazeLv3-A」、中央の円形にくり抜かれた部分が「MazeLv3-B」です。中央部分をゆっくりと回転させます。「MazeLv2」を非アクティブにして、「MaseLv3」をアクティブにしてください。オブジェクトを回転するコードを書きます。C# ファイルを作成し下記のコードを記入してください。

RotateScript.cs

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

public class RotateScript : MonoBehaviour
{
    void Update()
    {
        transform.Rotate(new Vector3(0.0f, 0.0f, 0.05f));
    }
}

 

RotateScript.csを「MazeLv3-B」にアタッチすると、中央部分が回転します。ちゃんと当たり判定も機能しています。

arduino-digital-ball-maze-02_35

 

手順10:迷路の切り替え

ようやく3つの迷路が完成しました。最後に実物の迷路を交換して、3Dモデルの迷路を切り替える設定をおこないます。箱にレベル1の実物の迷路を乗せた状態で、Unityのプロジェクトを実行してください。スペースキーを押すと、コンソールパネルに下記のようにフォトリフレクタの反応の値が表示されます。

photoRef1:451 photoRef2:971

レベル1の実物の迷路の裏には、フォトリフレクタ1の位置に黒、フォトリフレクタ2の位置に白のマーカーが貼ってあります。つまり、反応の値は黒が451、白が971です。フォトリフレクタの高さなどにより、違う値になることがありますが、黒 < 白という関係になります。

レベル1は黒白、レベル2は白黒、レベル3黒黒なので、白と黒の閾値を決めて、それぞれ条件に当てはまる場合に迷路を切り替えます。

MazeScript.csの13行目を見てください。ここで閾値を設定しています。白(451)と黒(971)の大体真ん中ということで700にしています。

    int threshold = 700;//フォトリフレクタのしきい値

スペースキーを押した時に呼び出されるsetMaze()の中で、場合分けの処理をしています。

    public void setMaze()
    {
        Debug.Log("photoRef1:" + photoRef1 +"  photoRef2:" + photoRef2);

        if( (photoRef1 == 0) && (photoRef2 == 0))
        {
            //値が0の場合何もしない
            return;
        }else if( (photoRef1 < threshold) && (photoRef2 > threshold) )
        {
            //レベル1
            mazeType = 1;
            mazeLv1.SetActive(true);
            mazeLv2.SetActive(false);
            mazeLv3.SetActive(false);
        }else if( (photoRef1 > threshold) && (photoRef2 < threshold) )
        {
            //レベル2
            mazeType = 2;
            mazeLv1.SetActive(false);
            mazeLv2.SetActive(true);
            mazeLv3.SetActive(false);
        }else if( (photoRef1 < threshold) && (photoRef2 < threshold) )
        {
            //レベル3
            mazeType = 3;
            mazeLv1.SetActive(false);
            mazeLv2.SetActive(false);
            mazeLv3.SetActive(true);
        }
    }

完成したUnityのプロジェクトは、配布ファイルの「UnityApp」を参照してください。ただし、無料版のSerialPortUtilityProを含んだファイルは配布できないため、SerialPortUtilityProのパッケージと「SPUPObject」は削除しています。手順5を参考に設定をおこなってください。

 

4. おわりに

みなさん、お疲れさまでした!「デジタルボール転がし迷路」はいかがでしたか?Arduinoとセンサを使って、実物とデジタルコンテンツをミックスした面白い作品になったと思います。

今回は3つのステージを作成し、ワープする穴や回転する壁を作りました。他にも、ネバネバして動きが遅くなる床や、中に入ると空中に飛び出す大砲、ボールを追いかけてくる敵キャラクタなど、アイデア次第でいろんなステージが作れそうです。ぜひ、オリジナルのステージを作ってみてください!

【動画で解説】ラズパイZeroで小型AIカメラを作ろう!
平原真

物事の関係性をテーマに活動するアーティスト。コンピュータや電子デバイスを使ったメディアアートを多数制作しているが、近年は木や石など自然の素材を使った立体作品を手掛ける。大阪芸術大学准教授。Oggoroggo Products 代表。著書に『実践Arduino! 電子工作でアイデアを形にしよう』(オーム社)。 https://makotohirahara.com/