jeffreylanters / react-unity-webgl

React Unity WebGL provides a modern solution for embedding Unity WebGL builds in your React Application while providing advanced APIs for two way communication and interaction between Unity and React.
https://react-unity-webgl.dev
Apache License 2.0
1.75k stars 162 forks source link

SendMessage: object does not have receiver for function #498

Open nwoodr94 opened 1 year ago

nwoodr94 commented 1 year ago

Please avoid duplicates

Language and Compiler

Babel and WebPack JavaScript

What environment are you using?

Local Development Server

When does your problem occur?

When the Unity App is running

What does your problem relate to?

The problem seems Unity related

React-Unity-WebGL Version

^9.4.0

React Version

^18.1.0

Unity Version

2021.3.5f1

What happened?

It appears that this library doesn't work with static Unity functions.

I wanted to call a Unity function from React, and then sends Unity data back to React. With static functions, this doesn't work, and so I had to refactor my Unity class to use regular methods. Not sure if this is expected behavior, but the error message was confusing.

Using the sample code attached to this bug report, the console will log:

SendMessage: object ReactManager does not have receiver for function RetrieveGameObjects!

Action: Maybe update the error message to suggest static methods are not supported.

Reproducible test case

ListNodes.cs

using UnityEngine;
using System.Runtime.InteropServices;
using System.Collections.Generic;
using Utility;

namespace DeepLynx
{
    public class ListNodes : MonoBehaviour
    {
       // This script is attached to an empty game object called ReactManager

        [DllImport("__Internal")]
        private static extern void SendGameObjects(string response);

        public static void RetrieveGameObjects()
        {
            Debug.Log("Retrieving GameObjects");
            string response = ListGameObjects();

            Debug.Log(response); // "Object1, Object2, Object3"

#if UNITY_WEBGL == true && UNITY_EDITOR == false
            SendGameObjects(response);
#endif
        }

        public static string ListGameObjects()
        {
            // This is a custom function that returns a list of game objects
            List<GameObject> gameObjects = CustomTags.GetGameObjects("SpecificTag"); 
            List<string> nodes = new List<string>();

            foreach (GameObject go in gameObjects)
            {
                nodes.Add(go.name);
            }

            return string.Join(",", nodes);
        }
    }
}

/Plugins/WebGL/React.jslib

mergeInto(LibraryManager.library, {
  SendGameObjects: function (response) {
    try {
      window.dispatchReactUnityEvent("SendGameObjects", UTF8ToString(response));
    } catch (e) {
      console.warn("Failed to dispatch event");
    }
  }
});

Component.js

// React Hooks
import React, { useCallback, useEffect, useState } from "react";

// Unity
import { Unity, useUnityContext } from "react-unity-webgl";

// Material
import {
  Box,
  Button,
} from "@mui/material";

function WebGL(props) {

  // GameObjects
  const [GameObjects, SetGameObjects] = useState();

  const { unityProvider, sendMessage, addEventListener, removeEventListener } =
    useUnityContext({
      // These 4 compiled assets are the bundle that Unity generates when you build to WebGL
      loaderUrl: "webgl/sandbox/Build/sandbox.loader.js",
      dataUrl: "webgl/sandbox/Build/sandbox.data",
      frameworkUrl: "webgl/sandbox/Build/sandbox.framework.js",
      codeUrl: "webgl/sandbox/Build/sandbox.wasm",
    });

  const handleGameObjects = useCallback((response) => {
    console.log(response);
  }, []);

  useEffect(() => {
    addEventListener("SendGameObjects", handleGameObjects);
    return () => {
      removeEventListener("SendGameObjects", handleGameObjects);
    };
  }, [addEventListener, removeEventListener, handleGameObjects]);

  function listGameObjects() {
    sendMessage("ReactManager", "RetrieveGameObjects");
  }

  return (
    <Box>
        <Button
          onClick={() => {
            listGameObjects();
          }}
        >
          Game Objects
        </Button>
    </Box>
  );
}

export default WebGL;

Would you be interested in contributing a fix?

jeffreylanters commented 1 year ago

I don't think SendMessage works with static methods, try making it an instance method

dungmv commented 1 year ago

create listGameObjects by useCallback instead of directly