引き換え可能なクーポン
Enable players to redeem coupons for rewards using Cloud Code scripts.
読み終わるまでの所要時間 6 分最終更新 23日前
ゲームの目標指向の性質は、プレイヤーが通常、タスクの完了にゲーム内報酬の獲得を期待することを意味します。例えば、プレイヤーは以下を期待する可能性があります。
- ボスを倒した後のアイテムと経験値の獲得
- サイドクエストの完了に対する通貨の受け取り
- 制作時の武器へのアップグレードの受け取り
Cloud Save での基本的な例
クーポンシステムには、クーポンコードの検証と、それを引き換えるプレイヤーの追跡という 2 つの主要な要件があります。これらの要件は、有効なクーポンをローカルに格納し、その後 Cloud Save を使用して、クーポンが引き換えられたかどうかを確認するスクリプトで満たすことができます。クーポンメタデータに失効日を追加し、Date ライブラリを使用してクーポンが失効したかどうかを確認することもできます。スクリプトの出力では、ゲーム内報酬が何であるかを説明できます。 JavaScript/* * Verify that a coupon passed as a script parameter "couponId" is valid, has not expired and has not been redeemed by using Cloud Save. * Return the reward and make the coupon invalid for that user. */const { DataApi } = require('@unity-services/cloud-save-1.4');const _ = require('lodash-4.17');const CLOUD_SAVE_COUPONS_KEY = 'REDEEMED_COUPONS';const VALID_COUPONS = [ // Gift the player 10 coins { id: 'FREECOINS10', reward: { id: 'coins', amount: 10, }, expiresAt: new Date(2021, 09, 29), }, // Gift the player a rare armor { id: 'RAREARMOR1', reward: { id: 'rare-armor', amount: 1, }, expiresAt: new Date(2021, 09, 29), },];module.exports = async ({ params, context }) => { // Initialize a Cloud Save API client using the player credentials const { projectId, playerId } = context; const cloudSaveApi = new DataApi(context); // Validate that the script parameter "couponId" is one of the valid coupons and select the corresponding coupon configuration const inputCouponId = _.toUpper(params.couponId); const coupon = _.find(VALID_COUPONS, function (c) { return c.id === inputCouponId; }); if (!coupon) { throw Error(`Invalid coupon "${params.couponId}"`); } //Check that the coupon has not expired if (Date.now() > coupon.expiresAt.getTime()) { throw Error(`The coupon "${coupon.id}" has expired`); } // Get the redeemed coupons from Cloud Save const cloudSaveGetResponse = await cloudSaveApi.getItems(projectId, playerId, [ CLOUD_SAVE_COUPONS_KEY, ]); const redeemedCouponsData = _.find(cloudSaveGetResponse?.data?.results, function (r) { return r.key === CLOUD_SAVE_COUPONS_KEY; }); const redeemedCoupons = redeemedCouponsData?.value ?? []; // Check if the coupon has been redeemed if (redeemedCoupons && _.indexOf(redeemedCoupons, coupon.id) >= 0) { throw Error(`The coupon "${coupon.id}" has already been redeemed`); } // Add the coupon id to the array of redeemed coupons and save it back into Cloud Save redeemedCoupons.push(coupon.id); await cloudSaveApi.setItem(projectId, playerId, { key: CLOUD_SAVE_COUPONS_KEY, value: redeemedCoupons, }); // Return the reward for the coupon return coupon.reward;};// Uncomment the code below to enable the inline parameter definition// - Requires Cloud Code JS dev environment setup with NodeJS (https://docs.unity3d.com/Packages/com.unity.services.cloudcode@latest/index.html?subfolder=/manual/Authoring/javascript_project.html)//// module.exports.params = {// couponId: { type: "String", required: true },// };
Economy の使用
Economy サービスを使用して、この例をさらに進めることができます。Economy サービスを使用して、ゲーム全体で インベントリアイテム と 通貨 を設定できます。Economy 設定が公開されたら、クーポンの検証後にスクリプトから直接アイテムと通貨の贈与を開始できます。 JavaScript/* * Verify that a coupon passed as a script parameter "couponId" is valid, has not expired and has not been redeemed by using Cloud Save. * Gift the appropriate reward in Economy. * Note: the Economy configuration needs to be published before it is available from Cloud Code scripts * */const { DataApi } = require('@unity-services/cloud-save-1.4');const { CurrenciesApi, InventoryApi } = require('@unity-services/economy-2.4');const _ = require('lodash-4.17');const CLOUD_SAVE_COUPONS_KEY = 'REDEEMED_COUPONS';const VALID_COUPONS = [ // Gift the player 10 coins { id: 'FREECOINS10', reward: { id: 'COINS', type: 'currency', amount: 10, }, expiresAt: new Date(2021, 09, 29), }, // Gift the player a rare armor { id: 'RAREARMOR1', reward: { id: 'RARE_ARMOR', type: 'inventoryItem', amount: 1, }, expiresAt: new Date(2021, 10, 21), },];module.exports = async ({ params, context }) => { // Initialize a Cloud Save API client using the player credentials const { projectId, playerId } = context; const cloudSaveApi = new DataApi(context); // Validate that the script parameter "couponId" is one of the valid coupons and select the corresponding coupon configuration const inputCouponId = _.toUpper(params.couponId); const coupon = _.find(VALID_COUPONS, function (c) { return c.id === inputCouponId; }); if (!coupon) { throw Error(`Invalid coupon "${params.couponId}"`); } //Check that the coupon has not expired if (Date.now() > coupon.expiresAt.getTime()) { throw Error(`The coupon "${coupon.id}" has expired`); } // Get the redeemed coupons from Cloud Save const cloudSaveGetResponse = await cloudSaveApi.getItems(projectId, playerId, [ CLOUD_SAVE_COUPONS_KEY, ]); const redeemedCouponsData = _.find(cloudSaveGetResponse?.data?.results, function (r) { return r.key === CLOUD_SAVE_COUPONS_KEY; }); const redeemedCoupons = redeemedCouponsData?.value ?? []; // Check if the coupon has been redeemed if (redeemedCoupons && _.indexOf(redeemedCoupons, coupon.id) >= 0) { throw Error(`The coupon "${coupon.id}" has already been redeemed`); } // Gift the coupon reward to the player await redeemCouponReward(coupon.reward, projectId, playerId, context); // Add the coupon id to the array of redeemed coupons and save it back into Cloud Save redeemedCoupons.push(coupon.id); await cloudSaveApi.setItem(projectId, playerId, { key: CLOUD_SAVE_COUPONS_KEY, value: redeemedCoupons, }); // Return the reward for the coupon return { id: coupon.reward.id, amount: coupon.reward.amount, };};// Gift the reward to a player based on the reward.type propertyasync function redeemCouponReward(reward, projectId, playerId, context) { // Initialize a Economy API clients using the player credentials const currenciesApi = new CurrenciesApi(context); const inventoryApi = new InventoryApi(context); // Gift the reward switch (reward.type) { case 'currency': await currenciesApi.incrementPlayerCurrencyBalance(projectId, playerId, reward.id, { amount: reward.amount, }); break; case 'inventoryItem': await inventoryApi.addInventoryItem(projectId, playerId, addInventoryRequest: { inventoryItemId: reward.id, }); break; default: throw Error('Invalid reward type'); }}// Uncomment the code below to enable the inline parameter definition// - Requires Cloud Code JS dev environment setup with NodeJS (https://docs.unity3d.com/Packages/com.unity.services.cloudcode@latest/index.html?subfolder=/manual/Authoring/javascript_project.html)//// module.exports.params = {// couponId: { type: "String", required: true },// };
Remote Config のインテグレーション
この例の 1 つの欠点は、クーポンの設定しやすさです。新しいクーポンの作成または既存のクーポンの無効化にはコードの変更が必要であり、十分な柔軟性が提供されません。Remote Config でクーポンをカスタム JSON 値として定義できます。
/* * Retrieve coupon details from Remote Config. * Verify that a coupon passed as a script parameter "couponId" is valid, has not expired and has not been redeemed by using Cloud Save. * Gift the appropriate reward in Economy. * Note: the Economy configuration needs to be published and Remote Config values need to be saved before they are available in Cloud Code scripts. * */const { DataApi } = require('@unity-services/cloud-save-1.4');const { CurrenciesApi, InventoryApi } = require('@unity-services/economy-2.4');const { SettingsApi } = require('@unity-services/remote-config-1.1');const _ = require('lodash-4.17');const CLOUD_SAVE_COUPONS_KEY = 'REDEEMED_COUPONS';const REMOTE_CONFIG_COUPONS_KEY = 'COUPONS';module.exports = async ({ params, context }) => { // Initialize the Cloud Save and Remote Config API clients using the player credentials const { projectId, environmentId, playerId } = context; const cloudSaveApi = new DataApi(context); const remoteConfig = new SettingsApi(context); // Fetch the available coupons from Remote Config const remoteConfigResponse = await remoteConfig.assignSettingsGet( projectId, environmentId, 'settings', [REMOTE_CONFIG_COUPONS_KEY] ); const availableCoupons = remoteConfigResponse?.data?.configs?.settings?.[REMOTE_CONFIG_COUPONS_KEY]; // Validate that the script parameter "couponId" is one of the valid coupons and select the corresponding coupon configuration const inputCouponId = _.toUpper(params.couponId); const coupon = _.find(availableCoupons, function (c) { return c.id === inputCouponId; }); if (!coupon) { throw Error(`Invalid coupon "${params.couponId}"`); } //Check that the coupon has not expired if (Date.now() > coupon.expiresAt) { throw Error(`The coupon "${coupon.id}" has expired`); } // Get the redeemed coupons from Cloud Save const cloudSaveGetResponse = await cloudSaveApi.getItems(projectId, playerId, [ CLOUD_SAVE_COUPONS_KEY, ]); const redeemedCouponsData = _.find(cloudSaveGetResponse?.data?.results, function (r) { return r.key === CLOUD_SAVE_COUPONS_KEY; }); const redeemedCoupons = redeemedCouponsData?.value ?? []; // Check if the coupon has been redeemed if (redeemedCoupons && _.indexOf(redeemedCoupons, coupon.id) >= 0) { throw Error(`The coupon "${coupon.id}" has already been redeemed`); } // Gift the coupon reward to the player await redeemCouponReward(coupon.reward, projectId, playerId, context); // Add the coupon id to the array of redeemed coupons and save it back into Cloud Save redeemedCoupons.push(coupon.id); await cloudSaveApi.setItem(projectId, playerId, { key: CLOUD_SAVE_COUPONS_KEY, value: redeemedCoupons, }); // Return the reward for the coupon return { id: coupon.reward.id, amount: coupon.reward.amount, };};// Gift the reward to a player based on the reward.type propertyasync function redeemCouponReward(reward, projectId, playerId, context) { // Initialize a Economy API clients using the player credentials const currenciesApi = new CurrenciesApi(context); const inventoryApi = new InventoryApi(context); // Gift the reward switch (reward.type) { case 'currency': await currenciesApi.incrementPlayerCurrencyBalance(projectId, playerId, reward.id, { amount: reward.amount, }); break; case 'inventoryItem': await inventoryApi.addInventoryItem(projectId, playerId, addInventoryRequest: { inventoryItemId: reward.id, }); break; default: throw Error('Invalid reward type'); }}// Uncomment the code below to enable the inline parameter definition// - Requires Cloud Code JS dev environment setup with NodeJS (https://docs.unity3d.com/Packages/com.unity.services.cloudcode@latest/index.html?subfolder=/manual/Authoring/javascript_project.html)//// module.exports.params = {// couponId: { type: "String", required: true },// };