✈️
ワールドクラフトで飛ぶ乗りもの
ちょい複雑な動きだったので少し自信がないですが、とりあえず良いことにして公開します。
周回して飛ぶ乗りものです。
飛行機っぽいリアルさはあまりないので、ファンタジックな感じですかね。
unitypackage
cluster公式さんの「テンプレートワールド」など、クラフトアイテムをアップロードできる状態のUnityのプロジェクトに読み込んでください。
モデル・マテリアル・スクリプトなどなど、改変はお好きにどうぞ~
なお今回、スクリプトを直書きせずjsファイルに書き込んでいます。
https://vins-jp.sakura.ne.jp/pack/wc_flier.unitypackage
基本に自信がない人は
クラフトアイテムアップロードの基本はこの記事を。
スクリプトの基本はこの記事を。
青と赤の違い
青は乗らなくても飛ぶ、速度が遅い。
赤は乗らないと飛ばない、速度が速い
……などの違いがあります。
スクリプト全文(赤いほう)
とりあえず速くしたいならbaseSpeedとかforwardSpeedを上げて、直進を長くしたいならforwardLengthを、高いところまで飛びたいならupLengthとかを上げる感じで。
回転させる部分が不要な場合はrotatorNumを0にしてください。
const stopTillRiding = true;
const baseSpeed = 2.5;
const forwardSpeed = 4.0;
const yRotSpeed = 40.0;
const upwardSpeed = 20.0;
const forwardLength = 2.0;
const upLength = 3.0;
const stopLength = 2.0;
const slideLength = 5.0;
const landingY = 0.5;
const landingLength = 1;
const rotateSeconds = 360.0 / yRotSpeed;
const rotatorSpeed = 200.0;
const rotatorNum = 2;
const rotatorsAr = [];
for (let rotatorNo = 0; rotatorNo < rotatorNum; rotatorNo++) {
rotatorsAr.push($.subNode("Rotator" + rotatorNo));
}
const speedVec = new Vector3(0, 0, forwardSpeed);
let ENUM_VAL = 0;
const STOP = ENUM_VAL++;
const FORWARD_A = ENUM_VAL++;
const UP = ENUM_VAL++;
const ROTATE_A = ENUM_VAL++;
const FORWARD_B = ENUM_VAL++;
const ROTATE_B = ENUM_VAL++;
const DOWN = ENUM_VAL++;
const LANDING = ENUM_VAL++;
const FORWARD_C = ENUM_VAL++;
const SLIDE_TO_FIRST_POS = ENUM_VAL++;
const END = ENUM_VAL++;
const initProc = () => {
$.state.tick = 0;
$.state.moveState = STOP;
$.state.rotatorTick = 0;
$.state.upwardVal = 0;
$.state.initialized = true;
}
const checkFirstPos = (alwaysUpdate) => {
if (!alwaysUpdate && $.state.firstPosChecked) {
return;
}
$.state.firstPos = $.getPosition();
$.state.pos = $.state.firstPos;
$.state.firstQuat = $.getRotation();
$.state.firstQuatY = $.state.firstQuat.createEulerAngles().y;
$.state.rotYVal = $.state.firstQuatY;
$.state.firstPosChecked = true;
}
const rotateAndMoveProc = (tickVal,specifiedY) => {
$.state.rotYVal += tickVal * $.state.currentYRotSpeed;
const quat = new Quaternion().setFromEulerAngles(new Vector3($.state.upwardVal, $.state.rotYVal, 0));
$.setRotation(quat);
const moveVec = speedVec.clone().applyQuaternion(quat).multiplyScalar(tickVal);
if (!!specifiedY) {
moveVec.set(moveVec.x, specifiedY*tickVal, moveVec.z);
}
$.state.pos = $.state.pos.add(moveVec);
}
const calcUpwardSpeedProc = (targetSpeed, rate) => {
if (rate > 0) {
if ($.state.upwardVal < targetSpeed) {
$.state.upwardVal += upwardSpeed * rate;
if ($.state.upwardVal > targetSpeed) {
$.state.upwardVal = targetSpeed;
}
}
} else {
if ($.state.upwardVal > targetSpeed) {
$.state.upwardVal += upwardSpeed * rate;
if ($.state.upwardVal < targetSpeed) {
$.state.upwardVal = targetSpeed;
}
}
}
}
const tickCheck = (tickVal) => {
if ($.state.tick >= tickVal) {
$.state.tick -= tickVal;
$.state.moveState++;
if ($.state.moveState == SLIDE_TO_FIRST_POS) {
$.state.slideFromPos = $.getPosition();
} else if ($.state.moveState == END) {
landedProc();
}
}
}
const rotatorProc = (tickVal) => {
if ($.state.moveState == SLIDE_TO_FIRST_POS || $.state.moveState == STOP) {
return;
}
$.state.rotatorTick += tickVal * rotatorSpeed;
for (let rotatorNo = 0; rotatorNo < rotatorNum; rotatorNo++) {
rotatorsAr[rotatorNo].setRotation(new Quaternion().setFromEulerAngles(new Vector3(0, $.state.rotatorTick, 0)));
}
}
const landedProc = () => {
$.state.moveState = STOP;
$.setPosition($.state.firstPos);
$.setRotation($.state.firstQuat);
$.state.pos = $.state.firstPos;
$.state.rotYVal = $.state.firstQuatY;
$.state.upwardVal = 0;
}
const easeOut = (t) => {
return 1 - Math.pow(1 - t, 3);
};
const slideProc = () => {
$.state.pos = $.state.slideFromPos.lerp($.state.firstPos, easeOut($.state.tick/slideLength));
}
$.onUpdate((deltaTime) => {
if (!$.state.initialized) {
initProc();
}
const tickVal = deltaTime * baseSpeed;
$.state.tick += tickVal;
rotatorProc(tickVal);
switch ($.state.moveState) {
case STOP:
if (stopTillRiding && !$.state.isRiding) {
$.state.tick = 0;
return;
}
tickCheck(stopLength);
if ($.state.moveState != STOP) {
checkFirstPos(false);
}
break;
case FORWARD_A: //離陸前
case FORWARD_C: //着陸後
$.state.currentYRotSpeed = 0;
rotateAndMoveProc(tickVal);
tickCheck(forwardLength);
break;
case FORWARD_B: //上空での前進
$.state.currentYRotSpeed = 0;
rotateAndMoveProc(tickVal);
tickCheck(forwardLength * 2 + upLength * 2);
break;
case UP:
calcUpwardSpeedProc(-upwardSpeed, -tickVal);
$.state.currentYRotSpeed = 0;
rotateAndMoveProc(tickVal);
tickCheck(upLength);
break;
case ROTATE_A:
case ROTATE_B:
calcUpwardSpeedProc(0, tickVal);
$.state.currentYRotSpeed = yRotSpeed;
rotateAndMoveProc(tickVal);
tickCheck(rotateSeconds / 2);
if ($.state.moveState == ROTATE_A + 1) {
$.state.currentYRotSpeed = $.state.firstQuatY + 180;
} else if ($.state.moveState == ROTATE_B + 1) {
$.state.currentYRotSpeed = $.state.firstQuatY;
}
break;
case DOWN:
calcUpwardSpeedProc(upwardSpeed, tickVal);
$.state.currentYRotSpeed = 0;
rotateAndMoveProc(tickVal);
if ($.state.pos.y < $.state.firstPos.y + landingY) { //どうしても誤差が出るので、高度(Y)がlandingY以下になったらLANDINGに移行
$.state.moveState++;
$.state.tick = 0;
$.state.sumVal = 0;
$.state.landingSpeed = landingY / landingLength * 3 / 2;
}
break;
case LANDING: //最後の高度(Y)の変化だけは計算で求めて着陸した感を強める
calcUpwardSpeedProc(0, -1 / landingLength * tickVal);
const yDownT = $.state.tick / landingLength;
const yDownSpeed = (-yDownT*yDownT+1) * $.state.landingSpeed;
$.state.sumVal += -yDownSpeed*tickVal;
rotateAndMoveProc(tickVal, -yDownSpeed);
tickCheck(landingLength);
break;
case SLIDE_TO_FIRST_POS: //どうしても誤差がでるので、着陸後初期位置にスライドさせる
slideProc();
tickCheck(slideLength);
break;
}
if ($.state.moveState != STOP) {
$.setPosition($.state.pos);
}
});
$.onRide((flag) => {
$.state.isRiding = flag;
if (flag && $.state.moveState == STOP) {
checkFirstPos(true);
}
});
Discussion