LoginSignup
7
3

More than 3 years have passed since last update.

IoTでマトリョミン(楽器)を作ってみた(2/2)AR.jsでマトリョーシカ編

Last updated at Posted at 2020-12-20

はじめに

音楽まったくやってない人って楽器ひくことにちょっとしたあこがれないですか?
私はそんな一人です、楽器ひいてみたい。
ということで、かくかくしかじかあってマトリョミン作ることにしました。前回は、音を奏でるテルミン(風)までを作りました。今回は、見た目をマトリョーシカにしていきます。

完成デモ

できたマトリョミン(風)はこちら。ARで見た目をマトリョーシカにしていて、奏でる音階に合わせてマトリョーシカの表情が変わります。

距離センサーとスピーカーで音を奏でて、かつARと連携する仕組みを作る

前回は、距離センサーとスピーカーで奏でる音を制御しましたが、今回はさらにARとも連携させます。ARは、A-FrameとAR.jsを使いました。

image.png

前回LEGOで組み立てたテルミン(風)に、LEGO互換パーツ化したARマーカーを付けます。

image.png

※LEGO互換ARマーカーの作り方はこちらで紹介しています
 →レジンでLEGO互換パーツを自作したい!その(1)

制御スクリプトはこちら

前回にちょっと追加で制御スクリプトが作れると思っていたのですが、甘かった。obniz x A-Frame x AR.js 連携の情報が見つからなくて結局自作しました(ちょっと苦戦しました)。

<!doctype HTML>
<html>
<script src="https://unpkg.com/obniz@3.10.1/obniz.js"></script>
<script src="js/aframe.min.js"></script>
<script src="js/aframe-ar.js"></script>
<body style="margin: 0px; overflow: hidden;">
<script>
let obnizConnectFlg = 0;
let hcsr04;
let speaker;
let glNo = 0;
let updateFlg=0;
const SCALELENGTH = 15;
const STARTCOUNT  = 5;
const CYCLEVAL  = 500;

const scaleArr = new Array();
const dispArr  = new Array();
scaleArr.push(0);   dispArr.push("無(近)");
scaleArr.push(262); dispArr.push("");
scaleArr.push(294); dispArr.push("");
scaleArr.push(330); dispArr.push("");
scaleArr.push(349); dispArr.push("ファ");
scaleArr.push(392); dispArr.push("");
scaleArr.push(440); dispArr.push("");
scaleArr.push(494); dispArr.push("");
scaleArr.push(523); dispArr.push("ド(高)");
scaleArr.push(0);   dispArr.push("無(遠)");

const OBNIZ_ID = 'xxxx-xxxx'; // お持ちのObniz ID を設定

const obniz = new Obniz(OBNIZ_ID);
obniz.onconnect = async function() {
    hcsr04  = obniz.wired("HC-SR04", {gnd:0, echo:1, trigger:2, vcc:3});
    speaker = obniz.wired("Speaker",{"signal":10, "gnd":11});
    obnizConnectFlg = 1;
}

AFRAME.registerComponent('canvas-texture', {
    init: function()
    {
        this.canvas = document.querySelector("#myCanvas");
        this.canvas.width  = 595;
        this.canvas.height = 842;
        this.context = this.canvas.getContext('2d');
        this.x = 200;
        this.y = 100;
        this.dx = 5;
        this.dy = 3;
        this.image = ["00.png","01.png","02.png","03.png","04.png","05.png","06.png","07.png","08.png","09.png"];
        this.imgSrc = "";
        this.dtsum = 0;
        this.distance = 0;
        this.updateFlg = 0;
    },
    update: function(){

        let material = this.el.getObject3D("mesh").material;
        const chara = new Image();
        console.log("glNo = " + glNo + ", this.image[ glNo ] = " + this.image[ glNo ]);
        chara.src = "images/" + this.image[ glNo ];
        chara.onload = () => {
            this.context.drawImage(chara, 0, 0);
            if (!material.map)
                return;
            else
                material.map.needsUpdate = true; 
        };
    },
    tick: function(t, dt)
    {
        this.dtsum += dt;
        if( this.dtsum >= CYCLEVAL ){
            this.dtsum = 0;
            if(obnizConnectFlg == 1){
                this.obniz();
            }
        }        
    },
    obniz: function(){
        hcsr04.measure(function(distance){
            console.log("distance = " + distance);
            updateFlg = 1;
            const tmpNo = Math.floor( distance / SCALELENGTH ) - STARTCOUNT;

            if(tmpNo < 0){
                glNo = 0;
            }else if(tmpNo >= 9){
                glNo = 9;
            }else if( !isNaN(tmpNo) ){
                glNo = tmpNo;
            }else{
                glNo = 9;
            }
            speaker.play( scaleArr[ glNo ] );
            updateFlg = 1;
        });
        if( updateFlg == 1){
            updateFlg = 0;
            this.update();
        }
    }
});
</script>

<a-scene embedded vr-mode-ui="enabled: false;" arjs="debugUIEnabled: false;">
    <a-assets>
        <canvas id="myCanvas"></canvas>
    </a-assets>
    <a-marker type="pattern" url="data/hiro.patt">
        <a-plane
                width="2" height="2.82"
                position="0 0 0"
                rotation="-90 0 0"
                material="src: #myCanvas; transparent: true; opacity: 0.95;"
                canvas-texture
        ></a-plane>

    </a-marker>
    <a-entity camera></a-entity>
</a-scene>
</body>
</html>

イラストの準備

描いたマトリョーシカのイラストは全10種類。
9種類はこちら

10枚目がこれ。距離が近すぎたらこのイラストが表示されます。

さいごに

アドベントカレンダーに参加したくて、マトリョミン(風)の楽器を作りました。obnizとA-Frame,AR.jsの連携が思ったより難しかったですが、無事つくれてよかった。

7
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
3