🎵

ワールドクラフトで25秒までの曲を聴くスクリプト

2023/02/22に公開

ワールドクラフトで25秒までの曲を聴くサンプルです。
まあGIFでは音が流れませんが。

ワールドクラフトでは5秒までの音を5つまで使うことができます。つまり、つなげば25秒までの曲を聴くことができます。
とはいえ同期の問題で、アイテムのOwner(持っている人、クリックした人など)以外にはつなぎのところが少し切れて聞こえてしまいますが。

サンプルワールド

https://cluster.mu/w/4a31f769-4ade-43d9-b7b0-1168f802d191
どういう風に聞こえるかは、こちらのワールドで確認してみてください。

音の指定


まずプロジェクトの中に、5秒以内にカットした音楽ファイルを入れます。


そして、Item Audio Set Listの「オーディオクリップ」のところにあなたの音を1つずつドラッグ&ドロップなどで指定していってください。
最大5つまで指定できます(その場合、画像右上の「3」という数字を「5」にすればいいわけです)。

このとき、Idが「Audio0」「Audio1」……となるようにしてください。

最大の5つまで指定すると、こんな風になりますね。

5秒制限に注意


5秒のつもりでもこんなことになったりするので注意。
(5.002秒)

unitypackage

cluster公式さんの「テンプレートワールド」など、クラフトアイテムをアップロードできる状態のUnityのプロジェクトに読み込んでください。
モデル・マテリアル・スクリプト・音声などなど、改変はお好きにどうぞ~

https://vins-jp.sakura.ne.jp/pack/wc_music.unitypackage

基本に自信がない人は

クラフトアイテムアップロードの基本はこの記事を。
スクリプトの基本はこの記事を。

スクリプト全文

今回の場合、音の長さによってスクリプトの改変はほぼ必須になると思います。
最初のほうにあるaudioNumaudioLengthArの数字をいじってください。

プレイヤーAは5秒+5秒+2秒で12秒の曲(audioNumが3、audioArは[5.0,5.0,2.0])。
プレイヤーBは5秒+5秒+5秒+5秒で20秒の曲(audioNumが4、audioArは[5.0,5.0,5.0,5.0])、音のつなぎのところにノイズ音を入れています(useBridgeSoundがtrue)。

useBridgeSoundをtrueにすると、音と音のつなぎのところで別の音を流します。
ノイズではなくスクラッチ音とかを入れれば、4種類の曲をサンプル視聴……みたいにできるかもしれません。単純につなぎの部分が同期で切れているのを目立たなくすることもできますね。

音楽プレイヤーB(赤いほう)
const audioNum = 4; //音ファイルの数
const useBridgeSound = true; //つなぎ音を鳴らすかどうか
const bridgeSoundShiftSec = 0.3; //つなぎ音を鳴らす場合、音の切れ目から何秒速くつなぎ音を鳴らすか
const stopMusicWhenBridgePlayed = true; //つなぎ音が鳴ったとき、曲を止めるか

const audioLengthAr = [5.0, 5.0, 5.0,5.0]; //各ファイルの音の長さ

const audioAr = [];
for (let audioNo = 0; audioNo < audioNum; audioNo++) {
	audioAr.push($.audio("Audio" + audioNo));
}
if (useBridgeSound) {
	audioAr.push($.audio("Audio" + audioNum));
}

const rotatorSpeed = 72.0;
const rotatorNum = 1;

const rotatorsAr = [];
for (let rotatorNo = 0; rotatorNo < rotatorNum; rotatorNo++) {
	rotatorsAr.push($.subNode("Rotator" + rotatorNo));
}

const initProc = () => {
	$.state.tick = 0;
	$.state.rotZVal = 0;
	$.state.audioNo = 0;

	$.state.bridgeSoundPlayed = false;
	$.state.currentLength = audioLengthAr[0];

	$.state.initialized = true;
}


const rotatorProc = (tickVal) => {
	$.state.rotZVal += tickVal*rotatorSpeed;

	const quat = new Quaternion().setFromEulerAngles(new Vector3(0, 0,$.state.rotZVal));
	for (let rotatorNo = 0; rotatorNo < rotatorNum; rotatorNo++) {
		rotatorsAr[rotatorNo].setRotation(quat);
	}
}

$.onUpdate((deltaTime) => {
	if (!$.state.initialized) {
		initProc();
	}

	if (!$.state.isGrabbing)
		return;

	rotatorProc(deltaTime);

	$.state.tick += deltaTime;
	if (useBridgeSound && !$.state.bridgeSoundPlayed) {
		if ($.state.tick >= $.state.currentLength - bridgeSoundShiftSec) {
			if (stopMusicWhenBridgePlayed) {
				stopCurrentMusic();
			}
			if (audioAr.length > audioNum) {
				audioAr[audioNum].play();
			}
			$.state.bridgeSoundPlayed = true;
		}
	}
	if ($.state.tick >= $.state.currentLength) {
		$.state.tick -= $.state.currentLength;

		stopCurrentMusic();
		$.state.audioNo = ($.state.audioNo + 1) % audioNum;
		$.state.currentLength = audioLengthAr[$.state.audioNo];
		playCurrentMusic();

		$.state.bridgeSoundPlayed = false;
	}
});

const playCurrentMusic = () => {
	if (audioAr.length > $.state.audioNo) {
		audioAr[$.state.audioNo].play();
	}
}

const stopCurrentMusic = () => {
	if (audioAr.length > $.state.audioNo) {
		audioAr[$.state.audioNo].stop();
	}
}

$.onGrab((flag) => {
	$.state.isGrabbing = flag;
	if (flag) {
		playCurrentMusic();
	} else {
		stopCurrentMusic();
	}
});

Discussion