見出し画像

倒れる壁をつくる(備忘録 その4)

 スクリプトアイテムを実装時につまづいたポイントを備忘録として記録しております。今回はCluster Creater Guideの記事を参考として、Cluster Conference 2022にも登場した倒れる壁を作っていきたいと思います。

オブジェクトのピボットの位置に注意

Unityの3Dオブジェクトを利用した以下のような2×3の壁を作っていきます。

何も考えずに作った場合

 空のオブジェクト(名称 倒れる壁)にScriptabile Itemのコンポーネントを付けて、Cube(2×3×0.15)の壁を子オブジェクトとしてつけていきます。この時、ピボットはCubeの中央に位置しています。

実装してみる

 アイテムをclusterにアップロードして、スクリプトを入れてみる。

F12でスクリプトを編集する

Cluster Creators Guideの通りコードを入力する。subNodeで指定するアイテム名は「Cube」に変更。

const container = $.subNode("Cube");
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;
  }
});


真ん中の位置で倒れてしまった


ピボットの位置を壁の下部に変更

 回転ドアの時と同様にピボットの位置を回転させたいところに移動していきます。

親となる空のオブジェクトとCubeの間にピポットを変更するため、もう一つ空のオブジェクトを入れました

①Scriptabile Itemを付けた空のオブジェクト(親)
②回転する位置にピボットを移すための空のオブジェクト(子)
③3Dオブジェクト(孫)

再度実装してみる

この状態で先ほどと同様に実装してみます。コードは以下の通り②ピボット位置変更のオブジェクトを回転するように修正します。

const container = $.subNode("ピボット位置変更");
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;
  }
});


ちゃんと倒れた!!

改変してみる

先ほどのコードの上部分が角度を指定している箇所なので、ここをいじってみましょう。
const maxAngle を90.0 → -90.0に変更して以下のように設定すると。

const container = $.subNode("ピボット位置変更");
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;
  }
});


向こう側に倒れる

これはアイテムを設置する際に回転させれば同じことできますが・・・。

さらにconst maxAngle を-150にすると

カンファレンスのデモであった落ちる仕様に

カンファレンスでスワンマンさんが落ちていった壁が再現できました。このままだと少し倒れるスピードが速いので、const openTimeSec を1.2くらいに調整したほうがよいかも!

お試しあれ!

次回は上下にうごくオブジェクトをつくる。


この記事が気に入ったらサポートをしてみませんか?