跳至主要內容
本頁面使用機器翻譯自英語,可能包含錯誤或不清楚的語言。如需最準確的信息,請參閱英文原文。由於更新頻繁,部分內容可能與英文原文有出入。請加入我們在 Crowdin 上的努力,幫助我們改進本頁面的翻譯。 (Crowdin translation page, Contributing guide)

Reown

介紹​

Reown讓建置者能夠建立安全、人性化且具備豐富洞察力的錢包與應用程式 UX。提供所有工具,讓您以更快的速度推出產品,並更聰明地擴充規模。有了 Unity 上的 Reown AppKit,您可以輕鬆地將 onchain 生態系統連接到您的遊戲,實現流暢的錢包互動,讓玩家感覺自然。

在本教程中,您將學習如何逐步將 Reown AppKit 整合到以 Kaia 為基礎的 Unity 遊戲中,讓您的玩家可以直接在遊戲中與 Web3 互動。

先決條件。​

開始​

在本指南中,您將學習如何使用 Kaia 上的 Reown AppKit 將錢包功能整合到您的 Unity 遊戲中。到最後,您將擁有一個可讓玩家進行以下工作的設定:

  • 連接和斷開他們的錢包
  • 檢視他們的本幣餘額和代幣餘額
  • 直接在遊戲中領取 Soulbound 代幣 (SBT)

我們將在 Kaia Mainnet 上建立此系統 (用於教育目的),但相同的程式碼可在 Kairos Testnet 上無縫執行,以進行開發與測試。

建立新的 Unity3D 專案​

若要建立新專案,請執行下列步驟:

  • 導覽到 ** 專案** 索引標籤、
  • 按一下 ** 新專案** 按鈕。
  • 選擇所有範本。我們將採用3D 模版、
  • 按一下 ** 建立**專案。

在 Unity 上安裝 Reown AppKit​

使用 Open UPM 的套件管理員

  1. 從位於 Unity 功能表列頂端的服務功能表開啟「一般設定」。
  2. 新增具有下列詳細資訊的新作用範圍註冊表:

  1. 按加➕,然後按儲存按鈕
  2. 從位於 Unity 頂端的 Windows 功能表開啟套件管理員。

  1. 從工具列開啟新增 ➕ 功能表,選擇依名稱新增套件…

  1. 逐一輸入下列套件的名稱:
    • com.nethereum.unity
    • com.reown.core
    • com.reown.core.crypto
    • com.reown.core.common
    • com.reown.core.network
    • com.reown.core.storage
    • com.reown.sign
    • com.reown.sign.unity
    • com.reown.sign.nethereum
    • com.reown.sign.nethereum.unity
    • com.reown.appkit.unity
    • com.reown.unity.dependencies
  2. 按下 ** 新增** 按鈕

設定應用程式場景​

我們需要為 Web3 功能準備場景:

載入場景​

  • 在 Assets > Scenes 下,按兩下 SampleScene。這將是我們所有 Claim SBT 功能所在的地方。

新增 Reown AppKit Prefab​

  • 導覽至專案 > 套件 > Reown.AppKit.Unity > Prefabs

  • 將 Reown AppKit Prefab 拖曳到您的場景層級結構中

建立使用者介面畫布​

  • 在場景階層中按一下滑鼠右鍵
  • 導覽至 GameObject > UI > Canvas
  • 將它重新命名為 Canvas

建立我們的應用程式介面​

在本節中,我們將建立 Soul Bound Token 領取功能的使用者介面元件。為此,我們將建立一個主面板和 3 個子面板及其對應的子元件。

主面板*

  • StatusPanel:顯示錢包連線資訊和代幣餘額。
  • ButtonPanel:連接和斷開錢包按鈕。
  • ** 領取面板**:領取 SBT 代幣並檢視 SBT 結餘

建立MainPanel、

  • 在 Canvas 上按一下滑鼠右鍵,按一下 UI → Panel 並將其重新命名為 MainPanel

建立子面板、

  • 在 MainPanel 上按一下滑鼠右鍵,按一下 UI → Panel 並將其重新命名為 StatusPanel
  • 重複上述步驟,分別在 MainPanel 中建立 ButtonPanel 和 ClaimPanel。

現在讓我們在每個子面板中建立元件。

建立 StatusPanel 元件、

  • 在 StatusPanel 上按一下滑鼠右鍵,按一下 UI → 文字 - TestMeshPro,然後將文字物件重新命名為 WalletStatusText。
    • 確保填滿文字物件。例如「狀態:未連線"
  • 在 StatusPanel 上按一下滑鼠右鍵,按一下 UI → 文字 - TestMeshPro 並將文字物件重新命名為 AccountText
    • 確保填滿文字物件。例如 「連線位址:」
  • 在 StatusPanel 上按一下滑鼠右鍵,按一下 UI → 文字 - TextMeshPro,然後將文字物件重新命名為 KaiaBalanceText。
    • 確保填滿文字物件。例如 「KAIA 結餘 :-」
  • 在 StatusPanel 上按一下滑鼠右鍵,按一下 UI → 文字 - TextMeshPro,然後將文字物件重新命名為 UsdtBalanceText。
    • 確保填滿文字物件。例如 「USDT 結餘 :-」

建立 ButtonPanel 元件、

  • 在 ButtonPanel 上按一下滑鼠右鍵,按一下 UI → Button - TextMeshPro,然後將文字物件重新命名為 ConnectWallet。
    • 確保填滿文字物件。例如 「連接錢包」
  • 在 ButtonPanel 上按一下滑鼠右鍵,按一下 UI → Button - TextMeshPro,然後將文字物件重新命名為 DisconnectWallet。
    • 確保填滿文字物件。例如 「中斷 Wallet」。

建立 ClaimPanel 元件、

  • 在 ClaimPanel 上按一下滑鼠右鍵,按一下 UI → 文字 - TextMeshPro,然後將文字物件重新命名為 ClaimLabel。
    • 確保填滿文字物件。例如 "Claim Soul Bound NFT:"。
  • 在 ClaimPanel 上按一下滑鼠右鍵,按一下 UI → 按鈕 - TextMeshPro,然後將文字物件重新命名為 ClaimButton。
    • 確保填滿文字物件。例如 「索賠」
  • 在 ClaimPanel 上按一下滑鼠右鍵,按一下 UI → 文字 - TextMeshPro,然後將文字物件重新命名為 ClaimBalanceText。
    • 確保填滿文字物件。例如 「餘額:」
備註

建立所有元件後,使用「移動工具」將它們整齊地排列在場景中。

建立與部署 Soul Bound Token 智慧契約​

在本節中,我們將使用下面的範例程式碼作為指引:


// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;
import "@kaiachain/contracts/KIP/token/KIP17/KIP17.sol";
import "@kaiachain/contracts/utils/Counters.sol";
import "@kaiachain/contracts/access/Ownable.sol";
contract SoulBoundToken is KIP17, Ownable {
using Counters for Counters.Counter;
Counters.Counter private _tokenIdCounter;
constructor() KIP17("SoulBoundToken", "SBT") {}
function safeMint(address to) public onlyOwner {
uint256 tokenId = _tokenIdCounter.current();
_tokenIdCounter.increment();
_safeMint(to, tokenId);
}
function _beforeTokenTransfer(address from, address to, uint256) pure override internal {
require(from == address(0) || to == address(0), "This a Soulbound token. It cannot be transferred.");
}
function _burn(uint256 tokenId) internal override(KIP17) {
super._burn(tokenId);
}
}

透過 Remix IDE 部署​

  • 複製並貼上上述程式碼到 Remix IDE 新建立的檔案 SBT.sol。
  • 在 Remix IDE 中:
    • 按一下「編譯契約」按鈕。
    • 導覽至「部署與執行交易」索引標籤。
    • 在 Environment(環境)下,選擇 Injected Provider - MetaMask。
    • 在合約下拉式選單中找到您的合約 (SoulBoundToken)。
    • 按一下部署以啟動您的令牌!
    • 當您的 MetaMask 彈出時:
      • 檢視部署詳細資訊。
      • 按一下「確認」以部署到 Kaia Mainnet。
備註

複製並儲存已部署的合約位址。您在稍後的教學中會用到它。

執行 SBTManager Script​

在這一步中,我們將建立一個腳本來管理核心的錢包互動:連接和斷開錢包、擷取本機貨幣 (KAIA) 和代幣餘額 (USDT) 以及啟用 SBT 索賠功能。

要做到這一點、

  • 在 Assets 資料夾中建立 Scripts 資料夾
  • 在新建立的 Scripts 資料夾中建立 SBTManager 指令碼。
  • 複製並貼上以下程式碼到您新建立的 scripts 檔案:

using UnityEngine;
using UnityEngine.UI;
using TMPro;
using Reown.AppKit.Unity;
using System.Threading.Tasks;
using System.Numerics;
public class SBTManager : MonoBehaviour
{
[Header("Contract Configuration")]
public string tokenContractAddress = "0xd077a400968890eacc75cdc901f0356c943e4fdb";
public string sbtContractAddress = "0x0bFF901145a0685E6d7661af7122f829459dA9A0";
[Header("UI References")]
public TextMeshProUGUI statusText;
public TextMeshProUGUI accountText;
public TextMeshProUGUI kaiaBalanceText;
public TextMeshProUGUI tokenBalanceText;
public TextMeshProUGUI sbtBalanceText;
[Header("Buttons")]
public Button connectButton;
public Button disconnectButton;
public Button mintSBTButton;
// Token ABI for balance checking
private const string TOKEN_ABI = "[{\"constant\":true,\"inputs\":[{\"name\":\"_owner\",\"type\":\"address\"}],\"name\":\"balanceOf\",\"outputs\":[{\"name\":\"balance\",\"type\":\"uint256\"}],\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"decimals\",\"outputs\":[{\"name\":\"\",\"type\":\"uint8\"}],\"type\":\"function\"}]";
// SBT Mint ABI
private const string MINT_ABI = "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"safeMint\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]";
// NFT Balance ABI
private const string NFT_BALANCE_ABI = "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"balanceOf\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]";
private async void Start()
{
await InitializeWallet();
SetupUI();
InitializeDisconnectedState();
}
private async Task InitializeWallet()
{
var config = new AppKitConfig
{
// Replace with your actual Reown project ID.
projectId = "PASTE PROJECT ID",
metadata = new Metadata(
"Kaia SBT Demo",
"Demo app for Kaia blockchain SBT minting",
"https://your-domain.com",
"https://your-domain.com/icon.png"
),
supportedChains = new[]
{
// Kaia Mainnet
new Chain(ChainConstants.Namespaces.Evm,
chainReference: "8217",
name: "Kaia Mainnet",
nativeCurrency: new Currency("KAIA", "KAIA", 18),
blockExplorer: new BlockExplorer("Kaiascan", "https://kaiascan.io/"),
rpcUrl: "https://alpha-hardworking-orb.kaia-mainnet.quiknode.pro/",
isTestnet: false,
imageUrl: "https://prnt.sc/P5TR6Zpr0Y1S"
),
// Kaia Kairos testnet
new Chain(ChainConstants.Namespaces.Evm,
chainReference: "1001",
name: "Kaia Kairos",
nativeCurrency: new Currency("KAIA", "KAIA", 18),
blockExplorer: new BlockExplorer("Kaiascan", "https://kairos.kaiascan.io/"),
rpcUrl: "https://responsive-green-emerald.kaia-kairos.quiknode.pro/",
isTestnet: true,
imageUrl: "https://prnt.sc/P5TR6Zpr0Y1S"
)
}
};
await AppKit.InitializeAsync(config);
AppKit.AccountConnected += OnAccountConnected;
AppKit.AccountDisconnected += OnAccountDisconnected;
Debug.Log("Kaia SBT Wallet initialized successfully!");
}
/*
SetupUI attaches button click events to their corresponding wallet actions:
- Connect button → ConnectWallet()
- Disconnect button → DisconnectWallet()
- Mint SBT button → ClaimSBT()
*/
private void SetupUI()
{
if (connectButton != null)
connectButton.onClick.AddListener(ConnectWallet);
if (disconnectButton != null)
disconnectButton.onClick.AddListener(DisconnectWallet);
if (mintSBTButton != null)
mintSBTButton.onClick.AddListener(ClaimSBT);
}
private void InitializeDisconnectedState()
{
UpdateStatus("Ready - Click Connect to link wallet");
UpdateAccount("No wallet connected");
UpdateKaiaBalance("0 KAIA");
UpdateTokenBalance("0 Tokens");
UpdateSBTBalance("0 SBTs");
SetButtonStates(connectEnabled: true, disconnectEnabled: false, mintEnabled: false);
}
#region Wallet Connection
public void ConnectWallet()
{
Debug.Log("Opening wallet connection...");
UpdateStatus("Opening wallet selection...");
AppKit.OpenModal();
}
public async void DisconnectWallet()
{
Debug.Log("Disconnecting wallet...");
UpdateStatus("Disconnected...");
try
{
await AppKit.DisconnectAsync();
// The OnAccountDisconnected event should handle UI updates,
// but we can force it here as a fallback
InitializeDisconnectedState();
}
catch (System.Exception e)
{
Debug.LogError($"Disconnect error: {e.Message}");
UpdateStatus($"Disconnect failed: {e.Message}");
}
}
public void ClaimSBT()
{
_ = MintSBT();
}
#endregion
#region Balance Checking
public async Task CheckKaiaBalance()
{
try
{
var account = await AppKit.GetAccountAsync();
if (account == null) return;
BigInteger balance = await AppKit.Evm.GetBalanceAsync(account.Address);
decimal kaia = (decimal)balance / (decimal)BigInteger.Pow(10, 18);
UpdateKaiaBalance($"{kaia:F4} KAIA");
Debug.Log($"KAIA balance: {kaia:F4}");
}
catch (System.Exception e)
{
Debug.LogError($"Error getting KAIA balance: {e.Message}");
UpdateKaiaBalance("Error");
}
}
public async Task CheckTokenBalance()
{
try
{
var account = await AppKit.GetAccountAsync();
if (account == null) return;
var balance = await AppKit.Evm.ReadContractAsync<BigInteger>(
tokenContractAddress,
TOKEN_ABI,
"balanceOf",
new object[] { account.Address }
);
var decimals = await AppKit.Evm.ReadContractAsync<BigInteger>(
tokenContractAddress,
TOKEN_ABI,
"decimals"
);
decimal tokens = (decimal)balance / (decimal)BigInteger.Pow(10, (int)decimals);
UpdateTokenBalance($"{tokens:F4} USDT");
Debug.Log($"Token balance: {tokens:F4}");
}
catch (System.Exception e)
{
Debug.LogError($"Error getting token balance: {e.Message}");
UpdateTokenBalance("Error");
}
}
public async Task CheckSBTBalance()
{
try
{
var account = await AppKit.GetAccountAsync();
if (account == null) return;
var balance = await AppKit.Evm.ReadContractAsync<BigInteger>(
sbtContractAddress,
NFT_BALANCE_ABI,
"balanceOf",
new object[] { account.Address }
);
UpdateSBTBalance($"{balance} SBTs");
Debug.Log($"SBT balance: {balance}");
}
catch (System.Exception e)
{
Debug.LogError($"Error getting SBT balance: {e.Message}");
UpdateSBTBalance("Error");
}
}
#endregion
#region SBT Minting
public async Task MintSBT()
{
try
{
var account = await AppKit.GetAccountAsync();
if (account == null)
{
UpdateStatus("No wallet connected!");
return;
}
UpdateStatus("Minting SBT...");
string txHash = await AppKit.Evm.WriteContractAsync(
sbtContractAddress,
MINT_ABI,
"safeMint",
account.Address
);
UpdateStatus($"SBT minted! TX: {txHash.Substring(0, 10)}... Waiting for confirmation...");
Debug.Log($"SBT mint transaction: {txHash}");
// Check updated SBT balance
await CheckSBTBalance();
UpdateStatus($"SBT successfully minted! TX: {txHash.Substring(0, 10)}...");
}
catch (System.Exception e)
{
UpdateStatus($"Mint failed: {e.Message}");
Debug.LogError($"SBT mint error: {e}");
}
}
#endregion
#region Event Handlers
private async void OnAccountConnected(object sender, System.EventArgs e)
{
var account = await AppKit.GetAccountAsync();
if (account != null)
{
Debug.Log($"Wallet connected: {account.Address}");
UpdateStatus("Wallet connected");
UpdateAccount(account.Address);
// Check all balances
await CheckKaiaBalance();
await CheckTokenBalance();
await CheckSBTBalance();
SetButtonStates(connectEnabled: false, disconnectEnabled: true, mintEnabled: true);
}
}
private void OnAccountDisconnected(object sender, System.EventArgs e)
{
Debug.Log("Wallet disconnected");
InitializeDisconnectedState();
}
#endregion
#region UI Updates
private void UpdateStatus(string message)
{
if (statusText != null)
statusText.text = message;
Debug.Log($"Status: {message}");
}
private void UpdateAccount(string address)
{
if (accountText != null)
{
if (address.Length > 10)
accountText.text = $"{address.Substring(0, 6)}...{address.Substring(address.Length - 4)}";
else
accountText.text = address;
}
}
private void UpdateKaiaBalance(string balance)
{
if (kaiaBalanceText != null)
kaiaBalanceText.text = balance;
}
private void UpdateTokenBalance(string balance)
{
if (tokenBalanceText != null)
tokenBalanceText.text = balance;
}
private void UpdateSBTBalance(string balance)
{
if (sbtBalanceText != null)
sbtBalanceText.text = balance;
}
private void SetButtonStates(bool connectEnabled, bool disconnectEnabled, bool mintEnabled)
{
if (connectButton != null)
connectButton.interactable = connectEnabled;
if (disconnectButton != null)
disconnectButton.interactable = disconnectEnabled;
if (mintSBTButton != null)
mintSBTButton.interactable = mintEnabled;
}
#endregion
#region Public Methods for Manual Updates
public async void RefreshAllBalances()
{
UpdateStatus("Refreshing balances...");
await CheckKaiaBalance();
await CheckTokenBalance();
await CheckSBTBalance();
UpdateStatus("Balances updated!");
}
#endregion
private void OnDestroy()
{
AppKit.AccountConnected -= OnAccountConnected;
AppKit.AccountDisconnected -= OnAccountDisconnected;
}
}

設定 SBTManager 遊戲物件​

  • 在場景中建立管理員物件
    • 在 Hierarchy 視窗(根層級)上按一下滑鼠右鍵。
    • 選擇「建立空物件」。
    • 將其命名為「SBTManager」。
  • 附上您的劇本
    • 選取 SBTManager GameObject。
    • 在 Inspector 中,按一下 Add Component。
    • 搜尋並選擇「SBTManager」。
  • 連接 UI 元件:
    • 選取 SBTManager 後,查看 Inspector。
    • 將您的 UI 元件從 Hierarchy 拖放到對應的欄位:
      • 狀態文字
      • 帳戶文字
      • 等 …...
    • 分別設定您的 USDT 和 SBT 合約地址

連接按鈕與腳本​

在本節中,我們將從 SBTManager 指令碼中連接下列按鈕與其各自的功能:

  • 選擇按鈕 (如 ConnectWallet)
    • 在 Inspector 中,找到 OnClick() 部分並按一下 ➕ 按鈕
    • 從階層視窗拖曳 SBTManager 物件到 None (物件) 欄位
    • 按一下無功能 → SBTManager → ConnectWallet()

  • 對下面的按鈕重覆步驟 1-3,並將其功能變更為:
    • DisconnectWallet -> DisconnectWallet()
    • ClaimButton -> ClaimSBT()

WebGL 建立設定​

在本節中,我們將為網頁部署設定 dApp:

  • 移至檔案 → 建立設定 → WebGL → 切換平台

  • 在同一個視窗中,按一下新增開放場景(右上方),將 SampleScene 新增為執行遊戲時出現的第一個場景。

  • 在同一視窗中,按一下播放器設定 → 播放器 → 解析度和呈現,在 WebGL 模版下,選擇標題為 Default 的那個。

測試和執行 Unity 應用程式​

在本節中,我們將測試 unity 應用程式中的各種功能。若要瞭解實際操作,請遵循以下步驟:

  • 建立並執行專案:導覽至檔案 → 建立並執行

  • 出現提示時儲存您的 WebGL 建立
  • 當專案建立並執行時,會在瀏覽器中開啟一個索引標籤 - SampleScene
  • 按一下連線錢包以連線至 Metamask。

  • 連線後,測試應用程式的所有功能:
    • 索賠 SBT
    • 檢查結餘

總結​

在本教程中,您將學習如何在 Kaia 上將 Reown AppKit 套件整合到您的 Unity 遊戲中。如需更深入的 Reown Unity 遊戲 SDK 使用指南,請參考 Reown AppKit on Unity Guide。

讓這個頁面變得更好