2024年09月21日 - メタバース

clusterのスクリプト入門(2)

clusterでスクリプト入門(1)」では、JavaScriptの基本と、clusterでスクリプトを実行するための基本手順についてまとめました。今回は、clusterのスクリプトの基本についてまとめたいと思います。

clusterのスクリプトの基本

clusterのスクリプトは、「Scriptable Item」コンポーネントに設定し、各オブジェクトごとに実行されます。オブジェクトは、自分自身の状態は自由に変更できますが、他のオブジェクトの状態は基本的に変更できません。

clusterのスクリプトの詳細な仕様については、下記を参考にしてください。

$.subNode

$.subNodeは、子オブジェクトを参照する関数です。子オブジェクトを操作したり、子オブジェクトのプロパティにアクセスしたりすることができます。ちなみに、関数で「$.log」や「$.subNode」など「$」がついているものは、clusterが用意した独自機能を提供する関数です。


const sphere = $.subNode('Model');

$.state

clusterは、変数を含むオブジェクトの内部状態を定期的に初期化する仕様になっています。そのため、変数は一定の間隔で初期化されます。clusterのオブジェクトはそれぞれ独自の$.stateを持ち、データを永続的に保持したい場合は$.stateを使用します。$.stateは宣言が不要で、変数や定数と異なりブロックスコープの制限も受けません。


$.state.time = 0.0;

$.onInteract

$.onInteractは、オブジェクトをクリックしたときの動作を定義する関数です。$.onInteractを使用するさいは、オブジェクトに1つ以上のColliderコンポーネントを設定する必要があります。


$.onInteract(() => {
    $.log('Click!');
});

アロー関数について

JavaScriptのユーザー定義関数は「function」を使って定義しますが、より簡潔に書くことができるアロー関数もあります。


//関数
function message(){ 
    $.log('Hello,World');
}

//アロー関数
const message = () => {
    $.log('Hello,World');
}

無名関数について

無名関数は、名前を持たない関数のことです。通常の関数と同様に機能しますが、名前を持たないため、直接定数に代入したり、即時実行したりするさいに使用されます。また、無名関数はコールバック関数としても利用されます。ちなみに、アロー関数は常に無名関数です。


//無名関数
const message = function(){ 
    $.log('Hello,World');
}

コールバック関数について

コールバック関数は、他の関数に引数として渡され、特定のタイミングで実行される関数です。例えば、$.onInteractは、オブジェクトをクリックしたさいにコールバック関数を実行します。


$.onInteract(
    //コールバック関数
    () => {
        $.log('Click!');
    }
);

$.onStart

$.onStartはスクリプトが読み込まれたさいに一度だけ実行されます。そのため、$.onStartでは主に初期化処理を行います。


$.onStart(() => {
    //初期化処理
    $.state.time = 0.0;
});

$.onUpdate

$.onUpdateはシーンが表示されている間、毎フレーム実行される関数で、ループ処理を行うために使用します。引数のdeltaTimeは、前回のフレームから現在のフレームまでの経過時間です。


$.onUpdate(deltaTime => {
    //毎フレームごとに実行する処理
});

トップレベルについて

トップレベルとは、スクリプトがロードされたときに実行されるスコープのことで、特定の関数に属さないスクリプト全体の最上位部分を指します。関数には、トップレベルでのみ使用できるもの、コールバック内でのみ使用できるもの、トップレベルとコールバック内の両方で使用できるものがあります。例えば、空間の状態を読み書きする関数(例:$.getPosition、ItemHandle.send)は、コールバック内でのみ使用可能です。


//ここはトップレベルです
const sphere = $.subNode('Model');

function message(){ 
    //関数定義内はトップレベルではありません
    $.log('Hello World');
}

//ここはトップレベルです
message();

$.onStart(()=>{
    //ここはコールバック内です
});

$.onInteract(() => {
    //ここはコールバック内です
});

$.onUpdate(deltaTime => {
    //ここはコールバック内です
});

クリックで動く球体アニメーション

ここまでで、clusterのスクリプトの基本についてまとめましたが、実践練習として、クリックで動く球体アニメーションを制作します。

制作環境構築

clusterのワールド制作環境を構築します。詳細は下記ページの「clusterでスクリプトを実行するための基本手順」を参考にしてください。

ゲームオブジェクトとコンポーネントの追加

トップバーの「ゲームオブジェクト」をクリックして、空のゲームオブジェクトを作成し、名前を「Moveobj」にします。また、子オブジェクトに球体を追加します。

「Moveobj」のインスペクタの「コンポーネントを追加」をクリックして、「Scriptable Item」と「Movable Item」を追加します。

テキストエディタでファイルを作成し、任意の場所に保存します。ファイル名は任意で構いませんが、「moveobj.js」など拡張子は「.js」にします。

保存したJavaScriptファイルを、「Scriptable Item」の「Source Code Asset」に設定します。

スクリプトの記述

「moveobj.js」にスクリプトを記述します。まず、球体をクリックしたときにアニメーション用のフラグをコンソールに表示します。フラグとは、特定の状態や条件を示すために使用される変数です。通常は真(true)または偽(false)の値を持ち、条件分岐やループの制御に使用されます。球体は最初停止した状態にしたいので、フラグは初期状態で偽(false)に設定します。


$.onStart(() => {
    //アニメーション用のフラグを初期状態でfalseに設定
    $.state.animFlg = false;
});

$.onInteract(() => {
    $.log($.state.animFlg);
});

if文を使用して、クリック時にアニメーション用のフラグが真(true)なら偽(false)に、偽(false)なら真(true)に切り替えます。


$.onStart(() => {
    $.state.animFlg = false;
});

$.onInteract(() => {
    //アニメーション用のフラグをトグルする
    if($.state.animFlg == true){
        $.state.animFlg = false;
    }else{
        $.state.animFlg = true;
    }
    $.log($.state.animFlg);
});

アニメーションに必要な定数directionを追加します。directionは、x、y、zの3つの座標値を持つ方向ベクトルです。方向ベクトルは、物体の動く方向と距離を示します。また、$.state.timeは経過時間を管理し、$.getPositionはオブジェクトの現在の位置を取得する関数です。


//方向ベクトル
const direction = new Vector3(1.0,0.0,0.0).normalize();

$.onStart(() => {
    //初期時間を0.0に設定
    $.state.time = 0.0;
    $.state.animFlg = false;
    //現在の位置を取得して保存
    $.state.pos = $.getPosition();
});

$.onInteract(() => {
    if($.state.animFlg == true){
        $.state.animFlg = false;
    }else{
        $.state.animFlg = true;
    }
});

$.onUpdate(deltaTime => {
});

$.onUpdateに球体をアニメーションさせるスクリプトを記述します。アニメーション用のフラグが真(true)の場合、$.state.timeに0.01ずつ加算します。$.setPositionはオブジェクトの位置を指定する関数で、引数としてVector3を受け取ります。経過時間によってオブジェクトの位置を変化させ、球体にアニメーションを適用します。

アニメーションは、正弦波関数「Math.sin($.state.time)」を方向ベクトルdirectionに掛けて、その結果を現在の位置$.state.posに加算することで実現しています。これにより、球体は波のような動きをします。現在の位置に直接変更を加えると予期せぬ動きになる可能性があるため、$.state.posはcloneを使って複製しています。


$.onUpdate(deltaTime => {
    if($.state.animFlg){
       //時間を0.01ずつ増加
       $.state.time += 0.01;
       $.setPosition(
           //初期位置を複製し、方向ベクトルdirectionにsin関数の値を掛けた分だけ移動
           $.state.pos.clone().add(
               direction.clone().multiplyScalar(Math.sin($.state.time))
           )
       );
    }
});

moveobj.js

完成したmoveobj.jsになります。球体をクリックすると、X軸方向にアニメーションします。

clusterのスクリプトはUnity上では動作しないため、スクリプトの動作確認は、ワールドをアップロードして行います。


//directionに、方向ベクトルを正規化して代入
const direction = new Vector3(1.0, 0.0, 0.0).normalize();

//初期設定
$.onStart(() => {
    //初期時間を0.0に設定
    $.state.time = 0.0;
    //アニメーション用のフラグを初期状態でfalseに設定
    $.state.animFlg = false;
    //現在の位置を取得して保存
    $.state.pos = $.getPosition();
});

//オブジェクトをクリックしたときの処理
$.onInteract(() => {
    //アニメーション用のフラグをトグルする
    if($.state.animFlg == true){
       //アニメーション停止
       $.state.animFlg = false; 
    }else{
       //アニメーション開始
       $.state.animFlg = true;  
    }
});

//毎フレームの更新処理
$.onUpdate(deltaTime => {
    //アニメーション用のフラグがtrueの場合のみアニメーション処理を行う
    if($.state.animFlg){
       //時間を0.01ずつ増加
       $.state.time += 0.01;
       //新しい位置を計算してオブジェクトを移動
       $.setPosition(
           //初期位置を複製し、方向ベクトルdirectionにsin関数の値を掛けた分だけ移動
           $.state.pos.clone().add(
               direction.clone().multiplyScalar(Math.sin($.state.time))
           )
       );
    }
});