Udonsharpを使って音源の波形を取得して、ゲーム内の物体に反映させてみよう(改良版)
前回は何を作るかも決めずに作成したので、コードが汚くなってしまいました。
何か合ったときに修正する手間がかかるため、今回はあらかじめ何をどのようにするかを決めてから、コードを書いていきましょう。
前回のプロジェクトを使ってもいいですが、今回は新しく作り直します。適当な名前でプロジェクトを作成してください。
適当にAudiosとかで作っておきます。
プロジェクトは一番最初の記事の状態にUdonsharpだけimportしておきます。
この状態のプロジェクトから始めましょう。
ここからの説明はよくわからない人は★マークまで読み飛ばしてOKです。
1.何をするか文章にまとめる
前回はキューブが音に合わせて動くっていうのがわかったのは最後でした。
なので今回はどのように何を動かすのかを決めてからコードを書いていきましょう。
8個のキューブが、音に合わせて、高さがリアルタイムで変わる
今回はこれを作っていきましょう。
これに必要な情報は、
a.使うオブジェクト
b.使う変数
c.変化する頻度
が必要になります。
a.使うオブジェクト
使うオブジェクトは、前回同様にCubeを使いましょう。
音源も前回と同じAudioSourceですね。
ただ、今回はオブジェクトはUnityで作成せず、コードで作成しましょう。
前回より汎用性はありますが、やっていることは前回とほぼ一緒です。
b.使う変数
波形データの取得→private,Float型(小数)の数(サイズは2の乗数でないといけません。かつ64以上8192以下)
波形データの乗数の値→private,int型(整数)の数「2n」表記のnの部分→上の条件より6以上13以下。128にしたいので7に設定。
キューブの個数 →private,int型の数、上記の制限があるので、2の乗数で作成したい。今回は8個なので8。
キューブのサイズの変更に使う数→private,float型の数の配列。キューブ1個あたりに1個割り当てたいので、8個必要。
キューブのPrefabを格納する変数→public,GameObject型の変数。
作成したキューブを格納する変数→private,GameObject型の配列。キューブの個数と同じなのでサイズは8。
c. 変化する頻度
毎フレーム更新を行いたいので、今回はUnityのUpdateというものを使います。
これでリアルタイムで情報が更新されます。
★よくわかんない説明ここまで
2.コードを書く
using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon;
using System;
public class AudioVisualizer2 : UdonSharpBehaviour
{
private float[] waveData_;
public int waveDataPow = 7;
public int cubeCount = 8;
private float[] cubeSize;
public GameObject prefabCube;
private GameObject[] audioCube;
private AudioSource aud;
void Start()
{
aud = GetComponent<AudioSource>();
audioCube = new GameObject[cubeCount];
cubeSize = new float[cubeCount];
waveData_ = new float[(int)(Math.Pow(2, waveDataPow))];
for(int i = 0; i < cubeCount; i++)
{
audioCube[i] = VRCInstantiate(prefabCube);
audioCube[i].transform.localScale = new Vector3(0.5f,1f,0.5f);
audioCube[i].transform.position = new Vector3(5f - i, 0f, -5.5f);
}
}
void Update()
{
aud.GetOutputData(waveData_, 1);
for(int i = 0;i<cubeCount; i++)
{
cubeSize[i] = 0;
}
for(int i = 0; i < waveData_.Length; i++)
{
if (waveData_[i] > 0)
{
cubeSize[i*cubeCount/waveData_.Length] += waveData_[i];
}
}
for(int i = 0; i < cubeCount; i++)
{
audioCube[i].transform.localScale = new Vector3(0.5f, cubeSize[i],0.5f);
}
}
}
はい。
説明は長いので追記します。
前回と同様の手順でU# Scriptを作成してください。
下の方のwindowで右クリック→Create→U# Scriptです。
ファイル名は、コードを完全にコピペしたいのであればAudioVisualizer2と入力してください。
修正方法がわかる方はどんな名前にしてもいいです。
3.ワールドへの適用
ワールドの方の調整を行いましょう。
前回と同様にAudioSourceを作成して、前回DLしたシャイニングスターをプロジェクトのAssetの欄にドラッグ&ドロップし、AudioSourceのAudioClipの欄にドラッグ&ドロップしましょう。
説明は前回より簡単にしますので分かんなかったら前回の見てください
AudioSourceにAdd Component→Udon behaviourを選択し、先程作成したAudio Visualizer2.assetをドラッグ&ドロップしてください。
prefabというものを作成します。今回はキューブを量産する金型みたいなものです。
Hierarchyウィンドウで右クリック→3D Object→Cubeを選択してください。
作成したキューブをそのままドラッグ&ドロップでAssetフォルダに入れます。
操作が終わったらHierarchy上のCubeは選択→Deleteキーで削除してしまいましょう。
Hierarchy上のAudioSourceを選択して、UdonBehabiourの欄の prefab cubeに先程のCubeをドラッグ&ドロップしましょう。
※Hierarchyウィンドウに作成したCubeでも動きますが、今回はこの方法で行います。
あとは上の再生ボタンを押してみましょう。
こんな感じでキューブが上下に拡大縮小していれば完了です。
右側のCube Countは可変なので、再生前に個数をUnity上で変更するとその個数のキューブが作成されます。
こんな感じ。
ワールドアップロードしても確認できますので、必要に応じてアップロードしてみてください。
お疲れ様でした!次は何をしましょうね・・・
補足
コードで何をしてたのか知りたい人はこの下に書きます。読まなくてもいいです。
using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon;
using System;
using Systemは数学の何かを使うときに必要なので今回は書いておいてください。
public class AudioVisualizer2 : UdonSharpBehaviour
{
private float[] waveData_;//波形データを入れる配列
public int waveDataPow = 7;//波形データは2のn乗で設定しなければいけないのでこう書いとく
public int cubeCount = 8;//キューブの個数
private float[] cubeSize;//キューブの高さを決める配列、キューブの個数分宣言する
public GameObject prefabCube;//音で高さが変わるオブジェクトを入れる、キューブじゃなくても別にいい
private GameObject[] audioCube;//音で高さが変わるオブジェクトを格納する配列、個数は上で設定したCubeCount
private AudioSource aud;//取得元の音源
void Start()
{
aud = GetComponent<AudioSource>();//説明してもわかりにくいのでこういうふうに書いておけばいいです
audioCube = new GameObject[cubeCount];
//↑音で拡大縮小するキューブの個数分、キューブを格納する配列を宣言、CubeCountの個数なので8個
cubeSize = new float[cubeCount];//キューブのそれぞれの高さを設定する配列を宣言、同じくCubeCountの個数なので今回は8個
waveData_ = new float[(int)(Math.Pow(2, waveDataPow))];
//↑音の波形データを格納する配列を宣言、今回はデータの個数は2の7乗(waveDataPowの値)なので128
for(int i = 0; i < cubeCount; i++)//iが0から8未満の間で以下の処理を実行
{
audioCube[i] = VRCInstantiate(prefabCube);//audioCube[i]という名前のオブジェクトをprefabCubeで初期化
audioCube[i].transform.localScale = new Vector3(0.5f,1f,0.5f);
//上で作成したオブジェクトを横0.5、縦1、奥行き0.5に設定(transform欄参照)
audioCube[i].transform.position = new Vector3(5f - i, 0f, -5.5f);
//上で作成したオブジェクトの位置をx=5-i,=0,z=-5.5に設定。iが1ならx=4みたいな感じ。
}
}
void Update()//ワールドにjoinしてからずっと繰り返される部分
{
aud.GetOutputData(waveData_, 1);//音源から現在の波形データを取得する
for(int i = 0;i<cubeCount; i++)
{
cubeSize[i] = 0;//キューブの高さを設定する配列をすべて0にする
}
for(int i = 0; i < waveData_.Length; i++)
{
if (waveData_[i] > 0)//waveData_が0より大きいときだけ
{
cubeSize[i*cubeCount/waveData_.Length] += waveData_[i];
//キューブの高さをそれぞれ設定する。cubeSize[0]はwaveData_[0]からwaveData[15]の和、
//cubeSize[1]はwaveData_[16]からwaveData_[31]までの和みたいな感じで今回は設定してある
}
}
for(int i = 0; i < cubeCount; i++)
{
audioCube[i].transform.localScale = new Vector3(0.5f, cubeSize[i],0.5f);
//それぞれのキューブに先程のcubeSizeの値を設定する
}
}
}
宣言とか配列とかfor文とかはC#のサイトとかに書いてあるので必要なら調べてみてください。