🔫

ワールドクラフトで弾を撃つ(音もでる)

2023/02/14に公開

ワールドクラフトで弾を撃てるスクリプトです。
音もでます(下のはアニメGIFなんで音は鳴っていませんが)。

撃った後にプレイヤーが移動してもそのままの軌道で飛び続けてくれます。

unitypackage

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

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

基本に自信がない人は

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

スクリプト全文

弾を撃つ距離を長くしたい方はbulletLifeを、スピードを変えたい方はbulletSpeedをいじってください。
持った部分がクルクル回転する要素が要らなければgrabObjRotSpeedを0にするのが手っ取り早いです。関係するコードを消してもいいですが。

const bulletNum = 3;
const bulletsAr = [];
for (let bulletNo = 0; bulletNo < bulletNum; bulletNo++) {
	bulletsAr.push($.subNode("Bullet" + bulletNo));
}

const grabObj = $.subNode("GrabObj");
const grabObjRotSpeed = 360;

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

const bulletLife = 0.5;
const bulletSpeed = new Vector3(0, 0, 20);

const defBulletPos = new Vector3(0, 0.85, 0);

const initProc = () => {
	$.state.initialized = true;

	for (let bulletNo = 0; bulletNo < bulletNum; bulletNo++) {
		bulletsAr[bulletNo].setEnabled(false);
	}

	$.state.firstPos = $.getPosition();
	$.state.firstRot = $.getRotation();

	$.state.grabObjRotZ = 0;
	$.state.currentBulletNo = 0;
};

$.onUse((flag) => {
	if (!flag)
		return;

	let result = false;
	for (let bulletNo = 0; bulletNo < bulletNum; bulletNo++) {
		if (ShootProc(($.state.currentBulletNo + bulletNo)%bulletNum)) {
			$.state.currentBulletNo = ($.state.currentBulletNo+1)%bulletNum;
			result = true;
			break;
		}
	}
	if (!result)
		return;

	audioAr[0].play();
}
);

const ShootProc = (no) => {
	if ($.state[no*4])
		return false;

	const rot = $.getRotation();

	bulletsAr[no].setEnabled(true);
	$.state[no*4] = true;
	$.state[no*4+1] = $.getPosition().add(defBulletPos.clone().applyQuaternion(rot)); //ワールド座標で管理
	$.state[no*4+2] = bulletSpeed.clone().applyQuaternion(rot); 
	$.state[no*4+3] = 0.0;

	return true;
}

const BulletProc = (no,rotInv,parentPos,deltaTime) => {
	if (!$.state[no*4])
		return;
	
	$.state[no*4+3] += deltaTime;
	if ($.state[no*4+3] >= bulletLife) {
		bulletsAr[no].setEnabled(false);
		bulletsAr[no].setPosition(defBulletPos); //初期位置はローカル座標そのままでOK
		$.state[no*4] = false;
		return;
	}

	//ワールド座標で加算していく
	const pos = $.state[no * 4 + 1].add($.state[no * 4 + 2].multiplyScalar(deltaTime));
	$.state[no * 4 + 1] = pos;
	//親の座標を引いたものに反転したクォータニオンをかけるとローカル座標になる
	const localPos = pos.sub(parentPos).applyQuaternion(rotInv);
	bulletsAr[no].setPosition(localPos);
}

$.onUpdate(deltaTime => {
	if (!$.state.initialized) {
		initProc();
	}
	const rotInv = $.getRotation().invert();
	const parentPos = $.getPosition();

	for (let bulletNo = 0; bulletNo < bulletNum; bulletNo++) {
		BulletProc(bulletNo,rotInv,parentPos,deltaTime);
	}

	$.state.grabObjRotZ += deltaTime * grabObjRotSpeed;
	grabObj.setRotation(new Quaternion().setFromEulerAngles(new Vector3(0, 0, $.state.grabObjRotZ)));
}
);

Discussion