2021-09-29 Unity WebGL平台开发遇到的坑

内容简介:最近在用Unity做一个 WebGL 平台的项目,开发过程中遇到了各种各样的坑,这里简单记录一下,以免以后再踩。首先是Http请求的问题,我最开始想的是,直接用C#里的写法,编辑器里测试毫无问题,但是一打包出来就不行,会报出错误的示范0:

本文转载自:Unity WebGL平台开发遇到的坑 | 叉叉白,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有。

最近在用Unity做一个 WebGL 平台的项目,开发过程中遇到了各种各样的坑,这里简单记录一下,以免以后再踩。

WebRequest请求(异步的问题)

首先是Http请求的问题,我最开始想的是,直接用C#里的写法,编辑器里测试毫无问题,但是一打包出来就不行,会报出 SystemException: Thread creation failed. 的错误,无奈只能用Unity自己的 UnityWebRequest 。

错误的示范0:

public void WrongGet()
{
    using (HttpClient httpClient = new HttpClient())
    {
        var text = httpClient.GetStringAsync("https://www.baidu.com/艹").Result;
        Debug.Log(text);
    }
}

错误的示范1:

public void WrongGet()
{
    using (WebClient webClient = new WebClient())
    {
        var text = webClient.DownloadString("https://www.baidu.com/艹");
        Debug.Log(text);
    }
}

这样写后果会比上面这种更严重,整个程序直接崩溃

2021-09-29 Unity WebGL平台开发遇到的坑_第1张图片

正确的示范:

private IEnumerator Get()
{
    using (var webRequest = UnityWebRequest.Get("https://www.baidu.com/艹"))
    {
        yield return webRequest.SendWebRequest();
        if (!webRequest.isNetworkError && !webRequest.isHttpError)
            Debug.Log(webRequest.downloadHandler.text);
    }
}

感觉这是异步的问题,因为js是单线程的。

如果确定要打包WebGL平台,就不要用异步,用协程比较稳,另外,尽量用Unity自己的那一套东西

SSL证书

这个不知道是什么鬼问题,Unity编辑器不支持ECC证书?还是其他什么问题,测试是在我的 服务器 上测试的,我的服务器配的是ECC证书,最开始在我的电脑上是不支持的,一直报错,后来不知道发生了什么,竟然莫名奇妙的支持了,这件事也就网了,但是后面代码合并的时候,到我同事的电脑上,又出现了这个问题,没办法,只能暂时先用http顶着了,后面再说申请RSA证书的事。

到后面打包出来测试的时候,又出问题了,里面的资源已经数据请求,都是用的http协议,但是我最后访问页面的时候,用的是https协议,https页面里,会阻断不安全的http请求,没办法,RSA证书的事提上日程,先搞定。

Unity Editor中发起https请求,如果后端是RSA证书没问题,后端是ECC证书可能会出问题

JSON

接下来是 JSON 序列化和反序列化,一提到JSON,最先想到的是 JSON.NET库 ,也就是 Newtonsoft.Json 库。用这个没毛病,从nuget上下载下来,然后把dll文件拖进去,OK。在编辑器里一切都没问题,但是一打包成网页就出问题。

什么问题呢?写一段正常的代码

string json = "json字符串";
Dictionary dic = JsonConvert.DeserializeObject>(json);

这样没毛病,一切OK,但是要按照下面这样写,就会踩雷

public class Dic
{
    public int code { get; set; }
    public Dictionary data { get; set; }

    public static Dic DeDic(string json)
    {
        return JsonConvert.DeserializeObject(json);
    }
}

如果是按照上面这样写,恭喜你,打包出来的东西肯定报错,反序列化得到的全都是null。

而且网上搜索的话,很难搜到原因,貌似遇到这个问题的人并不多?没办法,求着后端小哥哥帮忙改接口,把接口全都写成 key-value 的形式就可以用了。

为什么会出现这个问题?在原本都已经放弃的时候,无意中发现了这个插件 JSON.NET FOR UNITY ,在这个插件的介绍里找到了答案

While many in the Unity Community have succeeded in getting JSON .NET to work for their games, it has never worked properly with iOS or IL2CPP. The iOS errors are due to incompatibilities with AOT (Ahead of Time Compilation) that is used by Mono/IL2CPP in iOS.

大致的意思就是,JSON.NET在iSO平台上因为ATO的问题不能直接用,我想WebGL应该也是类似的问题吧,看着这个插件的支持平台上,写了WebGL,抱着试一试的想法尝试了一下,还真的可以,行得通,这个坑算是解决了,反序列化Json不能直接用JSON.NET库,要用Unity商店里的那个插件。

在Unity里,非桌面平台,尽量少用nuget仓库里的东西,如果要用,也要先确保Unity不同平台的支持情况,尽量用Unity商店里的插件,因为Unity商店里的插件一般都会注明平台支持情况

跨域

相比其他问题,这个问题已经不能叫问题了,只要稍微有点Web开发经要就能解决,因为所有请求都是从js里发出的,所以很容易遇到跨域问题,只要加上对应的返回头就可以了 Access-Control-Allow-Origin 。

Unity打包WebGL平台文件时,要尤其注意跨域问题,一定要提前和后端讲明

AB包

相比之下,AB包就没什么坑了,中规中矩,常规用法就可以了,建议按场景打包,虽然会有重复资源,但是操作简单啊,如果自己手动分包的话,麻烦的要死。

唯一要注意的一点就是IL2CPP编译时代码剥离的问题,这一点随便一百度,都会有很多教程告诉你怎么做。

音频

我们使用了网络音频,所以要注意网络音频的格式,打包出来的网页网络音频要用wav格式的,而在客户端可以使用ogg格式。

private IEnumerator PlayWebAudio(string url, float d)
{
#if UNITY_EDITOR || !UNITY_WEBGL
    using (var www = UnityWebRequestMultimedia.GetAudioClip(url, AudioType.OGGVORBIS))
#elif UNITY_WEBGL
    using (var www = UnityWebRequestMultimedia.GetAudioClip(url, AudioType.WAV))
#endif
    {
        yield return www.SendWebRequest();
        yield return new WaitForSeconds(d);
        if (www.isNetworkError || www.isHttpError)
        {
            Debug.Log(www.error);
        }
        else
        {
            var clip = DownloadHandlerAudioClip.GetContent(www);
            PlayAudioClip(clip);
        }
    }
}

Unity在使用网络资源的时候,要注意音频格式,不同平台的支持情况不一样,不确定的时候就用wav格式吧

程序内与网页传参

这个看了半天也没搞懂真正的用法,只能写个简单点的函数

先说明一下,能百度到的东西,很多都是过时的,而且大家都是你抄我,他抄你,抄来抄去,没啥意思。我找了一圈,就发现一篇是比较实用的,而且写得也挺易懂的 链接

最好的 工具 是Unity的官方文档,这个文档写的真不错,很多方法都有很详细的示例 文档

因为以前在UE4上也研究过这个东西,所以对 Emscripten 也有一定的了解。看一下文档就大致该知道怎么做了。

首先是在 Plugins目录下 创建一个 .jslib 文件,名字叫什么都无所谓,然后在里面写一些函数,尽量简单一点,越简单越好,函数的形式可以参考官方文档,要注意,官方文档给的这几个示例函数都是比较具有代表性的,基本上你能用到的东西,都已经告诉你该怎么处理了,传参,返回值,资源处理,该有的都有了。

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) + i]);
  },

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

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

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

在需要调用的时候,这样写

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());
    }
}

这个示例,能看懂的人肯定是一看就懂,看不懂的估计还要再补一下基础知识,仿照这个示例,咱们自己来写一个

jslib:

mergeInto(LibraryManager.library, {
  GetUrlParam: function (str) {
    var paraName = Pointer_stringify(str);
    var result = GetUrlParamFun(paraName);
    var bufferSize = lengthBytesUTF8(result) + 1;
    var buffer = _malloc(bufferSize);
    stringToUTF8(result, buffer, bufferSize);
    return buffer;
  },
});

简单分析一下,有个方法叫 GetUrlParam ,带有一个参数,已经确定需要传字符串进来,所以需要使用 Pointer_stringify() 方法,注意传参是将内容写入内存,使用指针去指的所以要用 Pointer_stringify ,再下面,调用一个叫 GetUrlParamFun 的函数,获取返回值,再后面就是将结果返回给Unity,也是将结果写入内存,然后用指针去指。(我指针理解的不够透彻,上面这段话难免会有一些问题)

需要在网页中插入一段js:

function GetUrlParamFun(paraName) {
    var url = document.location.toString();
    var arrObj = url.split("?");
        if (arrObj.length > 1) {
            var arrPara = arrObj[1].split("&");
            var arr;
            arr = arrPara[i].split("=");
            if (arr != null && arr[0] == paraName) {
                return decodeURIComponent(arr[1]);
            }
        }
        return "";
    }
    else {
        return "";
    }
}

上面这一段代码是用来获取页面url中的参数的

C#脚本中写

using UnityEngine;
#if !UNITY_EDITOR && UNITY_WEBGL
using System.Runtime.InteropServices;
#endif

public class XXXX : MonoBehaviour
{
#if !UNITY_EDITOR && UNITY_WEBGL
    [DllImport("__Internal")] private static extern string GetUrlParam(string str);
#endif
    private void Start()
    {
#if !UNITY_EDITOR && UNITY_WEBGL
    	string from = GetUrlParam("from");
    	Debug.Log(from);
#endif
    }
}

这样就会打印出来我们访问地址参数form,例如访问 http://localhost?id=0&from=kkkkkk ,会打印出来 kkkkkk

Unity调用JS代码,一点都不难,只要按照官方文档走就可以了,Unity的生态真的非常好,对新手友好,吐槽一下辣鸡UE4,文档乱的像坨翔



本文来源:码农网
本文链接:https://www.codercto.com/a/72962.html

 

你可能感兴趣的:(Unity,C#,unity3d,webgl)