「紫電改(自由飛行)」 ClusterScript コード解説
「紫電改(自由飛行)」 ClusterScript コード解説をしていきます。
はじめに
……と言っても既に上記の記事でソースコードまで公開されているため飛行するスクリプトに関しては問題ないと思います(感謝!!)
私の作成した紫電改の飛行部分もほぼ同じようなスクリプトで動作しています。
ただし、紫電改の場合だとそのまま水平垂直に移動するだけだと物足りないと考え、より飛行機のようなリアルさを出すために左右や上下方向を入力したときに機体自体をロール(傾き)させる部分を追加したのでその解説をしていきたいと思います。
実際にスクリプトを使用したクラフトアイテム
実際に作ったクラフトアイテムの動きは以下のようになります。
チケット交換も可能ですので、お気軽にご使用ください。
実際のソースコード
備考
本コードは、前述の cluster 公式 note に記載されているサンプルコード並びに前述したやまちゃんさんのソースコードに基づいています。
私が改変した箇所のみ説明していきたいと思います。その部分は改造・利用は自由です。
実際の追加コード
// ここから追加
// subNodeの設定
const body_obj = $.subNode('body');
const propeller_obj = $.subNode('propeller');
const l_wing_obj = $.subNode('l_wing');
const r_wing_obj = $.subNode('r_wing');
const tail_obj = $.subNode('tail');
const tire_obj = $.subNode('tire');
const cockpit_obj = $.subNode('cockpit');
//機体のローカル回転角度
let rollAngle_z = 0;
let rollAngle_x = 0;
// ここまで追加
// 中略
$.onUpdate(deltaTime => {
// 中略
const direction = $.getRotation().multiply(hor_rot).createEulerAngles();
const forwardRadian = direction.y * Math.PI / 180;
const sideRadian = ((direction.y + 90) % 360) * Math.PI / 180;
// ここまでは同じ
// ローカル ロール角度計算
if ($.state.steerInput.x === 0) {
rollAngle_z += (rollAngle_z > 0 ? -0.5 : 0.5);
if (Math.abs(rollAngle_z) < 0.5) rollAngle_z = 0;
} else {
rollAngle_z = Math.max(-40, Math.min(40, rollAngle_z - $.state.steerInput.x * 0.5));
}
// ローカル ピッチ角度計算
if ($.state.steerAdditionalAxisInput === 0) {
rollAngle_x += (rollAngle_x > 0 ? -0.5 : 0.5);
if (Math.abs(rollAngle_x) < 0.5) rollAngle_x = 0;
} else {
rollAngle_x = Math.max(-30, Math.min(30, rollAngle_x - $.state.steerAdditionalAxisInput * 0.25));
}
if (!(rollAngle_z == 0 && rollAngle_z == 0 && $.state.steerInput.x === 0 && $.state.steerAdditionalAxisInput === 0))
{
// ローカル軸設定
const bodyRotation = new Quaternion().setFromEulerAngles(new Vector3(rollAngle_x, 0, rollAngle_z));
body_obj.setRotation(bodyRotation);
l_wing_obj.setRotation(bodyRotation);
r_wing_obj.setRotation(bodyRotation);
tail_obj.setRotation(bodyRotation);
tire_obj.setRotation(bodyRotation);
cockpit_obj.setRotation(bodyRotation);
}
// ピッチ・ロール角度に比例した回転速度
const angSpeedFactor = Math.abs(rollAngle_z / 40);
const adjustedAngSpeed = BASE_ANG_SPEED * angSpeedFactor;
// 以降は同じ
// グローバル軸計算
let rotation_side = new Quaternion().setFromAxisAngle(
new Vector3(0, 1, 0), adjustedAngSpeed * $.state.steerInput.x * deltaTime
);
// グローバル回転設定
$.setRotation(newRotation);
// 中略
各部の解説
// subNodeの設定
const body_obj = $.subNode('body');
const propeller_obj = $.subNode('propeller');
const l_wing_obj = $.subNode('l_wing');
const r_wing_obj = $.subNode('r_wing');
const tail_obj = $.subNode('tail');
const tire_obj = $.subNode('tire');
const cockpit_obj = $.subNode('cockpit');
//機体のローカル回転角度
let rollAngle_z = 0;
let rollAngle_x = 0;
こちらで重要なのはsubNodeの定義です。通常の回転とは別に機体だけを回転させるローカルの操作をする際にここで定義したsubNodeに対して操作を行うことになります。
紫電改は部品が多いため複数定義していますが、通常のアイテムであれば一つの定義だけで済むはずです。
あとは実際の回転を制御するためrollAngle_x(X軸:上昇下降する時のピッチ角度)、rollAngle_z(Z軸:左右に曲がるときのロール角度)を定義してます。
subNodeの理解に関しては下記のイナバさんの記事が参考になります。
// ローカル ロール角度計算
if ($.state.steerInput.x === 0) {
rollAngle_z += (rollAngle_z > 0 ? -0.5 : 0.5);
if (Math.abs(rollAngle_z) < 0.5) rollAngle_z = 0;
} else {
rollAngle_z = Math.max(-40, Math.min(40, rollAngle_z - $.state.steerInput.x * 0.5));
}
同じ部分に関しては中略でガンガン飛ばしていきます。
$.state.steerInput.xは左右入力で1~-1に変動します。
0の場合は現在のロール角度(rollAngle_z)を0.5ずつ0になるようにします。
これにより一旦傾いた機体が少しずつ水平に戻ります。-0.5~0.5未満まで戻ったら機体がガタガタしないように0を入力します。
$.state.steerInput.xが0ではない場合は以下の処理に遷移して入力した方向に角度を付けます。
rollAngle_z = Math.max(-40, Math.min(40, rollAngle_z - $.state.steerInput.x * 0.5));
0.5かけているのは変化を緩やかにするための固定値です。
とりあえずここでは最大ロール角度を40で制限しました。
// ローカル ピッチ角度計算
if ($.state.steerAdditionalAxisInput === 0) {
rollAngle_x += (rollAngle_x > 0 ? -0.5 : 0.5);
if (Math.abs(rollAngle_x) < 0.5) rollAngle_x = 0;
} else {
rollAngle_x = Math.max(-30, Math.min(30, rollAngle_x - $.state.steerAdditionalAxisInput * 0.25));
}
こちらも$.state.steerAdditionalAxisInputを使っている以外はほぼ同じです。
$.state.steerAdditionalAxisInputは高度変更する場合に使用しているので現在のピッチ角度(rollAngle_x)の計算に使用します。
こちらは最大ピッチ角度を30としています。
if (!(rollAngle_z == 0 && rollAngle_z == 0 && $.state.steerInput.x === 0 && $.state.steerAdditionalAxisInput === 0))
{
// ローカル軸設定
const bodyRotation = new Quaternion().setFromEulerAngles(new Vector3(rollAngle_x, 0, rollAngle_z));
body_obj.setRotation(bodyRotation);
l_wing_obj.setRotation(bodyRotation);
r_wing_obj.setRotation(bodyRotation);
tail_obj.setRotation(bodyRotation);
tire_obj.setRotation(bodyRotation);
cockpit_obj.setRotation(bodyRotation);
}
ここが実際に機体を傾けるメイン処理となります。
最初のif文は入力なく、既に機体が水平の場合は何もしない処理です。
bodyRotationは実際に機体を傾ける値になり、こちらをsubNodeにsetRotationすることで、グローバルの回転とは別に機体の回転を制御します。
ここも通常のアイテムであれば一つのsubNodeに対して行うだけでいいはずです。
// ピッチ・ロール角度に比例した回転速度
const angSpeedFactor = Math.abs(rollAngle_z / 40);
const adjustedAngSpeed = BASE_ANG_SPEED * angSpeedFactor;
角度を付けるだけならこの処理は要らないのですが、曲がるときに機体の傾きと比例するようにしてみました。
これにより最初はゆっくり曲がりますが最大角まで傾くと曲がり方も大きくなるため違和感が減ります。
上下の場合はあんまり気にならなかったのでそのままにしています。
頭の位置に注意しよう
回転する際はsubNodeの中心から回るため、箒のようなアイテムだと箒の軸を中心に乗っているアバターが振り回されることになるためすごく酔います!
UnityでsubNodeに座標登録する時に座って頭の来る位置よりちょっと上ぐらいになるように調整するか、onStart()でbody_obj.setPosition(new Vector3(0.0, -0.8, 0.0));みたいな調整が座る位置によって必要です。
実際に動かしてみよう
こちらはクラフトモードOKかつ飛行し甲斐のあるワールドなので、アイテムが出来たら試乗に最適です!
こちらはかつて、メタバースえひめの企画で作成したワールドで、紫電改を作成するきっかけとなった場所です。自動操縦で愛媛観光地を回れる他、オレンジ色の愛媛カラーの紫電改も置いてるので、ぜひ立ち寄りください
最後に
やまちゃん (Yamachan)がソースコードを公開してくれるという大盤振る舞いで、私の紫電改もやりたかったことが大幅に短縮できてClusterの皆さまに使って頂き好評を頂きました。
これだけ頂いたのに私から返せるものは少ないため、急遽思い立って、noteの解説記事を書いてみました。
しかも、初投稿です対戦宜しくお願いします!
やまちゃんさんのコードやクラフトモードが実装されたことにより乗り物の需要や数はどんどん増えていくと思われます。
そんな時にモデル以外で乗り物の動きにちょっと工夫するだけでリッチな表現を実現すること可能なのでcluster クリエイターの皆さまの参考になれば幸いです。
とりあえず動き優先で未熟な記載もあったり、記事に誤字脱字ありましたら、申し訳ありませんがそれぞれで補完お願いします。
最後まで読んで頂き、ありがとうございました!
怪文書(読まなくていい)
$.onSteerの全てを手に入れたやまちゃんさん。
X際に放ったポストはcluster民をスクリプト沼に駆り立てた。
『俺の空飛ぶ箒か?欲しけりゃくれてやる。読め!
コードの全てをnoteに置いてきた!』
漢たちはClusterScriptを学び夢を追い続ける!
世は正に大乗り物クラフト時代!!