You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React中useUnityContext集成Unity WebGL的问题求助

React集成Unity WebGL(useUnityContext)问题解决方案

问题描述

  • 切换浏览器标签页后出现运行异常
  • 首次加载时React可向Unity发送消息,页面刷新后无法发送

解决方案

1. 修复切换标签页异常问题

浏览器对后台标签页的性能限制会导致Unity WebGL自动暂停/恢复时状态异常,通过手动控制暂停逻辑解决:

步骤1:关闭Unity自动暂停

修改useUnityContext配置,禁用默认的后台自动暂停:

const {
  unityProvider,
  // ...其他属性
} = useUnityContext({
  loaderUrl: "Kiwi/Build/Kiwi.loader.js",
  dataUrl: "Kiwi/Build/Kiwi.data",
  frameworkUrl: "Kiwi/Build/Kiwi.framework.js",
  codeUrl: "Kiwi/Build/Kiwi.wasm",
  streamingAssetsUrl: "Kiwi/StreamingAssets",
  autoPauseOnHidden: false, // 关闭自动暂停
});

步骤2:手动监听页面可见性

添加页面可见性监听,手动控制Unity的暂停与恢复:

useEffect(() => {
  const handleVisibilityChange = () => {
    document.hidden ? unityProvider.pause() : unityProvider.resume();
  };

  document.addEventListener("visibilitychange", handleVisibilityChange);
  return () => document.removeEventListener("visibilitychange", handleVisibilityChange);
}, [unityProvider]);

2. 修复刷新后无法发送消息问题

刷新后消息发送失败通常是Unity未完全加载就执行发送操作,或事件监听依赖缺失导致回调未正确绑定。

步骤1:封装安全的消息发送函数

确保仅在Unity加载完成后发送消息:

const safeSendMessage = useCallback((objectName, methodName, params) => {
  if (isLoaded && unityProvider?.instance) {
    sendMessage(objectName, methodName, params);
  } else {
    console.warn("Unity未加载完成,无法发送消息");
  }
}, [isLoaded, unityProvider, sendMessage]);

步骤2:调整消息发送时机

在getRecord中等待Unity加载完成后再发送历史记录:

const getRecord = useCallback(() => {
  axios.post(API_URL + "/api/auth/joingame", {
    wallet_address: account
  })
  .then((res) => {
    setUserId(res.data.userId);
    axios.get(API_URL + "/api/record/getRecord")
    .then(records => {
      if (isLoaded) {
        safeSendMessage("LoginController", "SetHistory", JSON.stringify(records.data));
      } else {
        // 未加载完成则监听加载事件后发送
        const handleLoaded = () => {
          safeSendMessage("LoginController", "SetHistory", JSON.stringify(records.data));
          removeEventListener("loaded", handleLoaded);
        };
        addEventListener("loaded", handleLoaded);
      }
    });
  });
}, [account, isLoaded, safeSendMessage, addEventListener, removeEventListener]);

步骤3:优化事件监听依赖

移除不必要的依赖,确保事件监听正确绑定:

useEffect(() => {
  addEventListener("ReactSetRecord", setRecord);
  addEventListener("DisconnectWallet", disconnectWallet);
  addEventListener("ConnectWallet", connectWallet);
  return () => {
    removeEventListener("ReactSetRecord", setRecord);
    removeEventListener("ConnectWallet", connectWallet);
    removeEventListener("DisconnectWallet", disconnectWallet);
  };
}, [addEventListener, removeEventListener, connectWallet, disconnectWallet, setRecord]);

完整修改后的组件代码

import styled from "styled-components";
import Footer from "./Footer";
import { Unity, useUnityContext } from "react-unity-webgl";
import { useCallback, useEffect, useState } from "react";
import { useEthers } from "@usedapp/core";
import axios from "axios";
import Button from "./Button";

const API_URL = "http://XXXXXXXXXX";

const StyledLicense = styled.div`
  padding: 0 5rem;
  width: 100%;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  background: var(--bg-02);

  @media (max-width: 720px) {
    padding: 2rem;
    padding-top: 7rem;
  }
`;

const Content = styled.div`
  display: flex;
  flex-direction: column;
  max-width: 150em;
`;

const Loading = styled.h1`
  font-weight: 700;
  color: var(--sub);
  text-transform: uppercase;
  margin-bottom: 0.3rem;
  margin-top: 3rem;

  font-size: 2.4rem;
  @media only screen and (max-width: 600px) {
    font-size: 1.6rem;
  }
`;

const CyberRunner = () => {
  const { activateBrowserWallet, deactivate, account } = useEthers();
  const [userId, setUserId] = useState();
  const {
    unityProvider,
    loadingProgression,
    isLoaded,
    addEventListener,
    removeEventListener,
    requestFullscreen,
    sendMessage
  } = useUnityContext({
    loaderUrl: "Kiwi/Build/Kiwi.loader.js",
    dataUrl: "Kiwi/Build/Kiwi.data",
    frameworkUrl: "Kiwi/Build/Kiwi.framework.js",
    codeUrl: "Kiwi/Build/Kiwi.wasm",
    streamingAssetsUrl: "Kiwi/StreamingAssets",
    autoPauseOnHidden: false,
  });

  const safeSendMessage = useCallback((objectName, methodName, params) => {
    if (isLoaded && unityProvider?.instance) {
      sendMessage(objectName, methodName, params);
    } else {
      console.warn("Unity未加载完成,无法发送消息");
    }
  }, [isLoaded, unityProvider, sendMessage]);

  const connectWallet = useCallback(() => {
    if (!account) {
      activateBrowserWallet();
    } else {
      getRecord();
    }
  }, [account, getRecord]);

  const disconnectWallet = useCallback(() => {
    if (!account) {
      deactivate();
    } else {
      safeSendMessage("LoginController", "SetDisconnectWallet");
    }
  }, [account, deactivate, safeSendMessage]);

  const setRecord = useCallback((_distance: number, _kill: number) => {
    axios.post(API_URL + "/api/record/setRecord", {
      user_id: userId,
      distance: _distance,
      kill: _kill
    })
    .then((res) => {
      console.log(res);
    });
  }, [userId]);

  const getRecord = useCallback(() => {
    axios.post(API_URL + "/api/auth/joingame", {
      wallet_address: account
    })
    .then((res) => {
      setUserId(res.data.userId);
      axios.get(API_URL + "/api/record/getRecord")
      .then(records => {
        if (isLoaded) {
          safeSendMessage("LoginController", "SetHistory", JSON.stringify(records.data));
        } else {
          const handleLoaded = () => {
            safeSendMessage("LoginController", "SetHistory", JSON.stringify(records.data));
            removeEventListener("loaded", handleLoaded);
          };
          addEventListener("loaded", handleLoaded);
        }
      });
    });
  }, [account, isLoaded, safeSendMessage, addEventListener, removeEventListener]);

  useEffect(() => {
    const handleVisibilityChange = () => {
      document.hidden ? unityProvider.pause() : unityProvider.resume();
    };

    document.addEventListener("visibilitychange", handleVisibilityChange);
    return () => document.removeEventListener("visibilitychange", handleVisibilityChange);
  }, [unityProvider]);

  useEffect(() => {
    addEventListener("ReactSetRecord", setRecord);
    addEventListener("DisconnectWallet", disconnectWallet);
    addEventListener("ConnectWallet", connectWallet);
    return () => {
      removeEventListener("ReactSetRecord", setRecord);
      removeEventListener("ConnectWallet", connectWallet);
      removeEventListener("DisconnectWallet", disconnectWallet);
    };
  }, [addEventListener, removeEventListener, connectWallet, disconnectWallet, setRecord]);

  return (
    <StyledLicense>
      {!isLoaded && (
        <Loading>Loading Application... {Math.round(loadingProgression * 100)}%</Loading>
      )}
      <Content>
        <Unity unityProvider={unityProvider} style={{ width: 1280, height: 720 }} />
      </Content>
      <Button text="Fullscreen" click={() => requestFullscreen(true)} primary />
      <Footer />
    </StyledLicense>
  );
};

export default CyberRunner;

内容的提问来源于stack exchange,提问作者BlackDragon94

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.24 14:09:07