clusterのスクリプトでドアを制作

「clusterのスクリプト入門(2)」までで、スクリプトの基本的な書き方についてまとめました。今回は、clusterのスクリプトを使用してドアを制作します。
clusterのスクリプトでドアを制作
ドアの3DCGモデルを制作
Blenderでドアの3DCGモデルを制作し、FBXに出力します。ドアはスクリプトで開閉させるので、原点をドアの側面に設定します。FBXに出力する方法は、下記を参考にしてください。
制作環境構築
clusterのワールド制作環境を構築します。詳細は下記ページの「clusterでスクリプトを実行するための基本手順」を参考にしてください。
ドアのFBXをUnityで読み込み
Unityで空のゲームオブジェクトを作成し、名前を「door」にします。ドアのFBXをUnityで読み込み、「door」の子オブジェクトとして追加します。
「door」のインスペクタの「コンポーネントを追加」をクリックして、「Scriptable Item」を追加し、「Source Code Asset」に「door.js」を設定します。
ドアのFBXに、「Mesh Collider」コンポーネントを設定します。
スクリプトの記述
door.jsにスクリプトを記述します。
定数の宣言と初期設定
トップレベルで必要な定数を宣言し、ドアの本体部分である子オブジェクトを取得します。また、$.onStartで初期設定を行います。ドアの開閉アニメーションは$.onUpdateで制御しますが、アニメーションが何もせずに始まらないようにするために$.state.initializedをfalseに設定します。
//ドアを閉めたときの角度
const minAngle = 0;
//ドアを開いたときの角度
const maxAngle = 80;
//ドアを開く時間(秒)
const openTime = 0.8;
const doorBody = $.subNode('doorBody');
$.onStart(() => {
//初期化状態フラグ
$.state.initialized = false;
//ドアの開閉状態フラグ
$.state.isOpen = false;
//タイマー
$.state.time = 0;
});
$.onInteract(() => {
});
$.onUpdate(deltaTime => {
});
線形補完関数
線形補完関数は、2つの値の間を滑らかに補完するために使います。例えば、ドアが閉じた角度(minAngle)から開いた角度(maxAngle)へ移動するさい、途中の角度を計算するのに使います。lerp関数は、指定された割合(t)に応じて、開始点(a)から終了点(b)への中間値を計算します。
//線形補間関数
function lerp(a, b, t){
if(b == a) {
return a;
}
t = Math.min(Math.max(t, 0), 1);
return a + t * (b - a);
}
イージング関数
イージング関数は、アニメーションの動きをより自然に見せるために使います。線形補完関数は一定の速度で値を補完しますが、イージング関数を使うと、開始時はゆっくりで徐々に加速するような動きになります。
//イージング関数
function easeInQuart(t){
return t * t * t * t;
}
オブジェクトをクリックしたときの処理
ドアをクリックして開閉状態を切り替えます。最初のクリック時にドアが開くように、$.state.initializedをtrueに設定します。また、連続したクリックによる誤動作を防ぐため、前回のクリックから2秒以上経過しているかを確認します。
$.onInteract(() => {
//2秒以上経過している場合
if($.state.time > 2){
//初期化済み
$.state.initialized = true;
//ドアの開閉状態を切り替える
if($.state.isOpen == true){
$.state.isOpen = false;
}else{
$.state.isOpen = true;
}
//タイマーをリセット
$.state.time = 0;
}
});
ドアの開閉アニメーション
$.onUpdateでドアの開閉アニメーションを制御します。変数angleにminAngleを代入して、ドアの現在の角度を初期化します。角度の初期化はトップレベルで行います。これにより、毎フレーム呼び出されるたびに角度がリセットされるのを防ぎます。
$.onUpdate内では、まずdeltaTimeで経過時間を取得し、これを$.state.timeに加算します。次に、$.state.initializedがtrueだったら、ドアの開閉状態をチェックします。ドアが開いている場合は、線形補完関数とイージング関数を使用して、ドアの角度をminAngleからmaxAngleの範囲で計算します。ドアが閉じている場合も同様に、ドアの角度をminAngleからmaxAngleの範囲で計算します。
次に、Quaternionオブジェクトを作成し、Vector3(0, angle, 0)を使用してY軸回りの回転を設定します。最後に、この回転をドアの本体に適用して、実際にドアをアニメーションさせます。
//ドアの現在の角度を初期化
let angle = minAngle;
$.onUpdate(deltaTime => {
//経過時間を加算
$.state.time += deltaTime;
//初期化済みの場合
if($.state.initialized == true){
//ドアが開いている場合
if($.state.isOpen == true){
angle = lerp(minAngle, maxAngle, easeInQuart($.state.time / openTime));
//ドアが閉じている場合
}else{
angle = lerp(maxAngle, minAngle, easeInQuart($.state.time / openTime));
}
//ドアの回転を更新
const rot = new Quaternion().setFromEulerAngles(new Vector3(0, angle, 0));
doorBody.setRotation(rot);
}
});
door.js
完成したdoor.jsになります。ドアをクリックもしくはタップすると開閉します。
clusterのスクリプトはUnity上では動作しないため、スクリプトの動作確認は、ワールドをアップロードして行います。
//ドアを閉めたときの角度
const minAngle = 0;
//ドアを開いたときの角度
const maxAngle = 80;
//ドアを開く時間(秒)
const openTime = 0.8;
//ドアの本体部分を取得
const doorBody = $.subNode('doorBody');
//線形補間関数
function lerp(a, b, t){
if(b == a) {
return a;
}
//tの範囲を0から1に制限
t = Math.min(Math.max(t, 0), 1);
return a + t * (b - a);
}
//イージング関数
function easeInQuart(t){
return t * t * t * t;
}
//初期設定
$.onStart(() => {
//初期化状態フラグ
$.state.initialized = false;
//ドアの開閉状態フラグ
$.state.isOpen = false;
//タイマー
$.state.time = 0;
});
//オブジェクトをクリックしたときの処理
$.onInteract(() => {
//2秒以上経過している場合
if($.state.time > 2){
//初期化済み
$.state.initialized = true;
//ドアの開閉状態を切り替える
if($.state.isOpen == true){
$.state.isOpen = false;
}else{
$.state.isOpen = true;
}
//タイマーをリセット
$.state.time = 0;
}
});
//ドアの現在の角度を初期化
let angle = minAngle;
//毎フレームの更新処理
$.onUpdate(deltaTime => {
//経過時間を加算
$.state.time += deltaTime;
//初期化済みの場合
if($.state.initialized == true){
//ドアが開いている場合
if($.state.isOpen == true){
angle = lerp(minAngle, maxAngle, easeInQuart($.state.time / openTime));
//ドアが閉じている場合
}else{
angle = lerp(maxAngle, minAngle, easeInQuart($.state.time / openTime));
}
//ドアの回転を更新
const rot = new Quaternion().setFromEulerAngles(new Vector3(0, angle, 0));
doorBody.setRotation(rot);
}
});


