1. ゲームループを作成する
まずは基本的なゲームループを構築しましょう。最初にマネタイズ戦略やweb3要素の活用方法を考えておくことが重要です! ゲームループには、Unity Asset StoreでInfinite Runner Engineを購入しました。このアセット内のデモシーンJellyForestを少し調整することで、iOSとAndroidで動作するビルドを作成できました。
2. ソーシャルサインインとSequence Embedded Walletソリューションの統合
設定
- Package Managerを使ってSequenceのUnity SDKをインストール
- Sequence Builderコンソールにサインイン
- Builderコンソールでゲーム用プロジェクトを作成
- BuilderコンソールでEmbedded Walletをセットアップ
SequenceConfigスクリプタブルオブジェクト(インストール手順でSamplesメニューからインポート)に、Builderで追加したGoogleとAppleのクライアントID、およびWaaSConfigKeyにConfiguration Keyを入力してください。- AndroidおよびiOSのクライアントIDを各プラットフォームに正しく設定してください。
- Builderコンソールから取得したBuilder API Keyを
Settings > API Access Keysのprodキーとして追加してください。
ソーシャルサインイン
- プレイヤーがログインするための基本的なシーンを作成します。
- 今回は、新しいシーンを作成し、背景画像を追加しました。
Canvasを作成し、Canvas Scalerコンポーネントを追加して「Scale with Screen Size」UIスケールモードを使用します。これにより、LoginPanel(およびCanvas配下の他のUI要素)がビルドターゲット切り替え時にも自動でスケーリングされます。LoginPanelプレハブをCanvas配下のシーン階層にドラッグします。これはProjectウィンドウのPackages > Sequence Embedded Wallet SDK > SequenceFrontend > Prefabsにあります。- UIマネージャーを作成し、
LoginPanelのOpenを呼び出します。実装例はこちら:
- 階層内の
LoginPanelプレハブとの参照を切り離し、シーンビューで自由に編集できるようにします。- 階層内の
LoginPanelゲームオブジェクトを選択します。 - 階層内の
LoginPanelゲームオブジェクトを右クリックします。 Prefab > Unpack Completely
- 階層内の
- LoginPanelをゲームのテーマに合わせてカスタマイズしてください。
LoginPageやOpenIdAuthenticatorの実装をご覧いただけます。
認証はOpen ID Connect Implicit Flowで動作します。
Sequence APIでセッションを登録する
ソーシャルサインインが完了すると、自動的にSequence WaaS(Wallet as a Service)APIでセッション登録リクエストが送信されます。仕組みは以下の通りです。 ソーシャルサインインが完了すると、OpenIdAuthenticator.SignedInイベントが発火します。これによりSequenceLogin.ConnectToWaaSで認可プロセスが開始されます。
ユーザーのウォレットを取得する
ウォレットを取得するには、SequenceWallet.OnWalletCreatedイベントを購読する必要があります。
SequenceConnectorをインポートすることを強くおすすめします。デフォルトで多くの便利な初期コードが含まれており、SDKとの連携に便利なインターフェースとして機能します。JellyForestとの統合でも活用しました。
JellyForestでは、SequenceWallet.OnWalletCreatedイベントが発火した際に次のシーンをロードするLevelLoader MonoBehaviourも作成しました。
3. コレクティブルコントラクトのデプロイ
プレイヤーがサインインしてウォレットを取得できるようになったので、次はコレクティブルを追加しましょう。 ERC1155コントラクトの利用を強くおすすめします。これは柔軟性の高いトークン規格で、ゲームに最適です。監査済みのERC1155実装をBuilder Consoleから簡単にデプロイできます。4. リモートミンターのデプロイ
デフォルトでは、Builder ConsoleからデプロイしたERC1155コントラクトは、トークンをミントするために適切な権限を持つコーラーが必要です。一見面倒に思えるかもしれませんが、これは大切な仕組みです。これがないと、誰でもコントラクトのmintメソッドを呼び出して無限にゲーム内アイテムを手に入れられてしまいます! Sequenceウォレット(または他のウォレット)を持つサーバーをデプロイし、builderでミント権限を付与しましょう。Jelly Forestでの実装例
Jelly Forestでは、ゲームプレイ中に集めたコインはすべてERC1155トークンとしてミントされます。実際の手順は以下の通りです。- Cloudflareにサインアップします。ここでミントサービスのコードをホストしますが、他の方法を利用しても問題ありません。
- ターミナルやコマンドラインを開きます。
git clone https://github.com/0xsequence-demos/cloudflare-worker-sequence-relayer.gitを実行し、その後cd cloudflare-worker-sequence-relayergit checkout permissionedMinterpnpm installで依存関係をインストールします。- wranglerをインストールします。
wrangler.tomlを開きます。nameの文字列を変更してサーバー名を設定します。- 新しいEOAウォレットを作成し、秘密鍵をエクスポートします。どのEOAウォレットでも構いません。Metamaskを使ってウォレットを作成し、秘密鍵をエクスポートすることもできます。秘密鍵の取り扱いには十分ご注意ください。秘密鍵をパソコンに平文で保存したり、バージョン管理にコミットしないようにしてください。この秘密鍵を
PKEYに設定してください。 CONTRACT_ADDRESSを設定します。PROJECT_ACCESS_KEYを設定します。これは、先ほどSequenceConfigスクリプタブルオブジェクトを設定した際にBuilder Consoleから取得した本番APIキーです。CHAIN_HANDLEを設定します。不明な場合は、Builder ConsoleのNode Gatewayページで各ネットワークのCHAIN_HANDLEを確認できます。
pnpm devでサーバーをローカルにデプロイします。コマンドラインにデプロイ先のlocalhostが表示されます。- 別のコマンドラインウィンドウを開きます。
curl http://localhost:8787(与えられたlocalhostに置き換えてください)でサーバーにリクエストを送信します。- ローカルサーバーが稼働しているコマンドラインで、ミンターのウォレットアドレスがログに表示されているはずです。
- このアドレスにBuilder Consoleでミント権限を付与します。
Contractsから該当コントラクトを探し、クリックして開きます。Write Contractをクリックします。grantRoleを展開します。roleには0x9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a6を入力します。これはMINTER_ROLEのKeccak-256ハッシュです。accountにはミンターのウォレットアドレスを貼り付けます。
wrangler deployでコードをCloudflare Workerにデプロイし、ミンティング用URLが発行されます。
proof を含む、C#で定義されたボディでサーバーにPOSTリクエストを送信します。Unity SDKでは、MintingRequestProverがこの処理を実装しています。
5. ゲーム内トークンをプレイヤーのインベントリにミントする
権限付きミンターサーバーのセットアップができたので、次はクライアント側(Made With Unityアプリ)を連携させ、ゲームプレイを通じてプレイヤーにトークンをミントできるようにします。Unity SDKのPermissionedMinter.MintToken メソッドを呼び出すことで、権限付きミンターにリクエストを送信できます。
Jelly Forestでは、プレイヤーがステージを進むごとに多くのコインを集めますが、これらはすべてERC1155トークンです。快適なユーザー体験を提供するためには、まだいくつかの課題を解決する必要があります。
- ユーザーのインベントリにどのトークンや権利があるか、チェーンからどのように読み取るのでしょうか?
- Arbitrumのような一部のチェーンでは高速ですが、ブロックチェーンのトランザクションは即時ではありません。コイン(または他のアイテム)を集めてから、ゲーム内インベントリに反映されるまで数秒待つ必要があるのは、一般的に良いユーザー体験とは言えません。
- 一見すると、ユーザーがトークンを獲得するたびにトランザクションを送信したくなるかもしれません。しかし、特にJelly Forestのように大量のコイン(トークン)を集めるゲームでは、これでは膨大な数のトランザクションが発生し、ガス代が非常に高額になってしまいます!
1. チェーンの読み取り
特定ユーザーのウォレット内のトークンを読み取る作業は複雑ですが、SequenceのIndexerを利用することで大幅に簡略化できます。Unity SDKでも実装済みです。 Jelly Forestで、Indexerを使ってプレイヤーのウォレットからゲームのERC1155コントラクトの全トークンを読み取るコード例を示します。2. キャッシュの構築
ブロックチェーンのトランザクションは即時反映されませんが、ユーザーに即時のフィードバックを提供するため、シンプルなインメモリキャッシュを利用します。 Jelly ForestでSequenceWallet を最初に受け取ると、SequenceConnector(ゲーム内でSequence SDKとの通信に使用する主要インターフェース)が Inventory を作成します。
Inventory はゲーム内でシンプルなキャッシュとして使われます。作成時や必要に応じて、Indexerでユーザーのウォレット内トークンを取得します。その後、ユーザーがトークンを獲得するたびにキャッシュ(Inventory)とオンチェーンデータを更新します。
Inventory の全実装はこちらでご覧いただけます
3. トランザクションキューの活用
SequenceのUnity SDKには、非常に柔軟なトランザクションキューイングシステムが用意されています。 Jelly Forestでは、PermissionedMinterTransactionQueuer MonoBehaviourをSequenceConnector GameObjectにアタッチし、Awakeで参照を取得しています。

mint token関数を呼び出すだけです。
Inventoryが更新され、PermissionedMinterTransactionQueuerのキューにミントトランザクションが追加されます。PermissionedMinterTransactionQueuerは、可能な限りトランザクションを自動的にまとめて処理し、ガス代を最小限に抑えます。
Jelly Forest では、プレイヤーがゲームオーバーになるたびにトランザクションキューアがトランザクションを送信するよう設定しています。ただし、30秒以内には送信されません。
トランザクション送信頻度の決定方法
Unity SDK を使う場合、これは技術的な問題というよりもゲームデザイン上の判断になります。TransactionQueuers(トランザクションキューア)は、X秒ごとに自動でトランザクションを送信したり、関数呼び出しでY秒以上経過していれば送信したり、または促された場合に最小時間制限(Y秒)を無視して送信することも可能です。
トランザクションキューアの設定を決める際に考慮すべきポイントをいくつか挙げます:
- トランザクションを送信する頻度が高いほど、ガス代も多くかかります。もちろん、選択するEVM互換ブロックチェーンによって、コストが問題になる前に送信できるトランザクションの数や複雑さは大きく異なります。
- トランザクションの送信頻度が低いと、ゲームの状態(キャッシュ)とオンチェーンの情報との同期が取れなくなります。もしトランザクションが失敗した場合、プレイヤーの体験を損なわずにリカバリーできる仕組みが必要です。
6. ゲーム内トークンを他のトークンと交換してバーンする
Jelly Forest では、コインや(場合によっては)下位のパワーアップをバーンすることで、パワーアップやコスメティックアイテムを購入できます。 この仕組みを実現し、強制するために、シンプルなBurnToMintスマートコントラクトをデプロイしました。このコントラクトでは、特定のトークンIDに対してミント要件(必要なトークンIDとその数量)を指定できます。ERC1155トークンのバッチを受け取ると、送信者がdataパラメータでミントしたいトークンIDを指定します。コントラクトは各トークンIDの必要数を受け取っているか確認し、条件を満たせばトークンをバーンして指定されたトークンIDを送信者(ユーザー)にミントします。条件を満たさない場合は、トランザクションが失敗し、元に戻されます。
このコントラクトには、Builder Console でゲームコントラクトのミント権限を付与しています:
Contractsから該当コントラクトを探し、クリックして開きます。Write Contractをクリックします。grantRoleを展開します。roleには0x9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a6を入力します。これはMINTER_ROLEのKeccak-256ハッシュです。accountにはミンターのウォレットアドレスを貼り付けます。
BurnToMintスマートコントラクトは、第三者による監査を受けていません。再利用の際は十分ご注意ください。
:::
ユーザーがショップでアップグレードやコスメティックを購入すると、PurchaseShopItemQueueableTransaction を SequenceWalletTransactionQueuer に追加することで、SequenceConnector から BurnToMint スマートコントラクトへトランザクションが送信されます。
7. ショップページの構築とミント要件の設定
Jelly Forest のショップページを構築し、各アップグレードや帽子の価格やミント要件を設定する際に、Scriptable Object を使って ShopItems を定義しました。Scriptable Object は Inspector でシリアライズできるため、調整や可視化が容易です。また、これらの Scriptable Object で各 Item の内容やトークンIDとの紐付けも行っています。 しかし、Scriptable Object で定義したミント要件と、オンチェーンのBurnToMint コントラクトで定義したミント要件を同期させるのは手間がかかり、バグの原因にもなりかねませんでした。
そこで、ShopItem の Scriptable Object 用に エディタ拡張 を作成し、ボタンを追加しました。このボタンを押すと、オンチェーンのミント要件と Scriptable Object で定義した内容が一致しているかを確認します。一致していない場合は、Scriptable Object に合わせてオンチェーンの BurnToMint コントラクトのミント要件を更新するトランザクションを送信します。トランザクションは、開発者のマシンに環境変数として保存された秘密鍵から作成した EOA ウォレット経由で送信されます。この EOA ウォレットが、このコントラクトの オーナー です。
実際、ショップページは60秒ごと(およびページを開くたび)にスマートコントラクトへミント要件の変更を問い合わせ、UIを自動で更新しています。これにより、ゲームの経済バランスをライブで調整でき、アップデート不要で反映できます。
下の動画をクリックしてください
8. 購入したアイテムをゲーム内で活用する
これで、プレイヤーはログインしてウォレットを作成し、トークンを獲得し、そのトークンでアイテムを購入できるようになりました。あとは、プレイヤーがアイテムを欲しくなる理由を作るだけです。つまり、ゲーム開発者として魅力的なパワーアップやコスメティックを作る番です。 トークンをゲーム内で使うには、ユーザーが指定したトークンIDを十分に所有しているか確認して、その効果を適用するだけです。 Jelly Forest では、いくつかのPowerUpTypesを定義し、各ItemにPowerUpTypeとティアを割り当てています。そして、プレイヤーが所有する各タイプの最強のパワーアップをInventoryから検索します。