Unity React WebGL + NextJS 整合以太坊錢包到你的 GameFi 專案裡

本文利用 react-unity-webgl 搭配 web3-react,Demo 在 Unity 下如何連接錢包並顯示錢包資訊,以及 Unity 如何與 Web3 進行溝通。

請先完成 Web3-React 的 NextJS 專案

NextJS + Web3-React 快速整合以太坊的各種錢

隨著 GameFi 項目轉向更高可玩性以及更高品質的趨勢,各種與常用遊戲引擎 Unity、Unreal 的 Service 相繼出現,如 Moralis、ChainSafe、Stratis、Enjin 等。但目前還處於 GameFi 初期,開發者還沒到這麼多,所以多少會受限於該 Service 提供的 SDK 完整度。

Unity 端

Unity => Web:定義 Extern 函式

首先,在 Unity 下製作三顆按鈕,用來連接常用的三種以太坊錢包
利用 Enum 的方式來定義錢包種類後
定義一個帶有 DllImport Attribute 的 Extern Connect 函式
呼叫時把剛定義的 WalletType

public enum WalletType
{
  MetaMask,
  WalletConnect,
  WalletLink
}

[DllImport("__Internal")]
private static extern void Connect(WalletType type);

m_metaMaskButton.onClick.AddListener(() => { Connect(WalletType.MetaMask); });
m_walletConnectButton.onClick.AddListener(() => { Connect(WalletType.WalletConnect); });
m_walletLinkButton.onClick.AddListener(() => { Connect(WalletType.WalletLink); });

透過 jslib 實作 Extern 函式

定義好 Extern 的函式後,我們需要在 Plugins 下新增 jslib 的檔案來實作它,檔名自訂
ReactUnityWebGL 是 Web 端 react-unity-webgl 的元件
透過這個元件,我們可以很輕鬆呼叫 Web 端自定義的函式

mergeInto(LibraryManager.library, {
    Connect: function (type) {
        ReactUnityWebGL.Connect(type);
    },
});

Web => Unity:實作 public 函式

這邊作法很簡單,用一個 Text 來顯示 Web 端的連接狀態
連接成功就傳送 Wallet Account 過來,反之就送 “Not connected” 字串

[SerializeField]
private Text m_log;

public void SetLog(string log)
{
  m_log.text = log;
}

Build WebGL

在 Publish Settings 下先把壓縮關掉後
Compression Format:Disabled
就可以 Build WebGL 專案了
發佈完成後,在 Build 資料夾下會有 4 個以專案名稱開頭的檔案
data、freamework.js、loader.js、wasm

這樣 Unity 端就完成了,接下來可以著手進行 Web 端


Web 端

安裝 react-unity-webgl

yarn add react-unity-webgl

把 WebGL Build 資料夾複製到 public 下

把 WebGL Build 資料夾複製到 public 下!
把 WebGL Build 資料夾複製到 public 下!
把 WebGL Build 資料夾複製到 public 下!

不然會抓不到檔案喔!

UnityContext

UnityContext 就是用來串接 WebGL 的元件
而這邊就要對應填入剛剛 Build 好的那 4 個檔案
data、freamework.js、loader.js、wasm

接著在主函式加入 Unity 標籤來顯示 unityContext
可以設定 style 來調整大小和對齊

import Unity, { UnityContext } from 'react-unity-webgl';

export const unityContext = new UnityContext({
  loaderUrl: "Build/UnityReact.loader.js",
  dataUrl: "Build/UnityReact.data",
  frameworkUrl: "Build/UnityReact.framework.js",
  codeUrl: "Build/UnityReact.wasm",
});

function MyApp({ Component, pageProps }: AppProps)
{
  return (
    <>
      <Web3ReactProvider getLibrary={getLibrary}>
        <Component {...pageProps} />
      </Web3ReactProvider>

      <Unity 
		style={{width:"100%", height:"50%", alignSelf:"center"}} 
        unityContext={unityContext} />
    </>
  )
}

Unity => Web:UnityContext.on

透過剛才在 _app.tsx 下 export 的 UnityContext 元件
我們可以使用 on 這個方法來註冊事件監聽器
EventName 就是剛剛我們在 Unity 定義的 Extern Connect 函式
WalletType Enum 則是跟 Unity 端做對應

function on(eventName: string, eventListener: Function): void;
import { unityContext } from './_app';

const Home: NextPage = () => {
  // ...
  
  enum WalletType {
    Injected,
    WalletConnect,
    WalletLink
  }

  unityContext.on("Connect", (type) => {
    switch (type) {
      case WalletType.Injected: activate(Connectors.Injected); break;
      case WalletType.WalletConnect: activate(Connectors.WalletConnect); break;
      case WalletType.WalletLink: activate(Connectors.WalletLink); break;
    }
  });
}

Web => Unity:UnityContext.send

Call Unity 端其實也很容易
透過 send 函式把 GameObject 名稱、public 函式名稱和參數帶入即可
這邊就如稍早提到的,利用 web3-react 的 active state 判斷連線狀態
連接成功就送擷取過的 Account 字串,反之就送 “Not connected” 字串

function send(
  gameObjectName: string,
  methodName: string,
  parameter?: string | number | boolean
): void;
import { unityContext } from './_app';

const Home: NextPage = () => {
  // ...
  const { active, activate, deactivate, account } = useWeb3React();
  
  function setLog(log:any) {
    unityContext.send("ReactController", "SetLog", log);
  }
  
  return (
    <>
      {active ? setLog(account?.substring(0, 15) : setLog("Not connected")}
	</>
  )
}

發佈留言