【VCI】無限音階楽器の制作メモ

VR

THE SEED ONLINEで公開中のVCI、「無限音階楽器」を作るにあたって、つまったところ、変更した部分などのメモ。

目的別に書いていきます。

僕はプログラムを始めたばかりで些細なことでもつまづいてしまったので、参考になればうれしい。

VCI、バーチャルキャストで検索しても、特に音に関しては情報が全く出てこないので、今後もなるべく書いていこうと思います。

目次

・オーディオのフォーマットはWAVで
・複数のアイテムを一緒に動かす方法
・Stickで叩いて音を出す方法
・楽器をすり抜けるようにして音を出す方法
・3Dペイントツール「Teddy」で文字の形の3Dモデルを入れる
・叩いた方の手を振動させる
・今後について

概要

円状に半音違い、1オクターブ12音の球を並べたバーチャル楽器。
時計回りに音が高くなる。

特殊な音作り方法によって、無限に音が上がり続ける、または下がり続けるように聴こえる。

付属のスティックで叩いて音を出す。

オーディオのフォーマットはWAVで

VCIのオーディオの仕様は、

VCIObject 以下のGameObjectに存在するAudioSourceのclipがVCIファイルに書き込まれます。

AudioClipがWAV以外の場合はWAVに変換、WAVは無変換でVCIに書き込まれます。

44100Hz 8Bit 1ch ~ 48000Hz 24Bit 2chまでに対応。

なので、元のデータをWAVで作成し、無変換で出力されるようにします。

なるべく容量を少なく、かつリッチに聴かせる、ということを考えると、

44100Hz 16bitがよさそうです。公式でも44100Hz 16bitを推奨していますね。

楽器だと 1ch(モノラル)がよいかなと思いましたが、うまく鳴らなかったので、2ch(ステレオ)のほうがよさそうです。

48000Hz 24bitだとモノラルでも大丈夫でした。もうすこし検証が必要そうです。

サウンドの設定はCompression Formatのところは念のためPCM(非圧縮)にしておいたほうがよさそう。

AudioSourceのパラメータ出力は対応していません(2019/02/23現在)

■参考:使用可能なUnityComponent

複数のアイテムを一緒に動かす方法

新しく作ったGameObjectの子どもにしてまとめると、一緒に動かすことができます。

HierarchyのCreate Enptyで空のGameObjectを作成。

それを親として、子になるアイテムを入れる。

ここでは、GameObject(Hontai)にA~G#の球を入れます。
Stickはバラバラに持ちたいので入れない。

Hontaiには「VCI Sub Item」をAdd Componentで入れる。

逆に、子にしたA~G#に「VCI Sub Item」がついていたら、右上にある歯車からRemove Componentで削除する。

Stickで叩いて音を出す方法

サンプルがStickだったので、まずはStickで実装。

Hierarchyの3D ObjectからCylinderを作成。

加工してStickにする。

main.luaのコードはこんな感じ。

スティックがそれぞれのSphere Colliderに物理衝突したら音が出る。

function onCollisionEnter(item, hit)
    if (item == "C" and hit == "Stick") or (item == "Stick" and hit == "C") then
        vci.assets._ALL_PlayAudioFromName("C_Sound") 
end
   if (item == "C#" and hit == "Stick") or (item == "Stick" and hit == "C#") then
        vci.assets._ALL_PlayAudioFromName("C#_Sound")
    end
 if (item == "D" and hit == "Stick") or (item == "Stick" and hit == "D") then
        vci.assets._ALL_PlayAudioFromName("D_Sound")
    end
  if (item == "D#" and hit == "Stick") or (item == "Stick" and hit == "D#") then
        vci.assets._ALL_PlayAudioFromName("D#_Sound")
    end
 if (item == "E" and hit == "Stick") or (item == "Stick" and hit == "E") then
        vci.assets._ALL_PlayAudioFromName("E_Sound")
    end
 if (item == "F" and hit == "Stick") or (item == "Stick" and hit == "F") then
        vci.assets._ALL_PlayAudioFromName("F_Sound")
    end
 if (item == "F#" and hit == "Stick") or (item == "Stick" and hit == "F#") then
        vci.assets._ALL_PlayAudioFromName("F#_Sound")
    end
 if (item == "G" and hit == "Stick") or (item == "Stick" and hit == "G") then
        vci.assets._ALL_PlayAudioFromName("G_Sound")
    end
 if (item == "G#" and hit == "Stick") or (item == "Stick" and hit == "G#") then
        vci.assets._ALL_PlayAudioFromName("G#_Sound")
    end
 if (item == "A" and hit == "Stick") or (item == "Stick" and hit == "A") then
        vci.assets._ALL_PlayAudioFromName("A_Sound")
    end
 if (item == "A#" and hit == "Stick") or (item == "Stick" and hit == "A#") then
        vci.assets._ALL_PlayAudioFromName("A#_Sound")
    end
 if (item == "B" and hit == "Stick") or (item == "Stick" and hit == "B") then
        vci.assets._ALL_PlayAudioFromName("B_Sound")
    end

end

■参考:叩くとアニメーションをして音が鳴るドラムを作る

楽器をすり抜けるようにして音を出す方法

onCollisionEnterだと、物理衝突があり、いまいち演奏がしにくかったり、Stickがあらぶったりしたので、物体をすり抜けるようにして音を出すコードにしました。

まずは、A~G#、それぞれのSphere ColliderのIs Triggerにチェックを入れる。

Stickの ColliderもIs Triggerにチェックを入れる。

これでオブジェクトがすり抜けるようになります。

そのままだと物理衝突がないので、onCollisionEnterは使えなくなるので、トリガーオブジェクトに侵入した瞬間呼び出される、「onTriggerEnter」を使います。

最初の部分だけ書き換えたコード全体。

function onTriggerEnter(item, hit)
    if (item == "C" and hit == "Stick") or (item == "Stick" and hit == "C") then
        vci.assets._ALL_PlayAudioFromName("C_Sound") --サウンドを鳴らす

end
   if (item == "C#" and hit == "Stick") or (item == "Stick" and hit == "C#") then
        vci.assets._ALL_PlayAudioFromName("C#_Sound")
    end
 if (item == "D" and hit == "Stick") or (item == "Stick" and hit == "D") then
        vci.assets._ALL_PlayAudioFromName("D_Sound")
    end
  if (item == "D#" and hit == "Stick") or (item == "Stick" and hit == "D#") then
        vci.assets._ALL_PlayAudioFromName("D#_Sound")
    end
 if (item == "E" and hit == "Stick") or (item == "Stick" and hit == "E") then
        vci.assets._ALL_PlayAudioFromName("E_Sound")
    end
 if (item == "F" and hit == "Stick") or (item == "Stick" and hit == "F") then
        vci.assets._ALL_PlayAudioFromName("F_Sound")
    end
 if (item == "F#" and hit == "Stick") or (item == "Stick" and hit == "F#") then
        vci.assets._ALL_PlayAudioFromName("F#_Sound")
    end
 if (item == "G" and hit == "Stick") or (item == "Stick" and hit == "G") then
        vci.assets._ALL_PlayAudioFromName("G_Sound")
    end
 if (item == "G#" and hit == "Stick") or (item == "Stick" and hit == "G#") then
        vci.assets._ALL_PlayAudioFromName("G#_Sound")
    end
 if (item == "A" and hit == "Stick") or (item == "Stick" and hit == "A") then
        vci.assets._ALL_PlayAudioFromName("A_Sound")
    end
 if (item == "A#" and hit == "Stick") or (item == "Stick" and hit == "A#") then
        vci.assets._ALL_PlayAudioFromName("A#_Sound")
    end
 if (item == "B" and hit == "Stick") or (item == "Stick" and hit == "B") then
        vci.assets._ALL_PlayAudioFromName("B_Sound")
    end

end

トリガーオブジェクトに接触した瞬間に呼び出される「onTriggerEnter」の応用で、
・トリガーオブジェクトに侵入している間呼び出される「onTriggerStay」
・トリガーオブジェクトから脱出した瞬間呼び出される「onTriggerExit」

も使えそうですね。それぞれトレモロとか、余韻みたいなものに。

3Dペイントツール「Teddy」で文字の形の3Dモデルを入れる

音の名前がわからないと演奏がしにくいな、と思い、

Unity上に直接絵をかいて3Dモデリングができるツール「Teddy」で簡易的な3Dモデルを作ってみました。

■Teddy | Unity Asset Store

Mesh Colliderがついているのですが、VCIで対応しておらず、Unityからも重いと怒られるので、Remove Componentで削除。

かわりにSphere ColliderをAdd Component。こちらもIs Triggerをチェック。

叩いた方の手を振動させる

楽器を叩くと手が振動するようにする。

以下をそのままコピペしてそれぞれのif文のところに入れるだけでいける。

vci.assets.HapticPulseOnGrabbingController(item, 1500, 0.05)

全体はこうなりました。

 function onTriggerEnter(item, hit)
        if (item == "C" and hit == "Stick") or (item == "Stick" and hit == "C") then
            vci.assets._ALL_PlayAudioFromName("C_Sound") --サウンドを鳴らす
            vci.assets.HapticPulseOnGrabbingController(item, 1500, 0.05)--叩いた手を振動させる
     end
       if (item == "C#" and hit == "Stick") or (item == "Stick" and hit == "C#") then
            vci.assets._ALL_PlayAudioFromName("C#_Sound")
            vci.assets.HapticPulseOnGrabbingController(item, 1500, 0.05)
        end
     if (item == "D" and hit == "Stick") or (item == "Stick" and hit == "D") then
            vci.assets._ALL_PlayAudioFromName("D_Sound")
            vci.assets.HapticPulseOnGrabbingController(item, 1500, 0.05)
        end
      if (item == "D#" and hit == "Stick") or (item == "Stick" and hit == "D#") then
            vci.assets._ALL_PlayAudioFromName("D#_Sound")
            vci.assets.HapticPulseOnGrabbingController(item, 1500, 0.05)
        end
     if (item == "E" and hit == "Stick") or (item == "Stick" and hit == "E") then
            vci.assets._ALL_PlayAudioFromName("E_Sound")
            vci.assets.HapticPulseOnGrabbingController(item, 1500, 0.05)
        end
     if (item == "F" and hit == "Stick") or (item == "Stick" and hit == "F") then
            vci.assets._ALL_PlayAudioFromName("F_Sound")
            vci.assets.HapticPulseOnGrabbingController(item, 1500, 0.05)
     end
     if (item == "F#" and hit == "Stick") or (item == "Stick" and hit == "F#") then
            vci.assets._ALL_PlayAudioFromName("F#_Sound")
            vci.assets.HapticPulseOnGrabbingController(item, 1500, 0.05)
        end
     if (item == "G" and hit == "Stick") or (item == "Stick" and hit == "G") then
            vci.assets._ALL_PlayAudioFromName("G_Sound")
            vci.assets.HapticPulseOnGrabbingController(item, 1500, 0.05)
        end
     if (item == "G#" and hit == "Stick") or (item == "Stick" and hit == "G#") then
            vci.assets._ALL_PlayAudioFromName("G#_Sound")
            vci.assets.HapticPulseOnGrabbingController(item, 1500, 0.05)
        end
     if (item == "A" and hit == "Stick") or (item == "Stick" and hit == "A") then
            vci.assets._ALL_PlayAudioFromName("A_Sound")
            vci.assets.HapticPulseOnGrabbingController(item, 1500, 0.05)
        end
     if (item == "A#" and hit == "Stick") or (item == "Stick" and hit == "A#") then
            vci.assets._ALL_PlayAudioFromName("A#_Sound")
            vci.assets.HapticPulseOnGrabbingController(item, 1500, 0.05)
        end
     if (item == "B" and hit == "Stick") or (item == "Stick" and hit == "B") then
            vci.assets._ALL_PlayAudioFromName("B_Sound")
            vci.assets.HapticPulseOnGrabbingController(item, 1500, 0.05)
        end
    

end

今後について

やりたいこと。できたら追記していきます。

・手でたたいて音を出したい

onCollisionEnterの場合、hit = RightHand、もしくはLeftHand。

手が当たるだけだと何も起こらなくて、Grabしないと音が出ない。

onTriggerEnterだったらhit = HandPointMarker、item = はそれぞれの球じゃなくて親のGameObjectになっちゃうので、バラバラに鳴らすことができないっぽい感じがする。

・アニメーションさせたい
回転させるか、大小させるか。センスが問われる…

公開したバーチャル楽器はこちら

■『Juzu』無限音階楽器 | THE SEED ONLINE

■『Alpha』無限音階楽器 | THE SEED ONLINE

おわりに

たれ乳アルパカ作曲系vtuber、alaki paca/荒木パカさん(@arabiiiiiiiiiii )にめちゃくちゃ教えてもらいました。ほとんど教えてもらいました。

VCIのブログも書いてらっしゃいます。

■vciで音を出す。そして弾き語りをする。| アルパカ研究室

公式にも色々書いてあるのでにらめっこ

■vci開発環境の導入 | Virtual Cast公式

■vciscript | Virtual Cast公式

『無限音階楽器』をTHE SEED ONLINEで公開しました!