【サンプル付き】ワールドで課金ができる!「ワールド内課金」機能を使ってみよう!

8/29のCluster Conference2024で発表された「ワールド内課金」機能。8/29にCluster Creator Kit v2.21.0.2がリリースされ、使用できるようになりました。

これまでclusterでは、クラフトストア・アクセサリーストアでアイテムを買ったり、イベントでクラスターコインを消費してギフトを贈ったりすることができました。

今回のアップデートで、ワールドでもワールド課金機能を使ったギミックを置くと、ワールド内で任意の商品を販売できるようになりました。例えば「課金をしないと開かないドア」だったり、これまでになかった仕組みを構築することができます。

そこで今回の記事では、シンプルな例として「サポート箱」のサンプルを配布します。
サンプルアイテムは、購入をすると音が鳴り、スペース内にいるプレイヤーの商品所持数の合計が表示されるだけで特典があるわけではないですが、ワールドに設置することで訪問者がワールド制作者に対してサポートすることができるようになります。ぜひ試しに置いてみてください。

※ワールド内課金では、販売する商品に対してガイドラインを設けております。機能を使用する際は「ワールド内課金商品ガイドライン」をご覧ください。

この記事では、すぐに使えるサンプルアイテムを配布しています。
まずは使ってみたいという方は下記からダウンロードしてください。本記事では、サンプルアイテムについて解説していきます。

※ワールド内課金機能はイベントでは使用できません(2024年8月時点)

ワールド内課金商品を設定する

ワールド内課金機能は、ワールドに設定された「ワールド内課金商品」のIDを活用することで使用できます。
ワールド内課金商品の設定の手順について以下で紹介します。

  • ワールドを新規作成する場合は一度ワールドをアップロードしてください。
    • ワールドが非公開の状態でもワールド内課金商品の設定をすることができます。
  • cluster公式Webサイトの「マイコンテンツ > ワールド」を開いてください。
  • ワールド内課金商品を設定するワールドの右上にある三点のメニューから「情報を編集」を選択するか(ワールドが公開されている場合は該当ワールドの右下に「情報を編集」ボタンが表示されます)、ワールド情報ページを開き「ワールドを編集」を選択してください。
  • ワールドの編集画面の最下部にワールド内課金商品を設定するメニューがあるので、そちらを選択してください。
    • ワールド内課金商品では「商品名」「商品説明」「価格」を設定できます。
    • 入力が完了したら「登録」を選択して、ワールド編集ページの情報も保存して終了してください。

ワールド内課金商品のIDを確認し、スクリプトに反映する

設定が完了したら、IDを確認し、スクリプトに反映してください。

  • 設定が完了したら、当該ワールドに「商品あり」というラベルがつきます。
    • 再度、ワールド編集ページを開いてください。
  • 設定した商品を選択して「ワールド内課金商品詳細」を開いてください。
  • ​商品IDが表示されるので、そちらをスクリプトにコピー&ペーストしてください!
  • サンプルアイテムの「SupportBox > Scripts」の中にあるスクリプトを編集してください
    • 一番上にある「cluster公式Webサイトのワールド編集ページで設定した商品のID」という記載をコピーした商品IDに置き換えてください(「””」は削除せずに、「”商品ID”」というかたちになるようにしてください)

ワールド内課金機能をテストする

Cluster Creator Kit 2.14.0から使用できるようになった「テスト用アップロード」「テスト用スペース」を活用することで、CCの消費無しでワールド内課金商品のテストを行うことができます。ワールド内課金について、テスト用スペースでは以下を行うことができます。詳細はこちらのページをご覧ください。

  • ワールド内課金商品をCC消費なしで購入が行えます。テスト用のスペースを活用することで、CCを消費せずにワールド内課金機能の検証を行うことができます。
  • デベロッパーメニューから所持状況の初期化が行えます。

今回のサンプルアイテムは「オブジェクトをインタラクトすると、ワールド内課金商品を購入できる。スペース内にいる全ユーザーの商品所持数を表示する。」という実装になっています。

const productId = "cluster公式Webサイトのワールド編集ページで設定した商品のID";

$.onStart(() => {
  // 購入通知を購読する
  $.subscribePurchase(productId);
});

$.onUpdate(deltaTime => {
  // 10秒に1回、定期的にスペース内にいるすべてのプレイヤーの購入状況を確認する
  let timer = $.state.timer ?? 0;
  timer -= deltaTime;
  if (timer <= 0) {
    timer += 10;
    const allPlayers = $.getPlayersNear(new Vector3(), Infinity);
    $.getOwnProducts(productId, allPlayers, "");
  }
  $.state.timer = timer;
});

$.onInteract(player => {
    player.requestPurchase(productId, "");
});

$.onPurchaseUpdated((player, productId) => {
  // 商品が購入された場合、スペース内にいるすべてのプレイヤーの購入状況を確認する
  const allPlayers = $.getPlayersNear(new Vector3(), Infinity);
  $.getOwnProducts(productId, allPlayers, "");
});

$.onGetOwnProducts((ownProducts, meta, errorReason) => {
  //  商品の所持状況を取得したとき
  let total = 0;
  for (let ownProduct of ownProducts) {
    // スペース内での商品所持数の合計を計算
    total += ownProduct.plusAmount - ownProduct.minusAmount;
  }
  // スペース内での商品所持数の合計数を表示
  $.subNode("Text").setText("みんなのサポート数: " + total);
});

$.onRequestPurchaseStatus((meta, status, errorReason, player) => {
  if (status == PurchaseRequestStatus.Purchased) {
      // Play Audio Source Gimmickがついたアイテムにシグナルを送る
      $.sendSignalCompat("this", "SupportSE");
  }
});

インタラクトして、ワールド内課金のギミックを動かす

インタラクトして、ワールド内課金のギミックを動かすスクリプトは下記になります。

const productId = "cluster公式Webサイトのワールド編集ページで設定した商品のID";

$.onInteract(player => {
  player.requestPurchase(productId, "");
});

今回は onInteract を使用することで、対象のオブジェクトをインタラクトした時にワールド内課金商品の購入ウインドウが表示されるようにしています。

onUse や onCollide などを使えば、特定のアイテムを持って使った時や何かに衝突した時など、別のタイミングで表示するようにもできます。 onRide で課金した時にだけ動かせるようになる乗り物をつくるなんてのもありかもしれません。

ワールド内課金商品の購入状況を確認する

下記では、ワールド内課金商品の購入状況を定期的に確認するようにしています。

$.onStart(() => {
  // スペース内にいるプレイヤーの商品所持状況をstateに保存する
  $.state.amounts = {};
  // 購入通知を購読する
  $.subscribePurchase(productId);
});

$.onUpdate(deltaTime => {
  // 10秒に1回、定期的にスペース内にいるすべてのプレイヤーの購入状況を確認する
  let timer = $.state.timer ?? 0;
  timer -= deltaTime;
  if (timer <= 0) {
    timer += 10;
    const allPlayers = $.getPlayersNear(new Vector3(), Infinity);
    $.getOwnProducts(productId, allPlayers, "");
  }
  $.state.timer = timer;
});

※購入の特典として何かのギミックを動かすときの注意点

onRequestPurchaseStatus は購入成功時に一度しか呼ばれないため、その前にアプリが終了されるなど何らかの問題でその処理が実行されないことがあります。

購入に応じて何かの値を変更する(例えばコインの所持量を増やす等。後述しますが、サンプルアイテムでは商品所持数を TextView 上に反映させています)場合は、このコールバックの中では処理せず、 getOwnProducts と onGetOwnProducts を組み合わせて使用することを推奨します。

$.onPurchaseUpdated((player, productId) => {
  // 商品が購入された場合、スペース内にいるすべてのプレイヤーの購入状況を確認する
  const allPlayers = $.getPlayersNear(new Vector3(), Infinity);
  $.getOwnProducts(productId, allPlayers, "");
});

$.onGetOwnProducts((ownProducts, meta, errorReason) => {
  //  スペース内にいるプレイヤーごとの商品の所持状況を取得
  let total = 0;
  for (let ownProduct of ownProducts) {
    // スペース内での商品所持数の合計を計算
    total += ownProduct.plusAmount - ownProduct.minusAmount;
  }
  // スペース内での商品所持数の合計数を表示
  $.subNode("Text").setText("みんなのサポート数: " + total);
});

購入が完了すると「購入が完了しました」というダイアログが表示されますが、ワールド側でもギミックを起動させることも可能です。
サンプルアイテムのスクリプトでは、購入が完了した時にギミックを動作させるシンプルなスクリプトを設定しています。

$.onRequestPurchaseStatus((meta, status, errorReason, player) => {
  if (status == PurchaseRequestStatus.Purchased) {
      // Play Audio Source Gimmickがついたアイテムにシグナルを送る
      $.sendSignalCompat("this", "SupportSE");
  }
});

このスクリプトでは購入が完了した状態になった時、 onRequestPurchaseStatus 内で sendSignalCompat を使ってSignalを送るようにしています。

例えば、 Play Audio Source Gimmick をつけて下記のように設定しておくと、購入した際に音が鳴るようになります。音を鳴らすのであれば、スクリプトから制御できる Item Audio Set List を使った方法もあるので、興味のある方はこちらの記事をご覧ください!

サンプルアイテムでは、スペース内の商品の合計所持数を表示するようにしています。スペース内にいるユーザーの商品の所持数によって値が変化します。

TextView がついたGameObjectを用意し、スクリプトで取得した値を表示させています。

スクリプト側では onPurchaseUpdated と onGetOwnProducts でスペース内にいるすべてのプレイヤーの商品状況を確認・計算し、スペース内の商品の合計所持数を表示する処理を書いています。

先述したように、購入に応じて何かの値を変更する場合は、 onRequestPurchaseStatus のコールバックの中では処理せず、 getOwnProducts と onGetOwnProducts を組み合わせて使用してください。コールバックのみで処理した場合、購入したにもかかわらず実行されない、あるいは誤って第三者に購入した結果が反映されてしまうということが発生する可能性があります(コールバックは必ずしも購入者により実行される保証がないため、例えばsendSignalCompatでownerにsignal等を送っている場合は購入者ではない第三者にsignalが送られる場合があります)

$.onPurchaseUpdated((player, productId) => {
  // 商品が購入された場合、スペース内にいるすべてのプレイヤーの購入状況を確認する
  const allPlayers = $.getPlayersNear(new Vector3(), Infinity);
  $.getOwnProducts(productId, allPlayers, "");
});

$.onGetOwnProducts((ownProducts, meta, errorReason) => {
  //  スペース内にいるプレイヤーごとの商品の所持状況を取得
  let total = 0;
  for (let ownProduct of ownProducts) {
    // スペース内での商品所持数の合計を計算
    total += ownProduct.plusAmount - ownProduct.minusAmount;
  }
  // スペース内での商品所持数の合計数を表示
  $.subNode("Text").setText("みんなのサポート数: " + total);
});

ワールド内課金機能によって、ワールド制作者も自身のワールドで任意の商品を販売できるようになり、本機能を使った仕組みも新たに制作できるようになりました。
ぜひいろいろ試してみてください!

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

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

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

続きを読む