🎟️

ClusterScript - 商品の購入状況をPlayerStorageに保存する

2024/12/12に公開

クラスター Advent Calendar 2024の8日目の枠が空いていたので、その日の記事ということにします。

概要

clusterのワールドに以下のようなものをつくります。

  • 商品を購入すると、購入したプレイヤーにチケットが1枚付与される
  • プレイヤーごとのチケット所持数は、プレイヤーがスペースを退室しても保存される
  • 手に入れたチケットはスペース内でプレイヤーが任意のタイミングで消費することができる

なお、この記事は既にある程度ClusterScriptに触れている方が読むことを想定して書いています。
Unitypackageの配布については追って検討します。

プレイヤーがスペースを退室してもプレイ状況を保存する方法はいくつかありますが、この記事ではPlayerStorageを使用します。PlayerStorageに保存する内容は、「持っているチケットの枚数」と、「チケットを付与した回数」の2つの情報です。
「商品を購入した回数」はPlayerStorageを使用しなくても OwnProduct.plusAmount から取得することができます。それを取得した上で、PlayerScriptで「商品を購入した回数」と「チケットを付与した回数」を比較して、「チケットを付与した回数」のほうが少なければ、「持っているチケットの枚数」を1枚増やします。

PlayerScriptをプレイヤーに適用する

PlayerStorageにアクセスするために、下記のPlayerScriptをプレイヤーにセットします。

// PlayerStorageを読み込む
let storageData = _.getPlayerStorageData();

// paidTicketAmount : プレイヤーが持っている有料チケットの枚数
let paidTicketAmount = storageData.paidTicketAmount ?? 0;

// addPaidTicketCount : プレイヤーに有料チケットを付与した回数
let addPaidTicketCount = storageData.addPaidTicketCount ?? 0;

// "OwnTicket"という名前がついたGameObjectのTextコンポーネントを取得する
const textObject = _.playerLocalObject("PaidTicketAmountDisplay");
const textComponent = textObject.getUnityComponent("Text");

// プレイヤーが持っている有料チケットの枚数を表示する
textComponent.unityProp.text = String(paidTicketAmount);

_.onReceive((messageType, arg, sender) => {
  // messageType "CheckTicket" を受け取った時
  if (messageType === "CheckTicket") {
    // 受け取ったargから、purchaseCount(商品を購入した回数)を取り出す
    let purchaseCount = arg.purchaseCount;

    // 商品を購入した回数が、チケットを追加した回数より多ければ、チケットを追加する
    if(purchaseCount > addPaidTicketCount){

      // 差分を計算する
      let diff = purchaseCount - addPaidTicketCount;
      
      //  プレイヤーが持っている有料チケットの枚数を1増やす
      paidTicketAmount += diff;
      _.log("paidTicketAmount: " + paidTicketAmount);

      // プレイヤーに有料チケットを付与した回数を1増やす
      addPaidTicketCount += diff;
      _.log("addPaidTicketCount: " + addPaidTicketCount);

      // PlayerStorageに保存する
      _.setPlayerStorageData({ paidTicketAmount, addPaidTicketCount });
    }
    // プレイヤーが持っている有料チケットの枚数を表示する
    textComponent.unityProp.text = String(paidTicketAmount);
  }

});

PlayerScriptをプレイヤーにセットする方法はこちらの記事をご覧ください。
https://zenn.dev/toyakun/articles/5bd732eb625f83

商品を購入してもらうためのアイテム

商品を購入するための、ボタンのようなアイテムをつくります。
ColliderのついたCubeなどを設置して、Scriptable Itemコンポーネントを追加し、以下のコードを割り当てます。

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

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

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

$.onPurchaseUpdated((player, productId) => {
  // 商品が購入された場合、購入したプレイヤーの購入状況を確認する
  $.getOwnProducts(productId, player, "");
});

$.onGetOwnProducts((ownProducts, meta, errorReason) => {
  //  商品の所持状況を取得したとき
  for (let ownProduct of ownProducts) {
    // 商品を購入した回数から返品した回数を引いて、商品の所持数を取得する
    let purchaseCount = ownProduct.plusAmount - ownProduct.minusAmount;

    // send用のobjectに商品idと所持数を格納する
    let obj = {
      productId: productId,
      purchaseCount: purchaseCount,
    }

    // 購入したプレイヤーにsendして、PlayerScriptで購入状況を反映する
    ownProduct.player.send("CheckTicket", obj);
  }
});

Player Local UIの設定

Player Local UIの中にGameObjectを配置し、Textコンポーネントを追加しましょう。
画像ではPaidTicketAmountDisplayObjectという名前にしています。

Scene内の適当なGameObjectに、Player Local Objecr Reference List コンポーネントを追加して、Idに PaidTicketAmountDisplayと設定しましょう。TargetObjectには、先ほど作成したPaidTicketAmountDisplayObjectをドラッグ&ドロップしましょう。

メモ

「チケットを付与した回数」を保存することで、以下のような状況に対応しています。

  • ワールド内でチケットを消費することができる
    • PlayerScriptに記述を追加する必要がある(そのうち記事にしたい)
  • 通信切断などによってチケット付与に失敗した場合に、追って付与し直すことができる
    • 商品を購入してもらうためのアイテムとは別に、購入状況を確認するためのアイテムをつくるとよさそう(そのうち記事にしたい)
  • スペースでのクラフトを許可している場合は、sendおよびonReceiveのmessageTypeであるCheckTicketを任意の文字列に変更しておくことをオススメします
    • スペースにPlayerHandle.send("CheckTicket", {purchaseCount: 100}); を実行するクラフトアイテムを設置すれば、特定のplayerが100回購入したことにできてしまう(はず)

Discussion