見出し画像

動くクラフトアイテムがつくれるようになりました!

いつも cluster をご利用いただきありがとうございます、プロダクトマネージャーのいかりです。

10/20 に開催の「Cluster Conference 2022」の中で発表された新機能「スクリプト(JavaScript)」について、会場で展示したサンプルコードと配布したクラフトアイテムの動き方をこちらでも紹介させていただきます。
サンプルコードを使うことで、ワールドクラフトで下記のような動きがつくれるようになります!

動くクラフトアイテムのつくり方

動くクラフトアイテムをつくるにはアイテム作成時に「Scriptable Item」をつけ、スクリプトを書いて、アップロードする必要があります。
また、PC版ワールドクラフトでは、cluster内でスクリプトの編集をすることもできます!

ワールドクラフト内でのスクリプトの使い方

ワールドクラフトでスクリプトを編集するためには設定を行う必要があります。下記にその手順を示すので、参考にしてみてください!

左上の三本線からメニューを開き、さらに「設定」を開いてください。
「設定」の「その他」内の「ワールド/アイテムクリエイター向け機能」をそれぞれオン(青色)にします。
「オン」になるとスクリプトを編集できるアイテムを注視した時に案内が出るようになります。(※案内が出るのは「Scriptable Item」をつけてアップロードしたアイテムのみになります)
F12を押すと左上にスクリプトエディタが開くので、こちらで編集していきましょう!

また、Creators GuideではCluster Creator Kitでのスクリプトの活用法を紹介した記事も公開しています。こちらもぜひご覧ください。

コピペ・改変することで使えるサンプルコードを配布します!

「とりあえず試してみたい!」という方向けに「Cluster Conference 2022」で紹介したサンプルコードを配布します!
先ほどの手順をもとに、ぜひ触ってみてください(使用するには少し改変が必要なので、そちらもチェックを忘れずに!)

ドア

△タップやクリックをすると扉が開閉するようになっています
△「Scriptable Item」は一番親のオブジェクトにつけてください
const door = $.subNode("Model");
const axis = new Vector3(0, 1, 0);

$.onInteract(() => {
  let isOpen = $.state.isOpen;
  isOpen = !isOpen;
  $.state.isOpen = isOpen;
  const rot = new Quaternion()
    .setFromAxisAngle(axis, isOpen ? -90 : 0);
  door.setRotation(rot);
});

△インタラクションのシンプルなサンプルです。間の動きはないので、 onInteract 内で処理しています。

移動する床

△左右の動きを繰り返す内容になっています(方向を変えれば前後移動でも使えます)
△「Scriptable Item」は一番親のオブジェクトにつけてください
const tile = $.subNode("Tile");
const period = 3;
const width = 4;

const trapezoidalWave = (t) => {
  if (t < 0.25) {
    return t * 4;
  } else 
  if (t < 0.5) {
    return 1;
  } else
  if (t < 0.75) {
    return 3 - t * 4;
  } else {
    return 0;
  }
};

$.onUpdate(deltaTime => {
  let time = $.state.time ?? 0;
  time += deltaTime;
  $.state.time = time;
  const pos = new Vector3(
    trapezoidalWave(time % period / period) * width - width / 2, 
    0, 
    0);
  tile.setPosition(pos);
});

△ループの動きのサンプルです。onUpdate内で毎フレームの位置を更新しています。

エレベーター

△動き方としては扉の開閉と上下の移動を繰り返す内容になっています
△「Scriptable Item」は一番親のオブジェクトにつけてください
const container = $.subNode("Container");
const doorLeft = $.subNode("Door_L");
const doorRight = $.subNode("Door_R");

const waitTimeSec = 10;
const transitTimeSec = 10;
const period = (waitTimeSec + transitTimeSec) * 2;

const targetHeight = 4;
const offsetHeight = 0.275;
const doorWidth = 0.5;

const easeInOutQuad = (t) => {
  return t < 0.5 ? 2 * t * t : 1 - Math.pow(-2 * t + 2, 2) / 2;
};

const upwardsCagePosYRatio = (time) => {
  if (time <= waitTimeSec) {
    return 0;
  } else {
    return easeInOutQuad((time - waitTimeSec) / transitTimeSec);
  }
};

const cagePosYRatio = (time) => {
  time = time % period;
  if (time < period / 2) {
    return upwardsCagePosYRatio(time);
  } else {
    return 1 - upwardsCagePosYRatio(time - period / 2);
  }
};

const doorPosXRatio = (time) => {
  time = time % (period / 2);
  if (time <= waitTimeSec) {
    return 1;
  } else {
    return 0;
  }
};

$.onUpdate(deltaTime => {
  let time = $.state.time ?? 0;
  time += deltaTime;
  $.state.time = time;
  container.setPosition(
    new Vector3(
      0, 
      cagePosYRatio(time) * targetHeight + offsetHeight, 
      0));
  doorLeft.setPosition(
    new Vector3(doorPosXRatio(time) * doorWidth, 0, 0));
  doorRight.setPosition(
    new Vector3(doorPosXRatio(time) * -doorWidth, 0, 0));
});

△ループの動きを少し発展させたサンプルです。easing をかけると動きがそれっぽくなります。

倒れる壁

△タップやクリックをすると壁が倒れるようになっています
△「Scriptable Item」は一番親のオブジェクトにつけてください
const container = $.subNode("Container");
const minAngle = 0.0;
const maxAngle = 90.0;
const openTimeSec = 0.8;
const closeTimeSec = 2;

const lerp = (a, b, t) => {
  if (b == a) return a;
  t = Math.min(Math.max(t, 0), 1);
  return a + t * (b - a);
};

const easeInQuart = (t) => {
  return t * t * t * t;
};

const easeOutBounce = (t) => {
  const n1 = 7.5625;
  const d1 = 2.75;
  
  if (t < 1 / d1) {
      return n1 * t * t;
  } else if (t < 2 / d1) {
      return n1 * (t -= 1.5 / d1) * t + 0.75;
  } else if (t < 2.5 / d1) {
      return n1 * (t -= 2.25 / d1) * t + 0.9375;
  } else {
      return n1 * (t -= 2.625 / d1) * t + 0.984375;
  }
};

const easeInBounce = (t) => {
  t = Math.min(t, 1);
  return 1 - easeOutBounce(1 - t);
};

$.onUpdate(deltaTime => {
  let time = $.state.time ?? 0;
  time += deltaTime;
  $.state.time = time;

  let angle = minAngle;
  if ($.state.open) {
    angle = lerp(minAngle, maxAngle, easeInQuart(time / openTimeSec));
    if (time > 5) {
      $.state.time = 0;
      $.state.open = false;
    }
  } else {
    angle = lerp(maxAngle, minAngle, easeInBounce(time / closeTimeSec));
  }

  const rot = new Quaternion()
    .setFromEulerAngles(new Vector3(angle, 0, 0));
  container.setRotation(rot);
});

$.onInteract(() => {
  if (!$.state.open && $.state.time > closeTimeSec) {
    $.state.time = 0;
    $.state.open = true;
  }
});

△ドアをさらに発展させたようなイメージです。こちらも easing をかけて動きに緩急をつけています。

ぜひご活用ください!

Cluster Creator Kitを使うにあたって便利な機能がまたひとつ増えたかなと考えております。またJavaScriptに触れたことがある方はもちろん、これまで触れてこなかった方にも今後ご活用いただけると非常にうれしく思います。

この機能はさらに拡張をする予定ですので今後のアップデートもお楽しみにしていただければと思います。