🎆

ワールドクラフトで花火的なもの(仮)

2023/02/06に公開

ちょっとトリッキーな感じになっちゃった上に、処理が重いかもしれないので(仮)です。

ワールドクラフトで花火です。
設置中に暴発しちゃった場合、再設置することをオススメします。
(各発射台のタイミングがズレてしまうので)

初期設定では、最大25秒待ってから最初の発射が行われます。
(各発射台のタイミングをそろえるため。1つだけだと花火として少しさみしいです)
気長に待ってください。

unitypackage

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

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

基本に自信がない人は

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

これまでとの違い

子アイテムのbase0・base1は設置後削除するための目安および最初の発射までの時間を示す目安。
tama0~tama29は花火の弾です。スクリプト内では10個ずつ3グループで扱っています。

スクリプト全文

今回はかなり設定項目がある上にスクリプト自体もややこしいことになっています。
子アイテムの構造もこれまでよりややこしいので、unitypackageを開いてprefabの中身を見てみてください。

const tamaInGroup = 10;
const tamaGroupNum = 3;

const waitLength = 1.0; //打ち上げ待機時間の長さ
const raisingSpeed = 48.0; //打ち上げ速度
const raisingLength = 3.2; //打ち上げ時間
const gravityPowerWhenRaising = 15.0; //落ちる力(上昇中)
const gravityPowerWhenExploded = 1.2; //落ちる力(爆発後)
const explodingLength = 1.6; //爆発後消えるまでの長さ
const explodingPower = 20.0; //爆発速度

const explodingPowerTimesAr = [1.0,0.6,0.2]; //各グループの初期速度(1.0ならexplodingPowerのまま)

const thetaRandomVal = 0.5; //弾の角度のブレ幅

const isXExplosion = true; //X方向に広がる falseならZ方向

const startMilliSeconds = 25000; //開始時刻を何ミリ秒ごとにそろえるか(25000ミリ秒=25秒)
const baseSpeed = 1.0;

const tamaAr = [];
const tamaNum = tamaInGroup * tamaGroupNum;
for (let tamaNo = 0; tamaNo < tamaNum; tamaNo++) {
	tamaAr.push($.subNode("tama" + tamaNo));
}

const baseNode = $.subNode("base0");
const baseHomePosY = 0.45; //ここに達したら花火発射開始


let ENUMVAL = 0;
const FIRST = ENUMVAL++;
const RAISING = ENUMVAL++;
const EXPLODING = ENUMVAL++;
const WAITING = ENUMVAL++;

const zeroVec = new Vector3(0, 0, 0);

const raisingProc = (deltaTime) => {
	$.state.currentY += $.state.currentRaisingSpeed * deltaTime;
	$.state.currentRaisingSpeed -= gravityPowerWhenRaising*deltaTime;
	const currentPos = new Vector3(0, $.state.currentY, 0);

	if ($.state.tick >= raisingLength) {
		$.state.tick = 0;
		$.state.fireworksState++;
		let tamaExplodingPower;
		for (let tamaNo = 0; tamaNo < tamaNum; tamaNo++) {
			if (tamaNo % tamaInGroup == 0) {
				tamaExplodingPower = explodingPower*explodingPowerTimesAr[parseInt(tamaNo / tamaInGroup)];
			}
			if (tamaNo > 0) {
				tamaAr[tamaNo].setPosition(currentPos);
				tamaAr[tamaNo].setEnabled(true);
			}
			const theta = (tamaNo % tamaInGroup) / tamaInGroup * Math.PI * 2+Math.random()*thetaRandomVal;
			$.state[tamaNo] = new Vector2(Math.cos(theta) * tamaExplodingPower, Math.sin(theta) * tamaExplodingPower);
			$.state[tamaNo+tamaNum*1] = $.state[tamaNo];
			$.state[tamaNo+tamaNum*2] = currentPos;
		}
	} else {
		tamaAr[0].setPosition(currentPos);
	}
}

const explodingProc = (deltaTime) => {
	if ($.state.tick >= explodingLength) {
		$.state.tick = 0;
		$.state.fireworksState++;
		for (let tamaNo = 0; tamaNo < tamaNum; tamaNo++) {
			tamaAr[tamaNo].setEnabled(false);
			tamaAr[tamaNo].setPosition(zeroVec);
		}

		return;
	}
	const currentTickRate = 1 - $.state.tick / explodingLength*0.8;
	
	let currentPos;
	for (let tamaNo = 0; tamaNo < tamaNum; tamaNo++) {
		if (isXExplosion) {
			currentPos = $.state[tamaNo + tamaNum * 2].add(new Vector3(
				$.state[tamaNo].x * deltaTime,
				$.state[tamaNo].y * deltaTime,
				0
			));
		} else {
			currentPos = $.state[tamaNo + tamaNum * 2].add(new Vector3(
				0,
				$.state[tamaNo].y * deltaTime,
				$.state[tamaNo].x * deltaTime,
			));
		}
		tamaAr[tamaNo].setPosition(currentPos);
		$.state[tamaNo + tamaNum * 2] = currentPos;

		$.state[tamaNo] = $.state[tamaNo + tamaNum * 1].multiplyScalar(currentTickRate).add(new Vector2(0, -gravityPowerWhenExploded * $.state.tick));
	}
}

const startRaisingProc = () => {
	$.state.tick = 0;
	$.state.currentY = 0;
	$.state.currentRaisingSpeed = raisingSpeed;
	$.state.fireworksState = RAISING;
	tamaAr[0].setEnabled(true);
}

const waitProc = (deltaTime) => {
	if ($.state.tick >= waitLength) {
		$.state.tick = 0;
		startRaisingProc();

	}
}

const firstProc = () => {
	const nowMilliSeconds = new Date().getTime()%startMilliSeconds;

	if (nowMilliSeconds < $.state.lastMilliSeconds) {
		startRaisingProc();
		baseNode.setPosition(new Vector3(0, baseHomePosY, 0));
	} else {
		$.state.lastMilliSeconds = nowMilliSeconds;
		baseNode.setPosition(new Vector3(0, nowMilliSeconds / startMilliSeconds * baseHomePosY, 0));
	}
}

$.onUpdate((deltaTime) => {
	if (!$.state.initialized) {
		$.state.lastMilliSeconds = 0;
		$.state.fireworksState = FIRST;

		for (let tamaNo = 0; tamaNo < tamaNum; tamaNo++) {
			tamaAr[tamaNo].setEnabled(false);
		}

		$.state.initialized = true;
	}

	$.state.tick += deltaTime * baseSpeed;
	switch ($.state.fireworksState) {
		case FIRST:
			firstProc();
			break;
		case RAISING:
			raisingProc(deltaTime);
			break;
		case EXPLODING:
			explodingProc(deltaTime);
			break;
		case WAITING:
			waitProc(deltaTime);
			break;
	}
});

Discussion