あなたの世界を作りなさい
経済
製品 API 使用例
18min
以下は、製品APIを使用して世界通貨と世界製品を管理する例です。
この例では、製品APIの基本的な理解を得て、サーバーとクライアント間でメッセージを送受信する実装を行うことができるため、世界通貨と製品の付与および控除を簡単にテストできます。
この例には、世界通貨と世界製品のクライアントスクリプトの説明が含まれており、その後、すべてのサーバーサイド処理を処理する統合サーバースクリプトが続き、以下の機能が含まれています:
- 世界通貨の付与
- 世界通貨の控除
- 通貨控除なしでの世界製品の付与
- 世界製品の控除
以下は、世界通貨の付与と控除を管理するための完全なクライアントコードと、その実装方法です。
CurrencyManagerSample
1import { Button, Text } from 'UnityEngine.UI';
2import { ZepetoScriptBehaviour } from 'ZEPETO.Script'
3import { LocalPlayer, ZepetoPlayers } from 'ZEPETO.Character.Controller';
4import { Room, RoomData } from 'ZEPETO.Multiplay';
5import { ZepetoWorldMultiplay } from 'ZEPETO.World';
6
7export default class CurrencyManagerSample extends ZepetoScriptBehaviour {
8
9 public currencyId: string;
10 public increaseQuantity: number;
11 public decreaseQuantity: number;
12 public increaseCurrencyBtn: Button;
13 public decreaseCurrencyBtn: Button;
14 public balanceText: Text;
15 public multiplay: ZepetoWorldMultiplay;
16
17 private _localPlayer: LocalPlayer;
18
19 Start() {
20
21 ZepetoPlayers.instance.OnAddedLocalPlayer.AddListener(() => {
22 this._localPlayer = ZepetoPlayers.instance.LocalPlayer;
23 this.loadCurrencyBalance(this.currencyId);
24 });
25
26 // 通貨UIの更新のためのルーム参加イベントを処理します
27 this.multiplay.RoomJoined += (room: Room) => {
28 room.AddMessageHandler("SyncCurrencyInfo", (message: CurrencyMessage) => {
29 const currentCurrencyId = message.currencyId;
30 const currentQuantity = message.quantity;
31 this.balanceUIUpdate(currentCurrencyId, currentQuantity);
32 });
33 };
34
35 // 通貨を増やすためのボタンイベントリスナー : クレジットリクエスト
36 this.increaseCurrencyBtn.onClick.AddListener(() => {
37 const data = new RoomData();
38 data.Add("currencyId", this.currencyId);
39 data.Add("quantity", this.increaseQuantity);
40 this.multiplay.Room?.Send("CreditCurrency", data.GetObject());
41 })
42
43 // 通貨を減らすためのボタンイベントリスナー : デビットリクエスト
44 this.decreaseCurrencyBtn.onClick.AddListener(() => {
45 const data = new RoomData();
46 data.Add("currencyId", this.currencyId);
47 data.Add("quantity", this.decreaseQuantity);
48 this.multiplay.Room?.Send("DebitCurrency", data.GetObject());
49 })
50
51 }
52
53 // 指定された通貨の現在の量をリクエストします
54 private loadCurrencyBalance(currentCurrencyId: string) {
55 this.multiplay.Room?.Send("LoadBalance", currentCurrencyId);
56 }
57
58 // 通貨の現在の量を反映するようにUIを更新します
59 private balanceUIUpdate(currentCurrencyId: string, currentQuantity: number) {
60 this.balanceText.text = currentQuantity.toString();
61 }
62
63}
64
65// 通貨同期メッセージのインターフェース
66interface CurrencyMessage {
67 currencyId: string,
68 quantity: number,
69}
- キャラクターがロードされると、サーバーにルームメッセージが送信され、既存の通貨の残高がロードされます。その後、サーバーから受信した残高情報に基づいてUIが更新されます。
TypeScript
1this.multiplay.Room?.Send("LoadBalance", currentCurrencyId);
- メッセージ交換を促進するために、CurrencyMessageインターフェースが定義されています。
TypeScript
1interface CurrencyMessage {
2 currencyId: string,
3 quantity: number,
4}
- ユーザーが通貨を増減させると、彼らはサーバーにルームメッセージとしてリクエストを送信します。 それには、どの通貨を増減させるか、そしてどのくらいの量を増減させるかのデータが含まれています。
TypeScript
1this.multiplay.Room?.Send("CreditCurrency", data.GetObject());
2this.multiplay.Room?.Send("DebitCurrency", data.GetObject());
- サーバーは通貨の増減を処理し、最終的な残高情報をクライアントに送信します。クライアントはこの情報を受け取り、UIを更新します。
TypeScript
1this.multiplay.RoomJoined += (room: Room) => {
2 room.AddMessageHandler("SyncCurrencyInfo", (message: CurrencyMessage) => {
3 const currentCurrencyId = message.currencyId;
4 const currentQuantity = message.quantity;
5 this.updateBalanceUI(currentCurrencyId, currentQuantity);
6 });
7};
以下は、世界の製品の付与と控除を管理するための完全なクライアントコードと、その実装方法です。
ProductManagerSample
1import { Button, Text } from 'UnityEngine.UI';
2import { ZepetoScriptBehaviour } from 'ZEPETO.Script'
3import { LocalPlayer, ZepetoPlayers } from 'ZEPETO.Character.Controller';
4import { Room, RoomData } from 'ZEPETO.Multiplay';
5import { ZepetoWorldMultiplay } from 'ZEPETO.World';
6import { InventoryRecord, InventoryService } from 'ZEPETO.Inventory';
7import { WaitUntil } from 'UnityEngine';
8
9export default class ProductManagerSample extends ZepetoScriptBehaviour {
10
11 public productId: string;
12 public productAddQuantity: number;
13 public acquireItemBtn: Button;
14 public useItemBtn: Button;
15 public itemCountText: Text;
16 public multiplay: ZepetoWorldMultiplay;
17
18 private _localPlayer: LocalPlayer;
19
20 Start() {
21
22 ZepetoPlayers.instance.OnAddedLocalPlayer.AddListener(() => {
23 this._localPlayer = ZepetoPlayers.instance.LocalPlayer;
24 this.StartCoroutine(this.refreshProductUI());
25 });
26
27 // 製品UIの更新のためのルーム参加イベントを処理します
28 this.multiplay.RoomJoined += (room: Room) => {
29 room.AddMessageHandler("SyncProductInfo", (message: ProductMessage) => {
30 this.StartCoroutine(this.refreshProductUI());
31 });
32 };
33
34 // アイテム取得ボタンのイベントリスナー : 製品を追加
35 this.acquireItemBtn.onClick.AddListener(() => {
36 const data = new RoomData();
37 data.Add("productId", this.productId);
38 data.Add("quantity", this.productAddQuantity);
39 this.multiplay.Room?.Send("AddProduct", data.GetObject());
40 })
41
42 // アイテム使用ボタンのイベントリスナー : 製品を使用
43 this.useItemBtn.onClick.AddListener(() => {
44 const data = new RoomData();
45 data.Add("productId", this.productId);
46 data.Add("quantity", 1);
47 this.multiplay.Room?.Send("UseProduct", data.GetObject());
48 })
49
50 }
51
52 private * refreshProductUI() {
53 const request = InventoryService.GetAsync(this.productId);
54 yield new WaitUntil(() => request.keepWaiting == false);
55 // リクエストが成功した場合、製品UIを更新します
56 if (request.responseData.isSuccess) {
57 this.updateItemCountText(request.responseData.product);
58 }
59 else {
60 console.log("製品アイテムの読み込みに失敗しました。");
61 }
62 }
63
64 private updateItemCountText(item: InventoryRecord) {
65 if (item != null) {
66 this.itemCountText.text = item.quantity.toString();
67 }
68 else {
69 this.itemCountText.text = "0";
70 }
71 }
72
73}
74
75// 製品メッセージのインターフェース。
76interface ProductMessage {
77 productId: string,
78 productAction: ProductAction,
79}
80
81// 製品アクションタイプを定義するための列挙型。
82export enum ProductAction {
83 Use,
84 Add,
85}
TypeScript
1private * refreshProductUI() {
2 const request = InventoryService.GetAsync(this.productId);
3 yield new WaitUntil(() => request.keepWaiting == false);
4 if (request.responseData.isSuccess) {
5 this.updateItemCountText(request.responseData.product);
6 }
7 else {
8 console.log("製品アイテムのロードに失敗しました。");
9 }
10 }
- メッセージを送受信するには、ProductMessageインターフェースを定義します。ProductAction列挙型を定義して、例の実装を行います。
TypeScript
1interface ProductMessage {
2 productId: string,
3 productAction: ProductAction,
4}
5
6export enum ProductAction {
7 使用,
8 追加,
9}
- 製品を増減させるとき、そのデータをサーバーにルームメッセージとして送信します。 どの製品をどれだけ増減させるかに関するデータが含まれています。
TypeScript
1this.multiplay.Room?.Send("AddProduct", data.GetObject());
2this.multiplay.Room?.Send("UseProduct", data.GetObject());
- 製品の変更を処理した後、サーバーは最終的な在庫情報をクライアントに送信し、UIをそれに応じて更新します。
TypeScript
1this.multiplay.RoomJoined += (room: Room) => {
2 room.AddMessageHandler("SyncProductInfo", (message: ProductMessage) => {
3 this.StartCoroutine(this.refreshProductUI());
4 });
5};
- 一般的に、ワールド製品は購入するために通貨が必要です。
以下は、世界の通貨と製品を管理する完全なサーバーコードと、その実装方法です。
TypeScript
1import { Sandbox, SandboxOptions, SandboxPlayer } from "ZEPETO.Multiplay";
2import { loadCurrency } from "ZEPETO.Multiplay.Currency";
3import { loadInventory } from "ZEPETO.Multiplay.Inventory";
4
5export default class extends Sandbox {
6
7 async onCreate(options: SandboxOptions) {
8
9 // 通貨
10 //通貨の読み込みをリクエスト
11 this.onMessage("LoadBalance", (client, message: string) => {
12 this.loadBalance(client, message);
13 });
14 //クレジットの追加をリクエスト
15 this.onMessage("CreditCurrency", (client, message: CurrencyMessage ) => {
16 const currencyId = message.currencyId;
17 const quantity = message.quantity;
18 this.addCredit(client, currencyId, quantity);
19 });
20 //クレジットの引き落としをリクエスト
21 this.onMessage("DebitCurrency", (client, message: CurrencyMessage ) => {
22 const currencyId = message.currencyId;
23 const quantity = message.quantity;
24 this.onDebit(client, currencyId, quantity);
25 });
26
27 // 商品
28 //アイテムの追加をリクエスト
29 this.onMessage("AddProduct", (client, message: any) => {
30 const productId = message.productId;
31 const quantity = message.quantity;
32 this.addProduct(client, productId, quantity);
33 });
34 //アイテムの使用をリクエスト
35 this.onMessage("UseProduct", (client, message: any) => {
36 const productId = message.productId;
37 const quantity = message.quantity;
38 this.useProduct(client, productId, quantity);
39 });
40
41 }
42
43 async addCredit(client: SandboxPlayer, currencyId: string, quantity: number) {
44 try {
45 const currency = await loadCurrency(client.userId);
46 await currency.credit(currencyId, quantity);
47 this.loadBalance(client, currencyId);
48 } catch (e) {
49 console.error(`AddCredit Error: ${e}`);
50 }
51 }
52
53 async onDebit(client: SandboxPlayer, currencyId: string, quantity: number) {
54 try {
55 const currency = await loadCurrency(client.userId);
56 if (await currency.debit(currencyId, quantity) === true) {
57 this.loadBalance(client, currencyId);
58 } else {
59 console.error("DebitCredit Error: 通貨が不足しています");
60 }
61 } catch (e) {
62 console.error(`DebitCredit Error: ${e}`);
63 }
64 }
65
66 async loadBalance(client: SandboxPlayer, currencyId: string) {
67 try {
68 const currency = await loadCurrency(client.userId);
69 let balancesObject = await currency.getBalances();
70 let balancesMap: Map<string, number> = new Map(Object.entries(balancesObject));
71 const specificCurrencyID = currencyId;
72 const specificBalance = balancesMap.get(specificCurrencyID) ?? 0;
73 const currencySync: CurrencyMessage = {
74 currencyId: specificCurrencyID,
75 quantity: specificBalance
76 }
77 client.send("SyncCurrencyInfo", currencySync);
78 } catch (e) {
79 console.error(`loadBalance Error: ${e}`);
80 }
81 }
82
83 async addProduct(client: SandboxPlayer, productId: string, quantity: number) {
84 try {
85 const inventory = await loadInventory(client.userId);
86 await inventory.add(productId, quantity);
87 const productMessage: ProductMessage = {
88 productId: productId,
89 productAction: ProductAction.Add
90 }
91 client.send("SyncProductInfo", productMessage);
92 } catch (e) {
93 console.error(`addProduct Error: ${e}`);
94 }
95 }
96
97 async useProduct(client: SandboxPlayer, productId: string, quantity: number) {
98 try {
99 const inventory = await loadInventory(client.userId);
100 await inventory.use(productId, quantity);
101 const productMessage: ProductMessage = {
102 productId: productId,
103 productAction: ProductAction.Use
104 }
105 client.send("SyncProductInfo", productMessage);
106 } catch (e) {
107 console.error(`useProduct Error: ${e}`);
108 }
109 }
110
111 onJoin(client: SandboxPlayer) {
112 // 参加時のロジック
113 }
114
115 onTick(deltaTime: number): void {
116 // タイックロジック
117 }
118
119 onLeave(client: SandboxPlayer, consented?: boolean) {
120 // 退室時のロジック
121 }
122
123}
124
125// 通貨メッセージのインターフェース
126interface CurrencyMessage {
127 currencyId: string,
128 quantity: number,
129}
130
131// 商品メッセージのインターフェース。
132interface ProductMessage {
133 productId: string,
134 productAction: ProductAction,
135}
136
137// 商品アクションタイプを定義するための列挙型。
138export enum ProductAction {
139 Use,
140 Add,
141}
- を使用して、currency.credit() と currency.debit() を使用して、希望する通貨の残高を増減させます。その後、currency.getBalances() を呼び出して、各通貨の現在の残高を取得します。
TypeScript
1currency.credit(currencyId, quantity);
2currency.debit(currencyId, quantity);
3currency.getBalances();
- を使用して、inventory.add() と inventory.use() を使用して、希望する通貨の残高を増減させます。
TypeScript
1inventory.add(productId, quantity);
2inventory.use(productId, quantity);
- クライアントからの通貨や商品の増減に関するリクエストを処理します。
- 使用するloadBalance()を使用して、ZEPETO Studioに登録された通貨情報を取得します。
- 複数の通貨がある場合、特定の通貨IDの値に基づいて条件を設定し、その通貨の残高のみを取得できます。
- 最終的な残高値をクライアントにルームメッセージとして渡します。