基本から作例まで、当日いただいた質問に回答します!【 #ClusterWorkshop #11 「スクリプト1mmもわからん!なきみへ!」】

目次

こんにちは、プロダクトマネージャーの Smith です!
1/18 にスクリプト初心者のための Cluster Workshop を開催させていただいた所、会場で沢山の質問をいただきました。
当日は時間の都合でお答えできなかった質問も、この記事でじっくり回答しておりますのでぜひご一読ください!

当日参加できなかった方は、こちらのアーカイブ動画をぜひご覧ください!

Q:if 文や switch 文はありますか?

あります!
cluster のスクリプトは JavaScript という一般的なプログラミング言語で記述します。
JavaScript の仕様として定義されている if 文などの構文はほとんどサポートされています。
より cluster のスクリプトを理解するために、JavaScript を覚えるのもオススメです。

Creators Guideでもスクリプトの解説をいくつか公開しています。

Q:スクリプトリファレンス、何を見たら何がわかるのかわかりません、まずはどこから読み始めるとよいでしょうか

本稿では基本的な読み方を解説します!
Cluster Workshop で使った $.onInteract を例にします。

まずは最初の方を見てみましょう。

ここには情報が以下の順で記載されています。

  • スクリプトに記述する機能の名前
  • 機能にどんな情報を渡すべきでどんな情報が返されるか
  • 機能の概要

記載内容はプログラミングに関するある程度の前提知識がある想定で書かれているので、最初は雰囲気がわかれば十分です。
次いで、その機能の実装例が記載されている場合があります。ここはコピペチャンスです!

末尾には、最初に書かれていた「機能にどんな情報を渡すべきでどんな情報が返されるか」についての詳細が記載されています。

どの機能も基本的にはこの構成で説明が記載されています。
スクリプトのサンプルが記載されている機能も多いので、わからなくてもとりあえずコピペして試してみるのもオススメです。

Q:スクリプトの構文はどう組み立てたらよいですか?

まずは今回の Cluster Workshop で触れた範囲の 2つを押さえましょう!

  1. 〇〇したときに
  2. △△をする

Cluster Workshop では「〇〇したときに」として「ユーザーがアイテムをクリック/タップした時」のスクリプトを記述しました。

$.onInteract(() => {
});

また、「△△をする」は「ログ出力をする」スクリプトを記述しました。

$.log("こんにちは");

この2つを構文として組み立てるときは、かならず「〇〇したときに」の波括弧 {} の中に「△△をする」のスクリプトを書きましょう。

$.onInteract(() => {
  $.log("こんにちは");
});

「〇〇したときに」

「〇〇したときに」は「ユーザーがアイテムをクリック/タップした時」以外にも種類があります。
スクリプトリファレンスのこのページonCollideonExternalCallEnd などの on が先頭についているものがそれらに該当します。

中でもスクリプト初心者に扱いやすいのは、単独で利用できる onGrabonUpdate です。onGrab は「アイテムを掴んだ時」なので、 Unity 側でアイテムを作るときに Grabbable Item コンポーネントがついている必要があります。

もう一つの onUpdate は少し特殊です。
「◯◯をした時」ではなく、「1秒間に数十回の頻度で処理されるその都度」です。例えば下記の様に「ログ出力をする」処理を onUpdate の中に書いた場合、ログ出力が 1秒間に数十回行われます。

$.onUpdate(() => {
  $.log("こんにちは");
});

「△△をする」

「△△をする」は例を挙げるときりがないので、簡単で目に見えてわかりやすいものをひとつ紹介します。
このスクリプトは「ユーザーがアイテムをクリック/タップした時」に、「アイテムを削除する」というものです。

$.onInteract(() => {
  $.destroy();
});

このように、基本的な 2つの構文を組み合わせるところから始めましょう。

気になった方はスクリプトリファレンスを試しに読んでみてください。
スクリプトリファレンスは、最初は 2割くらいの理解を目指しましょう。

Q:“こんにちは” のくくりの記号はキーボードのどこにありますか?

” (ダブルクオーテーション) のことですね!
日本語配列(日本で販売されている一般的なキーボード)なら Shift を押しながら数字の 2 キーを押すことで入力できます。
キーボードの種類によっては、配置はこの限りではありません。

Q:$.onInteract(() => {}) の () の中を書く場合と書かない場合があるのはなぜ?どちらが良いですか?

$.onInteract(() => {}) の () の中身は PlayerHandle というプレイヤー情報を扱うものが入りますが、 () の中身を書くか書かないかは任意です、プレイヤー情報を扱わない場合は省略できます。
とりあえず書いておく分には支障はないので、書くか書かないか悩むようであればとりあえず書いておいたほうが無難かと思います。

Q:ワールドクラフト以外のワールドも今日とおなじやり方ですか?

Creator Kit で作成したワールドでは、アプリ内で F12 を押下してスクリプトをエディットすることが出来ません。
Creator Kit で作成したワールド内のアイテムを更新したい場合、現在はワールドごとアップロードし直す必要があります。

アイテムのスクリプトをテストしながら作りたい場合、一旦ワールドクラフト用のアイテムでスクリプトを編集し、うまく動いたものを Creator Kit で作成したワールドに反映する、などで開発の手間を減らすことが出来ます。 

一部のスクリプトの機能は、 Creator Kit で作成したワールドでのみ有効なものがあります。
対象の機能はスクリプトリファレンス上で「Creator Kit のワールドに組み込まれたアイテムでしか利用できません」のような記載がありますのでご確認ください。

Q:Unity でスクリプトの挙動をみる方法はありますか?

公式にはスクリプトの挙動を見る方法は提供していません。
clusterのクリエイターが制作しているものであれば、かおもさんの CSEmulator がオススメです!

Q:Visual Studio Code の強みを教えてください

エディタは好みなので、使い慣れたものが一番です!
という前提のもと、簡単に VisualStudio Code の説明をします。

VisualStudio Code は、様々なプログラミング言語を書くことに向いているエディタです。
また、世界中のデベロッパーが開発した拡張機能を簡単に手に入れることができます。
拡張機能を利用することにより、スクリプトを書く目的に対してより効率の良い開発ができるようになります。
cluster スクリプトを書く場合でも、JavaScript 言語に関する拡張であればある程度は流用はできると思います。

Q:Visual Studio Code のインストール先ディレクトリは Cドライブ以外でも大丈夫ですか?

大丈夫です! 
任意のフォルダに配置していただいても Visual Studio Code のインストールに支障ありませんが、よく分からなければ初期値で示される場所へのインストールを推奨します。

Creators Guideでも Visual Studio Code の導入方法について解説しておりますので、ぜひご覧ください。

Q:VisualStudio Codeの拡張の自慢をしてください

公式で推奨する拡張はありません・・・
もし「clusterで使うなら、これが便利だよ!」ということを知っている方はXやハロクラ拡張版で教えていただけると嬉しいです!

ベータ機能のスクリプトを使って会話を分岐させるゲームを作れますか?

当日は、詳細をお伺いしてサンプルを提示できる時間がなかったため割愛させていただいた質問です。
実現したいことが下記のようなものであると仮定して解説していきます。

  • ボタンに見立てた cube の形のアイテムが 2つある
  • 2つの cube とは別に、テキストを表示するアイテムがある
  • 2つの cube のいずれかをクリック/タップした際、テキスト(会話)の表示内容を変える

この場合、まずテキストが表示できるアイテムをアップロードしておく必要があります。
クラフトアイテムでなくワールド内のアイテムの場合でも、アイテム個別のアップロードは不要ですが、テキストを表示するためのアイテムが必要です。
この挙動を実現したい場合、今回のイベントで解説した知識以外の要素が多分に含まれますのでご了承ください。

アイテムの準備

テキストは指定の構造で Unity 上で GameObject を作成します。
コンポーネントは TextView コンポーネントを利用します。
今回はベータ機能を利用するため、予めメニューバーの Cluster > Settings からベータ機能を有効にしておいてください。
予め、以下のように Unity 側でアイテムの準備をします。

テキストを出し分けるアイテム
ScriptableItem コンポーネントを追加した GameObject と、その子要素として TextView コンポーネントを追加した GameObject を作ります。

ボタンとして使うアイテム 2つ
ScriptableItem コンポーネントを追加した GameObject を作ります。

スクリプトの編集

今回記述するスクリプトは Cluster Workshop で触れた範囲を大きく超えるため、解説は割愛します。

テキストを出し分けるアイテム
$.onStart(p => {
  const node = $.subNode("Text");
  node.setText("猫派ですか、犬派ですか");
});

$.onReceive((id, v, sender) => {
  const n = $.subNode(OUTPUT_SUB_NODE_NAME);
  switch (v) {
    case "cat": n.setText("ニャン!"); break;
    case "dog": n.setText("ワン!"); break;
  }
});
ボタンとして使うアイテム 1
const OUTPUT_SUB_NODE_NAME = "Text";

$.onStart(p => {
  const n = $.subNode("Text");
  n.setText("犬!");
});

$.onInteract((id, v, sender) => {
  const items = $.getItemsNear($.getPosision(), 3);
  for (let i = 0; i < items.length; i++) {
    items[i].send("", "dog");
  }
});
ボタンとして使うアイテム 2
const OUTPUT_SUB_NODE_NAME = "Text";

$.onStart(p => {
  const n = $.subNode("Text");
  n.setText("猫!");
});

$.onInteract((id, v, sender) => {
  const items = $.getItemsNear($.getPosision(), 3);
  for (let i = 0; i < items.length; i++) {
    items[i].send("", "cat");
  }
});

ここまで作ると 1つ目のボタンを押した時と2つめのボタンを押した時で違うテキストを表示させることができます。

今回は基本的な実装だけ紹介しました。
よりゲームっぽく仕立てるためには複雑な実装が必要となります。
まずはいきなり難しいことをやらずに、分かる要素が 8割、難しい要素やわからない要素が 2割くらいのものから始めて少しずつ覚えていきましょう。

Q:車みたいに4つ部品を別々の位置で回転させることできますか?スクリプトを書いたら車本体を中心に衛星みたいな回転してしまいました。

できます!
衛星みたいに回転する現象は、おそらく回転させる対象が意図したものと異なるか、回転させる対象の座標が問題だったのかと思われます。

回転について、理解しやすくするために 2D の回転で解説します。
車の部品が入れ子構造になっている場合、本来回転させたい部品ではないものを回転させた場合には衛星のように回転する場合があります。

下の図を見てください。
大きな円の点線が回転させたい部品の親要素で、赤い点が親要素の中心(原点)とします。
実線で書かれた 2つの円は車の部品です。
ここで誤って親要素を回転させてしまった場合、本来回転させたい分品である子要素が、親要素の原点を中心に回転してしまいます。
料理のお皿が置いてある中華テーブルを回している状態と同じです。

親要素を回転させることで子要素も一緒に回転させることが目的なのであれば、子要素の座標を見直してください。
図のように親要素の原点が子要素の原点と重なるのであれば、子要素はその場にとどまりつつ回転します
たとえ中華テーブルでも、テーブルの中心にお皿が置いてあれば、そのお皿はその場で回転します。

子要素が親要素の原点からずれていると、子要素は親要素の原点を中心とした軌道で回転します。

子要素を親要素の原点に置きたい場合、基本的には Unity のインスペクタ上で TransformPosition が (0, 0, 0) になっていれば OK です。

例えば、BlenderでつくったモデルをUnityに持ってくる場合は下記の記事が参考になります。

上記は親要素を回転させる場合の解説でしたが、子要素を直接回転させる場合の座標は (0, 0, 0) でなくても問題ありません。
車の部品として購入したアセットなど、外部から持ってきたオブジェクトの原点が見た目に反してずれていた場合は、直接解決するすべはありません。
目視で原点を調整するか、サードパーティが開発したエディタ拡張などでの解決になるかと思います。

Q:自転車の変速機みたいなものは作れますか?

ここでは RidableItem コンポーネントで作った乗り物の速度をユーザー任意の操作で可変にしたいという質問であるとして解説します。
任意の GameObject に以下のコンポーネントを追加します。

  • MovableItem
  • Ridable Item
  • Steer Item Trigger
  • Scriptable Item

Steer Item Trigger の設定は下記のとおりです。

  • Move Input Triggers
    • Target: this
    • Key: 任意、ここでは “move” とします
    • Value Input: Vector2
  • Additional Axis Input Triggers
    • Target: this
    • Key: 任意、ここでは “axis” とします
    • Value Input: Float

スクリプトの編集

このスクリプトでは Steer Item Trigger から送信された state を取得し、それに応じたギアチェンジ(移動速度変更)を行っています。
Cluster Workshop で触れていない要素を多く使っているため、詳細な説明は割愛します。
ベータ機能を用いていますのでご注意ください。

$.onStart(() => {
  $.state.currentGear = 1;
  $.state.lastGearControl = 0;
});

$.onUpdate(() => {
  const axis = $.getStateCompat("this", "axis", "float");
  const gearControl = (axis > 0)
    ? Math.floor(axis)
    : Math.ceil(axis);

  if (gearControl !== $.state.lastGearControl) {
    $.state.lastGearControl = gearControl;
    $.state.currentGear = $.state.currentGear + gearControl;

    if ($.state.currentGear > 5) {
      $.state.currentGear = 5;
    } else if ($.state.currentGear < 1) {
      $.state.currentGear = 1;
    }
  }

  const vector = $.getStateCompat("this", "move", "vector2");
  const pos = $.getPosition();
  const distance = 0.1 * $.state.currentGear;
  const newX = pos.x + vector.x * distance;
  const newZ = pos.z + vector.y * distance;
    
  $.setPosition(new Vector3(newX, pos.y, newZ));
});

Q:スクリプトを各ユーザで独立に動作させる確実な方法はありますか?

あります!

Unity コンポーネントを用いる方法

Cluster Workshop のイベント中で触れられていた、ユーザー毎にアイテムを生成する方法で実現できます。

  • Interact Item Trigger コンポーネントを Create Item Gimmick のトリガにする
  • Create Item Gimmick で Scriptable Item が付与された Prefab を生成する

スクリプトを用いる方法

スクリプトのみで処理させる場合は、ユーザー毎の状態を記憶しておくことで各ユーザーで独立しているように見せることが出来ます。

ここではユーザー毎にアイテムをクリック/タップした回数に応じてコンソール出力するログの内容が変わるアイテムを、コピペで動く状態で記載します。
Cluster Workshop で触れていない要素を多く使っているため、詳細な説明は割愛します。
ベータ機能を用いていますのでご注意ください。

$.onStart(() => {
  $.state.userProgress = {};
});

$.onInteract((playerHandle) => {
  let count = $.state.userProgress[playerHandle.id] || 1;
  $.log(`${count} 回クリックしたよ!`);
  $.state.userProgress = {...$.state.userProgress, [playerHandle.id]: count + 1};
});

この例では、「誰が」という情報と「何回クリックしたか」という情報を JavaScript のオブジェクトとして保存しています。保存する場所は $.state です。$.state は短時間の間、情報を記憶しておくための特殊な存在です。

Q:サンプルコード集「X軸方向に回転する」は設置した瞬間回り始めますが、「ワールド入室時」など決まったタイミングで動き始めさせることは可能ですか?

厳密なワールド入室時のタイミングでの起動は出来ませんが、任意のアイテムに新しく入室した誰かが触れたら、という条件であればほぼ近いことが可能です!
コピペ用スクリプトを記載していますが、Cluster Workshop で触れた範囲を超えるので、詳細は割愛します。
スポーン地点付近への接触を感知するアイテムと回転するアイテムの 2つが必要で、このサンプルではお互いのアイテムが近くに配置されている必要があります。
サンプルではベータ機能を利用していますのでご注意ください。

スポーン地点付近への接触を感知するアイテム

$.onStart(() => {
  $.state.enteredUsers = {};
});

$.onCollide((playerHandle) => {
  if ($.state.enteredUsers[playerHandle.id]) {
    return;
  }

  const items = $.getItemsNear($.getPosition(), 3);
  for (let i = 0; i < items.length; i++) {
    items[i].send("entry", true);
  }

  $.state.enteredUsers = {...$.state.enteredUsers, [playerHandle.id]: true};
});

回転するアイテム

この例では入室の度に回転する/しないを切り替えるようにしています。

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

$.onReceive((id, value, sender) => {
  if (id !== "entry") {
    return;
  }
  $.state.active = !$.state.active;
});

$.onUpdate(() => {
  if (!$.state.active) {
    return;
  }
  const euler = $.getRotation().createEulerAngles();
  const newRotation = new Quaternion().setFromEulerAngles(new Vector3(euler.x + 1.0, euler.y, euler.z));
  $.setRotation(newRotation);
});

Q:$.onIntractでタップしながら文章をコンソールに表示していく場合どのようなコードになりますか?

ここでは「こんにちは」という文章を、タップする毎に「こ」「こん」「こんに」、という具合に一文字ずつ追加されるような表示のしかたをするサンプルを例示します。
こちらも Cluster Workshop では触れていない要素を扱ったスクリプトとなりますので、解説は割愛します。

const text = "こんにちは";

$.onStart(() => {
  $.state.count = 0;
});

$.onInteract(() => {
  if ($.state.count < text.length) {
    $.state.count++;
  }
  $.log(text.substr(0, $.state.count));
});

Q:公式ワールドにあった、エモートでライブハウスのスポットライトが反応する仕様はスクリプトで出来ますか?

できません・・・
まだクリエイター向けの環境が整う前の古の技術で成り立っています!

Q:テクスチャ入れ替え的なことが出来たらすてきです

どうにか機能として提供できるように頑張ります・・・!
テクスチャ以外にも、見た目に対して作用できる機能を計画しているので楽しみにお待ち下さい!

Q:スクリプトの練習中にクラフトアイテムが沢山つくられてしまいました、消せませんか?

消せます!
前提として、ワールドクラフト中にアイテムのスクリプトを保存してアップロードし直すと、新しいアイテムとして保存されます。
こうして作られた新しいアイテムが不要な場合、そのアイテムをアーカイブすることができます。

cluster.mu のウェブサイトにログインしている状態で、左側のメニューの「マイコンテンツ」を開き「クラフトアイテム」タブを選択してください。
編集ボタンが右上にあるので、押下するとアーカイブするアイテムを選択できます。

アーカイブしたいアイテムにチェックボックスを入れてアーカイブボタンを押下することで一覧から削除できます。

以上、盛りだくさんでしたが一通り回答させていただきました。
今回、回答に含まれる個別の作例は、Cluster Workshop で触れた範囲を大きく超えるものであるため、作例への質問や新たなリクエストはお受け出来かねます。
Cluster Workshop を重ねることで徐々に触れられる範囲を増やしていきますので、ぜひ次回もお越しください!

記事をシェアしてワールド制作を盛り上げよう!

Cluster Creators Guide|バーチャル空間での創作を学ぶならをもっと見る

今すぐ購読し、続きを読んで、すべてのアーカイブにアクセスしましょう。

続きを読む