見出し画像

乗り物から飛翔体を撃つ方法

むさしは
Cluster Script Advent Calendar 2024
12月21日枠をいただきました

前半戦の記事はこちら

この記事では
先人たちのスクリプトをミックスして
キメラを召喚する方法をまとめます
具体的には
借り物パッチワークの
キメラスクリプトで
『乗り物から飛翔体を撃つ』という
少年の夢を叶えるお話です(笑)


1 キメラスクリプト

1 キメラとは

キメラとは
異なる動物の複数の部位で構成される
妖怪のことです

歌川国芳 Tokyoアーカイブ

日本では
頭が猿で足が虎、尻尾は蛇の
『鵺』が有名ですね

2 キメラスクリプト

むさしがつくるアイテムのスクリプトは
自分が編み出したものではなく
先人たちのスクリプトをミックスした
キメラスクリプトです(笑)

2 乗り物と飛翔体

3 乗り物

1 オブジェクト『Small』

・初期表示(表示)
・メッシュのあるオブジェクト構成は
 『raisama-s』
 『funnel-s』
 『kurokumo-s』の3つです

Small

2 オブジェクト『point』

・初期表示(非表示)
・メッシュのあるオブジェクト構成は
 『raisama』
 『funnel』
 『kurokumo』の3つです

point

3 主なコンポーネント

・『Ridable Item』
・『Item Audio Set List』
・『Humanoid Animation List』
・『Scriptable Item』

乗り物コンポ

4 スクリプト

//cluster公式さん【回転定義】
const speed =  50.0;
const axis = new Vector3(0.0, 0.0, 1.0);
const subNode1 = $.subNode("funnel-s");
const subNode2 = $.subNode("funnel");

//ミッコちゃん【表示非表示定義】
const kawarimi1 = $.subNode("small");
const kawarimi2 = $.subNode("point");

//vinsさん【プロシージャモジュール】
const kawarimiProc = () => {
//ミッコちゃん【表示非表示指令】
      let active1 = kawarimi1.getEnabled();
      let active2 = kawarimi2.getEnabled();
  
      if (active1) {
          active1 = false;
          active2 = true;
      } else {
          active1 = true;
          active2 = false;
      }

      kawarimi1.setEnabled(active1);
      kawarimi2.setEnabled(active2);
  
      $.state.active1 = active1;
      $.state.active2 = active2;          
};

//ニンニン猫さん【アニメ定義】
const timeLength = 0.1;
const timeLengthMax = 3.333;
const animation = $.humanoidAnimation("Anim1");
let rideplayer = null;

//cluster公式さん【効果音定義】
const se = $.audio("Audio1");
const audioPosition = $.subNode("AudioPosition");

//やまちゃん【乗り物スピード定義】
const VER_SPEED = 10; // [m/s]
const HOR_SPEED = 30; // [m/s]
const ANG_SPEED = 90; // [deg/s]

const hor_rot = new Quaternion().setFromAxisAngle(new Vector3(0, 1, 0), 0);


$.onStart(() => {
//cluster公式さん【初期化】
    $.state.driver = null;
    $.state.steerInput = new Vector2(0, 0);
    $.state.steerAdditionalAxisInput = 0;

//きえらさん【初期位置取得&初期化】
    $.state.orijinPos = $.getPosition();
    $.state.orijinRot = $.getRotation();
    $.state.count = 0;
});

$.onRide((isGetOn, player) => {
    $.state.isRiding = isGetOn;
    $.state.player = isGetOn ? player : null;
    $.state.driver = (isGetOn) ? player : null;

    if (isGetOn) {
//ニンニン猫さん【アニメ指示】
        $.state.clicked = true;
        $.state.time = 0.0;
        $.state.sumtime = 0.0000;
        rideplayer = player;
        player.setHumanoidPose(animation.getSample($.state.sumtime));

		se.play();

//vinsさん【プロシージャモジュール】
        kawarimiProc();

    } else {
//vinsさん【プロシージャモジュール】   
        kawarimiProc();

//ニンニン猫さん【アニメ解除】
        $.state.clicked = false;
        player.setHumanoidPose(null);

        se.play();
        
//きえらさん【設置時の座標に配置など】
    	$.setPosition($.state.orijinPos);
    	$.setRotation($.state.orijinRot);
        $.state.count = 0;
    }
});

//cluster公式さん【乗り物平面移動トリガー】
$.onSteer((input, player) =>{
    $.state.steerInput = input;
});

//cluster公式さん【乗り物垂直移動トリガー】
$.onSteerAdditionalAxis((input,sammon,player) => {
    $.state.steerAdditionalAxisInput = input;

//きえらさん【スイッチ】
    if (sammon) { 
		$.state.count++;
		  switch($.state.count) {  
			case 1:
//cluster公式さん【クリエイト・召喚呪文(笑)】
				const id = new ItemTemplateId("25b1b6db-2296-4e7c-9c0d-fd0429301769");//DM
				const position = $.getPosition().clone().add(new Vector3(0, 1, 0));
				const rotation = $.getRotation();
	  			$.createItem(id, position, rotation);
			break;
		default:
		$.state.count = 0;
	  }
	}  
});

$.onUpdate((deltaTime) => {
//cluster公式さん【回転指示】  
    subNode1.setRotation(subNode1.getRotation().multiply(new Quaternion().setFromAxisAngle(axis, speed *   deltaTime)));

//cluster公式さん【乗車稼働装置】
    if (!$.state.driver) {
        return;
    }
    if (!$.state.driver.exists()) {
        $.state.driver = null;
        return;
    }

//cluster公式さん【回転指示】
    subNode2.setRotation(subNode2.getRotation().multiply(new Quaternion().setFromAxisAngle(axis, speed *   deltaTime)));

//ニンニン猫さん【アニメ稼働】
    if ($.state.clicked) {
        $.state.time += deltaTime;
        $.state.sumtime += deltaTime;
        if ($.state.time >= timeLength) {
            $.state.time = 0;
            if ($.state.sumtime >= timeLengthMax) {
                $.log("timeLengthMax");
                $.log($.state.sumtime);
                $.state.sumtime -= timeLengthMax;
            }
            rideplayer.setHumanoidPose(animation.getSample($.state.sumtime));
        }
    }

//やまちゃん【乗り物($) のワールド座標での向きを求めるソースコード】
    // note: original script causes error during changing avatar (null returned)
    //const direction = $.state.driver.getRotation().createEulerAngles();
    // player sit position is horizontally rotated 180 degs from ridable item
    const direction = $.getRotation().multiply(hor_rot).createEulerAngles();

//cluster公式さん【乗り物機能】
    const forwardRadian = direction.y * Math.PI / 180;
    const sideRadian = ((direction.y + 90) % 360) * Math.PI / 180;

//cluster公式さん×やまちゃん【移動量】コラボ
    const vectorForwardInput = new Vector3(
        Math.sin(forwardRadian) * $.state.steerInput.y * deltaTime * HOR_SPEED,
        0,
        Math.cos(forwardRadian) * $.state.steerInput.y * deltaTime * HOR_SPEED
    );
    const vectorSideInput = new Vector3(
        Math.sin(sideRadian) * $.state.steerInput.x * deltaTime * HOR_SPEED,
        0,
        Math.cos(sideRadian) * $.state.steerInput.x * deltaTime * HOR_SPEED
    );
    const vectorVerticalInput = new Vector3(
        0,
        $.state.steerAdditionalAxisInput * deltaTime * VER_SPEED,
        0
    );

    const newPosition = $.getPosition()
        .add(vectorForwardInput)
        //.add(vectorSideInput)
        .add(vectorVerticalInput);

    $.setPosition(newPosition);

//やまちゃん【旋回反映】
    let rotation_side = new Quaternion().setFromAxisAngle(
        new Vector3(0, 1, 0), ANG_SPEED * $.state.steerInput.x * deltaTime);
    const newRotation = $.getRotation().multiply(rotation_side);

    $.setRotation(newRotation);

});

5 乗降時のスケール調整

乗り降りの際の
巨大化と極小化は
ミッコちゃんの表示非表示切り替えの
ソースコードを
vinsさんのプロシージャモジュールで
呼び出しています
ミッコちゃんとvinsさんに感謝です

6 乗降時のニンニンポーズ制御

乗り降りの際の
ニンニンポーズは
ニンニン猫さん(提督)のソースコードを
お借りしています
提督に感謝です
※ソースコードは提督から
 直接いただきましたので
 アニメの記事ではありませんが
 紫電改のnote記事をご紹介します

7 降車時に元の位置に戻る機能

降りた後にの
元の位置に戻る『orijinpos機能』は
きえらさんのソースコードを
お借りしています
きえらさんに感謝です

8 乗り物機能と旋回機能

Cluster公式さんが
ワークラで乗り物を乗れるように
してくれました
そしてその機能を
やまちゃんが
さらに魅力的にしてくれました
Cluster公式さんと
やまちゃんに感謝です

9 召喚呪文【再掲】

後述の飛翔体を作成して
クラフトアイテムのIDを取得したのち
乗り物垂直移動トリガーを
このコードにかえることで
乗り物が上下する際
飛翔体がでるようになります
柱:XXX…XXX  = クラフトアイテムのID
const id = new ItemTemplateId("XXX…XXX");//

//cluster公式さん【乗り物垂直移動トリガー】
$.onSteerAdditionalAxis((input,sammon,player) => {
    $.state.steerAdditionalAxisInput = input;

//きえらさん【スイッチ】
    if (sammon) { 
		$.state.count++;
		  switch($.state.count) {  
			case 1:
//cluster公式さん【クリエイト・召喚呪文(笑)】
				const id = new ItemTemplateId("25b1b6db-2296-4e7c-9c0d-fd0429301769");//DM
				const position = $.getPosition().clone().add(new Vector3(0, 1, 0));
				const rotation = $.getRotation();
	  			$.createItem(id, position, rotation);
			break;
		default:
		$.state.count = 0;
	  }
	}  
});

4 飛翔体

1 オブジェクト『point』

・初期表示(表示)
・メッシュのあるオブジェクト構成は
 『plasma』
 『saucer』の2つです

plasna

2 主なコンポーネント

・『Movable Item』
・『Item Audio Set List』
・『Scriptable Item』

コンポーネント

3 スクリプト

//きえらさん【時間制御定義】
const obj = $.subNode("point");

const waitTime1 =3.0;
const waitTime2 =6.0;
const v3 = new Vector3(0,0,0);

//vinsさん【飛翔体定義】
const star = $.subNode("point");

const starVec = new Vector3(0,0,5);
const startZ = 0;
const goalZ = 30;

//Cluster公式さん【回転定義】
const subNode0 = $.subNode("saucer");
const subNode1 = $.subNode("p1");
const subNode2 = $.subNode("p2");

const speed =  200.0;
const axis0 = new Vector3(0,0,1);
const axis1 = new Vector3(1,0,0);
const axis2 = new Vector3(1,0,0);

//Cluster公式さん【効果音定義】
const se = $.audio("Audio1");
const audioPosition = $.subNode("AudioPosition");

//Cluster公式さん【効果音トリガー】
$.onStart(() => {
    se.attach(audioPosition);
    se.play();
});

$.onUpdate((deltaTime) => {
//Cluster公式さん【効果音定義】
    subNode0.setRotation(subNode0.getRotation().multiply(new Quaternion().setFromAxisAngle(axis0, speed *   deltaTime)));
    subNode1.setRotation(subNode1.getRotation().multiply(new Quaternion().setFromAxisAngle(axis1, speed *   deltaTime)));
    subNode2.setRotation(subNode2.getRotation().multiply(new Quaternion().setFromAxisAngle(axis2, speed *   deltaTime)));

//きえらさん【時間制御】
if(!$.state.init) {
  $.state.init = true;
  }

  let time1 = ($.state.time1 ?? 0) + deltaTime;
  $.state.time1 = time1;

  if (time1 > waitTime1) {
  
  	if (!$.state.flg) {
  	  obj.setPosition(v3);
 	    $.state.flg = true;
  	}

    $.state.time1 = 0;
    } 

    let time2 = ($.state.time2 ?? 0) + deltaTime;
    $.state.time2 = time2;

    if (time2 > waitTime2) {
      //Cluster公式さん【消滅呪文】
      $.destroy();
      $.state.time2 = 0;
    } 

    if (!$.state.flg) {
        return;
    }

//vinsさん【飛翔制御】
    let pos = star.getPosition();
    if(pos.z > goalZ) {
      		star.setPosition(new Vector3(pos.x,pos.y,startZ));
    } else {
      		star.setPosition(pos.add(starVec.clone().multiplyScalar(deltaTime)));
    }
});

4 時間差トリガー

飛翔体は
世界に召喚されてから
3秒間その場にとどまり
その後、秒速5mで
前方向に30m飛翔して消滅します
きえらさんの時間制御と
vinsさんの飛翔制御の
4次元時空ソースコードのおかげです
きえらさんとvinsさんに感謝です

5 召喚技

召喚するための
クラフトアイテムIDの設定方法は
こちらの記事が参考になります

6 乗り物の意外な可能性

乗り物機能は
方向キーとスペース・シフトキーの
複数トリガーが魅力で
乗り物以外の表現の可能性も
広げてくれています!
この記事も参考にしてみてください

7 大事にしてること

スクリプトの領域を
・定義系エリアと
・トリガー系エリア
・毎フレーム処理エリアの3つに分け
先人たちのスクリプトを
名刀『鵺斬丸』で
この領域の何処に当てはまるか
切り分けています

銀河旋風ブライガー

頭は猿で足が虎、尻尾は蛇で…
まさにキメラスクリプトですね(笑)

8 むすびに

いかがでしたか?
先人たちが公開してくれている
スクリプトをミックスすれば
子どもの頃の夢をかなえられる
クラフトアイテムをつくれます
皆様もぜひ挑戦してみてください!

これまでの成果を
アドカレとしてまとめる機会をくれた
clusterさんに感謝です

これからも
日々精進してまいります