본문 바로가기
개발/Unity

유니티 - Unity에서 리액트로 이벤트 호출하기 (Communication from Unity to React)

by 피로물든딸기 2023. 3. 25.
반응형

Unity 전체 링크

리액트 전체 링크

 

참고

- 유니티 WebGL + React 라우터 적용하기

- 리액트에서 Unity 오브젝트 컨트롤하기

- https://react-unity-webgl.dev/docs/api/event-system

 

리액트에서 Unity 함수를 호출하였으므로, 반대로 Unity에서 React의 state를 변경해보자.


유니티 구현

 

OnMouseOver를 추가하여 왼쪽으로 클릭하면 속도가 빨라지게, 오른쪽으로 클릭하면 느려지게 구현한다.

    private void OnMouseOver()
    {
        if(Input.GetMouseButtonDown(0))
        {
            Debug.Log("Left, Speed Up");
            unitySpeedUp(5.0f);
            return;
        }

        if (Input.GetMouseButtonDown(1))
        {
            Debug.Log("Right, Speed Down");
            unitySpeedDown(5.0f);
            return;
        }
    }

 

reactSpeedUp/Down은 react에서 변수를 증가시키거나 감소시킨다.

구체적인 구현은 react에서 하기 때문에 extern으로 선언한다.

[DllImport("__Internal")]를 사용하기 위해 InteropServices를 선언한다.

using System.Runtime.InteropServices;

public class Test : MonoBehaviour
{
	...
    
    [DllImport("__Internal")] 
    private static extern void reactSpeedUp(float curSpeed, float value);
    [DllImport("__Internal")] 
    private static extern void reactSpeedDown(float curSpeed, float value);

 

unitySpeedUp/Down은 아래와 같이 구현한다.

실제 유니티 오브젝트의 speed를 변경하고, WebGL에서는 react 함수도 호출하도록 구현한다.

    public void unitySpeedUp(float value)
    {
#if UNITY_WEBGL == true && UNITY_EDITOR == false
        reactSpeedUp(rotateSpeed, value);
#endif
        rotateSpeed = rotateSpeed + value;
    }

    public void unitySpeedDown(float value)
    {
#if UNITY_WEBGL == true && UNITY_EDITOR == false
        reactSpeedDown(rotateSpeed, value);
#endif
        rotateSpeed = rotateSpeed - value;
    }

 

전체 스크립트는 다음과 같다.

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

public class Test : MonoBehaviour
{
    bool rotating = true;
    float rotateSpeed = 30.0f;
    Vector3 offset, rotation;

    [DllImport("__Internal")] 
    private static extern void reactSpeedUp(float curSpeed, float value);
    [DllImport("__Internal")] 
    private static extern void reactSpeedDown(float curSpeed, float value);

    public void changeRotate()
    {
        rotating = rotating ? false : true;
    }

    public void unitySpeedUp(float value)
    {
#if UNITY_WEBGL == true && UNITY_EDITOR == false
        reactSpeedUp(rotateSpeed, value);
#endif
        rotateSpeed = rotateSpeed + value;
    }

    public void unitySpeedDown(float value)
    {
#if UNITY_WEBGL == true && UNITY_EDITOR == false
        reactSpeedDown(rotateSpeed, value);
#endif
        rotateSpeed = rotateSpeed - value;
    }

    private void OnMouseOver()
    {
        if(Input.GetMouseButtonDown(0))
        {
            Debug.Log("Left, Speed Up");
            unitySpeedUp(5.0f);
            return;
        }

        if (Input.GetMouseButtonDown(1))
        {
            Debug.Log("Right, Speed Down");
            unitySpeedDown(5.0f);
            return;
        }
    }

    void Update()
    {
        if (rotating)
        {
            offset = Vector3.one;

            rotation.y = -(offset.x + offset.y) * Time.deltaTime * rotateSpeed;

            transform.Rotate(rotation);
        }
    }
}

Plugin 만들기

 

구현만 해서는 안되고 함수를 내보내기 위해서는 플러그인이 필요하다.

먼저 Assets/Plugins/WebGL 폴더를 만든다.

그리고 Show in Explorer를 클릭하자.

 

메모장이나 EditPlus 등을 이용하여 아래와 같이 jslib 파일을 만든다.

파일 이름은 원하는 대로 만들 수 있다.

jslib에서 아래와 같이 코드를 작성해야 Unity → 자바스크립트 함수를 호출할 수 있게 된다.

위에서 extern했던 reactSpeedUp, reactSpeedDown 그리고 변수명을 넣어주었다.

 

파일을 정상적으로 저장하였다면 유니티에서 아래와 같이 플러그인이 생성된 것을 알 수 있다.


리액트 구현

 

useState로 speed( = rotateSpeed)를 선언한다. 

유니티에서 초기 속도가 30.0f였기 때문에 30으로 초기화하였다.

const [speed, setSpeed] = useState(30.0);

 

useUnityContext 에서는 add/removeEventListener를 추가한다.

  const {
    unityProvider,
    sendMessage, // unity 함수를 호출하기 위한 sendMessage 추가
    addEventListener, // unity -> react 통신
    removeEventListener, // unity -> react 통신
    UNSAFE__detachAndUnloadImmediate: detachAndUnloadImmediate,
  } = useUnityContext({
    loaderUrl: "build/html-hosting.loader.js",
    dataUrl: "build/html-hosting.data",
    frameworkUrl: "build/html-hosting.framework.js",
    codeUrl: "build/html-hosting.wasm",
  });

 

리액트에서는 속도를 보기만 할 뿐이므로, 아래와 같이 setSpeed로 speed의 값만 변경한다.

  const setSpeedUp = useCallback((curSpeed, value) => {
    setSpeed(curSpeed + value);
  }, []);

  const setSpeedDown = useCallback((curSpeed, value) => {
    setSpeed(curSpeed - value);
  }, []);

 

위의 speed의 변화를 p 태그로 추가하면 된다.

<p>{`speed : ${speed}`}</p>

 

마지막으로 EventListener들을 useEffect에서 처리하면 된다.

  useEffect(() => {
    addEventListener("reactSpeedUp", setSpeedUp);
    addEventListener("reactSpeedDown", setSpeedDown);

    return () => {
      detachAndUnloadImmediate().catch((reason) => {
        console.log(reason);
      });
      removeEventListener("reactSpeedUp", setSpeedUp);
      removeEventListener("reactSpeedDown", setSpeedDown);
    };
  }, [
    detachAndUnloadImmediate,
    addEventListener,
    removeEventListener,
    setSpeedUp,
    setSpeedDown,
  ]);

 

전체 코드는 다음과 같다.

import React, { useCallback, useEffect, useState } from "react";
import { Unity, useUnityContext } from "react-unity-webgl";

const Router2 = () => {
  const [speed, setSpeed] = useState(30.0);

  const {
    unityProvider,
    sendMessage, // unity 함수를 호출하기 위한 sendMessage 추가
    addEventListener, // unity -> react 통신
    removeEventListener, // unity -> react 통신
    UNSAFE__detachAndUnloadImmediate: detachAndUnloadImmediate,
  } = useUnityContext({
    loaderUrl: "build/html-hosting.loader.js",
    dataUrl: "build/html-hosting.data",
    frameworkUrl: "build/html-hosting.framework.js",
    codeUrl: "build/html-hosting.wasm",
  });

  const setSpeedUp = useCallback((curSpeed, value) => {
    setSpeed(curSpeed + value);
  }, []);

  const setSpeedDown = useCallback((curSpeed, value) => {
    setSpeed(curSpeed - value);
  }, []);

  useEffect(() => {
    addEventListener("reactSpeedUp", setSpeedUp);
    addEventListener("reactSpeedDown", setSpeedDown);

    return () => {
      detachAndUnloadImmediate().catch((reason) => {
        console.log(reason);
      });
      removeEventListener("reactSpeedUp", setSpeedUp);
      removeEventListener("reactSpeedDown", setSpeedDown);
    };
  }, [
    detachAndUnloadImmediate,
    addEventListener,
    removeEventListener,
    setSpeedUp,
    setSpeedDown,
  ]);

  return (
    <div>
      {/* 버튼 추가 */}
      <button onClick={() => sendMessage("Cube", "changeRotate")}>
        Rotate!!
      </button>
      <p>{`speed : ${speed}`}</p>
      <Unity unityProvider={unityProvider} style={{ width: "50%" }} />
    </div>
  );
};

export default Router2;

 

리액트를 실행해서 유니티 오브젝트를 왼쪽/오른쪽 클릭하면 오브젝트의 속도가 변하고

리액트에도 속도(<p> 태그의 speed)가 변경되는 것을 알 수 있다.

 

Unity Plus:

 

Easy 2D, 3D, VR, & AR software for cross-platform development of games and mobile apps. - Unity Store

Have a 2D, 3D, VR, or AR project that needs cross-platform functionality? We can help. Take a look at the easy-to-use Unity Plus real-time dev platform!

store.unity.com

 

Unity Pro:

 

Unity Pro

The complete solutions for professionals to create and operate.

unity.com

 

Unity 프리미엄 학습:

 

Unity Learn

Advance your Unity skills with live sessions and over 750 hours of on-demand learning content designed for creators at every skill level.

unity.com

반응형

댓글