放一个tauri桌面应用和unity3D场景内部通信的视频
这里这个AI对话框是桌面组件,画面是包含unity游戏的背景窗口,AI对话返回的内容会交给本地tts生成音频,然后再发送音频和文本给unity
费了不小功夫,既然最后做成了,那也还算顺利。
unity本身可以选择Webgl平台,虽然网页渲染能力有限,但它也有优点:可移植性,可以灵活地嵌入其他网页
构建完的项目会生成一个index.html 这是此unity项目的模板网页文件,不能直接用浏览器启动,需要通过本地服务器来启动。
构建好的webgl与外部项目如何通信——
以我当前的Tauri项目为例,我需要在unity里创建的3D场景作为我电脑的壁纸背景,所以我将webgl的index网页插入到外部项目的网页中。
首先填写配置
// 插入形如以下的内容,这些内容是webgl的配置,会自动生成在模板文件中,直接复制过来不要其他部分
var buildUrl = "Build";
var loaderUrl = buildUrl + "/build.loader.js";
var config = {
dataUrl: buildUrl + "/build.data",
frameworkUrl: buildUrl + "/build.framework.js",
codeUrl: buildUrl + "/build.wasm",
streamingAssetsUrl: "StreamingAssets",
companyName: "DefaultCompany",
productName: "项目名",
productVersion: "0.1.0",
};
然后创建并获取实例
let unityInstance = null;
//这是在通过配置创建unity实例的同时获取到实例
createUnityInstance(document.querySelector("#unity-canvas"), config, (progress) => {
}).then((instance) => {
unityInstance = instance;
console.log("Bridge: Unity实例加载完成");
}).catch((message) => {
alert(message);
});
特别注意这个实例的上下文只在此窗口/网页,因此如果别的标签页/窗口想要获取实例或者发送消息则需要和实例所在窗口通信
向unity实例发送消息
unityInstance.SendMessage("Server", "ReceiveTTSData", jsonStr);//这里的这几个参数都是和unity内部的脚本相呼应的而不是照抄我的就行
// 标准语法
unityInstance.SendMessage(objectName, methodName, value);//可以无参数,则不带value,如果是数字参数,可以不带引号,其余一般写为string
// 参数说明:
// objectName: Unity 场景中的 GameObject 名称
// methodName: 该 GameObject 上挂载的脚本中的方法名
// value: 传递给方法的参数(可选,可以是字符串或数字)
unity内部的接收消息的方法
// 我只是在拿我的举例子方便你对照着上面的发送信息理解
public void ReceiveTTSData(string jsonPayload)
{
Debug.Log("Unity: 收到TTS数据");
try
{
TTSPayload payload = JsonUtility.FromJson<TTSPayload>(jsonPayload);//所以我这里也只是把string解析为了json格式而不是传了json格式数据
EnqueuePayload(payload);
}
catch (Exception e)
{
Debug.LogError($"Unity解析TTS数据失败: {e.Message}");
// 如果当前正在播放,不要直接通知结束,因为可能还在播放
if (!isPlaying)
{
NotifyPlaybackComplete();
}
}
}
有关通信,我一开始觉得调用实例麻烦,就在unity内部开了http服务器,结果发现非常慢,也容易出问题,尽管我这个项目在实现通信时好多层到处跑,但也比http快
这里还有个小巧思,unity接收到音频播放完会给外部传一个信号表示播放完了,这样外部会再传个音频进来,来保持队列,但是这个过程会导致像视频里那样的每句间有几秒静默,我修复了这个问题,使unity在音频即将播放完前几秒就提前发送音频结束信号,这样让外部等待提前进入,然后内部保持一个实际最多只有两个对象的队列,上一个播放完下一个播放,这样消除了这个等待静默。
你可能会觉得外面建了队列怎么里面还建队列,这不逻辑上的浪费吗,直接在unity里管理队列不行吗,其实这里面还有一个顺序的问题,就是我虽然第二句和第三句都加入了队列,但是我有可能第三句提前加入,尽管我是按顺序进行的,但是通信这个东西,它就是有可能打乱顺序,导致排到前面,因而外部保持队列排序,内部只建个队列而无需进行顺序逻辑管理。当然也可以多传顺序id参数,但那就真的没必要了。
似乎还有更快的,就是共享内存机制,这样不用传递音频数据,我没有试,目前响应还算可靠,就是webgl导致内存压力很大,看后续怎么优化。
提一嘴对口型,用的是uLipSync,直接适配vrm文件,简直不要太方便
此方悬停