Unity——Js和Unity互相调用

Unity项目可以打包成WebGl,打包后的项目文件:

image-20211203184830096

Build中是打包后的Js代码;

Index.html是web项目的入口,里面可以调整web的自适应,也可以拿去嵌套;

TemplateData是打包时候选的webGl模板;

web端游戏可能Unity只负责做游戏部分,而官网由另外的团队制作,之间就需要Unity和Js代码之间的相互调用;

Unity调用JavaScript

声明一下,这里说的都是Unity和外部JS代码的互相调用,项目内调用有其他方法;

老版本提供一个过时的方法

1.在WebGL项目中的Index.html中添加要调用的JS方法

function Unity2JavaScript() { alert("UnityToWeb") }

2.Unity中调用

Application.ExternalCall("Unity2JavaScript");
//可以有参数,没有返回值
//Application.ExternalCall("Unity2JavaScript",a,10,"aaaa");

Unity建议使用的方法

1.在Plugins文件夹中,创建后缀为.jslib的文件,在其中写需要调用的js代码

mergeInto(LibraryManager.library, {

  Hello: function () {
    window.alert("Hello, world!");
  },

  HelloString: function (str) {
    window.alert(Pointer_stringify(str));
  },

  PrintFloatArray: function (array, size) {
    for(var i = 0; i < size; i++)
    console.log(HEAPF32[(array >> 2) + size]);
  },

  AddNumbers: function (x, y) {
    return x + y;
  },

  StringReturnValueFunction: function () {
    var returnStr = "bla";
    var buffer = _malloc(lengthBytesUTF8(returnStr) + 1);
    writeStringToMemory(returnStr, buffer);
    return buffer;
  },

  BindWebGLTexture: function (texture) {
    GLctx.bindTexture(GLctx.TEXTURE_2D, GL.textures[texture]);
  },

});

2.Unity中调用——__Internal.jslib

using UnityEngine;
using System.Runtime.InteropServices;

public class NewBehaviourScript : MonoBehaviour {

    [DllImport("__Internal")]
    private static extern void Hello();

    [DllImport("__Internal")]
    private static extern void HelloString(string str);

    [DllImport("__Internal")]
    private static extern void PrintFloatArray(float[] array, int size);

    [DllImport("__Internal")]
    private static extern int AddNumbers(int x, int y);

    [DllImport("__Internal")]
    private static extern string StringReturnValueFunction();

    [DllImport("__Internal")]
    private static extern void BindWebGLTexture(int texture);

    void Start() {
        Hello();
        
        HelloString("This is a string.");
        
        float[] myArray = new float[10];
        PrintFloatArray(myArray, myArray.Length);
        
        int result = AddNumbers(5, 7);
        Debug.Log(result);
        
        Debug.Log(StringReturnValueFunction());
        
        var texture = new Texture2D(0, 0, TextureFormat.ARGB32, false);
        BindWebGLTexture(texture.GetNativeTextureID());
    }
}

新方法多了可以返回值,但是每次修改必须打包才能测试;

JavaScript调用Unity

这里面有巨坑,天坑,人都坑傻了!!!

官方文档中有这几行字

image-20211203222807404

恰好我用的2020版本的Unity;

主要使用这个API——

SendMessage("游戏对象名","方法名","参数"); 这个和参数和lua调用c#差不多了,但是怎么调用这个api就很玄学了;

首先如果你调用这个方法需要在Unity的资源已经加载完成才可以,这个好解决,js加个button;

<button Type="button" onclick="TestSend()">WebToUnity</button>

其次在调用这个方法前需要先实例化UnityInstance变量;

var gameInstance = null;
script.onload = () => {
		gameInstance = createUnityInstance(document.querySelector("#unity-canvas"), {
        	dataUrl: "Build/Test.data",
        	frameworkUrl: "Build/Test.framework.js",
        	codeUrl: "Build/Test.wasm",
        	streamingAssetsUrl: "StreamingAssets",
        	companyName: "DefaultCompany",
       		productName: "UnityToWeb",
        	productVersion: "0.1",
      });
};
//以上的参数都可以在unity的playersetting界面找到;

最后调用时要在then中用lamda表达式

function TestSend() {
	gameInstance.then((unityInstance) => {
		unityInstance.SendMessage("Canvas","OnLogin","dqwreqweraf");        
	});
}

完整的index.html

<!DOCTYPE html>
<html lang="en-us">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <title>Unity WebGL Player | UnityToWeb</title>
    <link rel="shortcut icon" href="TemplateData/favicon.ico">
    <link rel="stylesheet" href="TemplateData/style.css">
  </head>
  <body>
    <div id="unity-container" class="unity-desktop">
	<button Type="button" onclick="TestSend()">WebToUnity</button>
      <canvas id="unity-canvas" width=960 height=600></canvas>
      <div id="unity-loading-bar">
        <div id="unity-logo"></div>
        <div id="unity-progress-bar-empty">
          <div id="unity-progress-bar-full"></div>
        </div>
      </div>
      <div id="unity-mobile-warning">
        WebGL builds are not supported on mobile devices.
      </div>
      <div id="unity-footer">
        <div id="unity-webgl-logo"></div>
        <div id="unity-fullscreen-button"></div>
        <div id="unity-build-title">UnityToWeb</div>
      </div>
    </div>
    <script>
      var buildUrl = "Build";
      var loaderUrl = buildUrl + "/Test.loader.js";
      var config = {
        dataUrl: buildUrl + "/Test.data",
        frameworkUrl: buildUrl + "/Test.framework.js",
        codeUrl: buildUrl + "/Test.wasm",
        streamingAssetsUrl: "StreamingAssets",
        companyName: "DefaultCompany",
        productName: "UnityToWeb",
        productVersion: "0.1",
      };

      var container = document.querySelector("#unity-container");
      var canvas = document.querySelector("#unity-canvas");
      var loadingBar = document.querySelector("#unity-loading-bar");
      var progressBarFull = document.querySelector("#unity-progress-bar-full");
      var fullscreenButton = document.querySelector("#unity-fullscreen-button");
      var mobileWarning = document.querySelector("#unity-mobile-warning");

      // By default Unity keeps WebGL canvas render target size matched with
      // the DOM size of the canvas element (scaled by window.devicePixelRatio)
      // Set this to false if you want to decouple this synchronization from
      // happening inside the engine, and you would instead like to size up
      // the canvas DOM size and WebGL render target sizes yourself.
      // config.matchWebGLToCanvasSize = false;

      if (/iPhone|iPad|iPod|Android/i.test(navigator.userAgent)) {
        container.className = "unity-mobile";
        // Avoid draining fillrate performance on mobile devices,
        // and default/override low DPI mode on mobile browsers.
        config.devicePixelRatio = 1;
        mobileWarning.style.display = "block";
        setTimeout(() => {
          mobileWarning.style.display = "none";
        }, 5000);
      } else {
        canvas.style.width = "960px";
        canvas.style.height = "600px";
      }
      loadingBar.style.display = "block";

      var script = document.createElement("script");
      script.src = loaderUrl;
	  var gameInstance = null;
      script.onload = () => {
		gameInstance = createUnityInstance(document.querySelector("#unity-canvas"), {
         dataUrl: "Build/Test.data",
        frameworkUrl: "Build/Test.framework.js",
        codeUrl: "Build/Test.wasm",
        streamingAssetsUrl: "StreamingAssets",
        companyName: "DefaultCompany",
        productName: "UnityToWeb",
        productVersion: "0.1",
      });
      };
	  
	  function TestSend() {
            gameInstance.then((unityInstance) => {
            unityInstance.SendMessage("Canvas","OnLogin","dqwreqweraf");        
            });
          }
	  
      document.body.appendChild(script);
    </script>
	
  </body>
</html>

更新新坑

Unity和JavaScript代码互相调用会有很严重的时序问题,比如需要在Unity场景加载完成开始调用的登录等方法;

不知道Unity打包成WebGL底层怎么处理的,我在单例的构造里写初始化会被多次执行,如果在这里调用js方法后果就是反复横跳导致栈内存爆炸;

解决办法是在Unity生命周期函数中调用(Awake或者Start)js函数,目的是通知加载完成,然后这个js函数中给Unity传登录信息(这里的登录信息可以是js从浏览器cookies获取的,unity做不到);

Life is too short for so much sorrow.  
posted @ 2021-12-03 22:45  小紫苏  阅读(113)  评论(0编辑  收藏  举报