Unity之webgl端通过vue3接入腾讯云联络中心SDK

腾讯云联络中心SDK:云联络中心 Web-SDK 开发指南-文档中心-腾讯云 (tencent.com)

1 首先下载Demo

1.1 对其进行解压

1.2根据文档操作

查看README.md,根据说明设置server下的dev.js里的相关参数。

然后打开电脑终端,cd到项目的路径:

安装依赖

运行

1.3 运行demo

复制http://127.0.0.1:5173/在浏览器里输入,这时候会显示如下画面:

输入电话号码,点击拨打就会把电话打出去。

2 在Unity端的操作

2.1 创建Unity工程

新建一个Unity工程,在Assets/Plugins/WebGl下创建一个后缀为jslib的文件,记事本打开编写脚本如下:

javascript 复制代码
mergeInto(LibraryManager.library, {
	ReportReady: function () {
		window.ReportReady()
	},
         
        TellPhone:function(typeName, phone){
             SendPhone(UTF8ToString(typeName), UTF8ToString(phone))
        }

});

2.2 编写挂载对象UIPanel上的脚本

cs 复制代码
using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using UnityEngine;
using UnityEngine.UI;
using TMPro;

public class UIPanel : MonoBehaviour
{ 
    [DllImport("__Internal")]
    private static extern string ReportReady();
    [DllImport("__Internal")]
    private static extern string TellPhone(string type,string phone);
    public TextMeshProUGUI text;
    public TMP_InputField inputField;
    void Start()
    {
        ReportReady();//向vue报告脚本初始化完成
    }
    public void OpenPhone()
    {
        TellPhone("tellphone",inputField.text);
    }
    public void receiveMsgFromVue(string token) {
        text.text = token;
        Debug.Log("接受来自vue的消息 == " + token);
    }
}

2.3 Unity的UI界面

2.4最后打包webgl的包

放在tccc-demo-vue\src\路径下,如下图所示:

2.5改写index.html

打开index.html:

SendPhone是Unity发送给网页的方法,sendMsgToUnity方法是网页发送个Unity的方法。

index.html完整代码如下:

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 | Web731</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">
      <canvas id="unity-canvas" width=1920 height=1080></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-warning"> </div>
      <div id="unity-footer">
        <div id="unity-webgl-logo"></div>
        <div id="unity-fullscreen-button"></div>
        <div id="unity-build-title">Web731</div>
      </div>
    </div>
    <script>
      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 warningBanner = document.querySelector("#unity-warning");

      // Shows a temporary message banner/ribbon for a few seconds, or
      // a permanent error message on top of the canvas if type=='error'.
      // If type=='warning', a yellow highlight color is used.
      // Modify or remove this function to customize the visually presented
      // way that non-critical warnings and error messages are presented to the
      // user.
      function unityShowBanner(msg, type) {
        function updateBannerVisibility() {
          warningBanner.style.display = warningBanner.children.length ? 'block' : 'none';
        }
        var div = document.createElement('div');
        div.innerHTML = msg;
        warningBanner.appendChild(div);
        if (type == 'error') div.style = 'background: red; padding: 10px;';
        else {
          if (type == 'warning') div.style = 'background: yellow; padding: 10px;';
          setTimeout(function() {
            warningBanner.removeChild(div);
            updateBannerVisibility();
          }, 5000);
        }
        updateBannerVisibility();
      }

      var buildUrl = "Build";
      var loaderUrl = buildUrl + "/web0803.loader.js";
      var config = {
        dataUrl: buildUrl + "/web0803.data.unityweb",
        frameworkUrl: buildUrl + "/web0803.framework.js.unityweb",
        codeUrl: buildUrl + "/web0803.wasm.unityweb",
        streamingAssetsUrl: "StreamingAssets",
        companyName: "DefaultCompany",
        productName: "Web731",
        productVersion: "0.1",
        showBanner: unityShowBanner,
      };

      // 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;
        unityShowBanner('WebGL builds are not supported on mobile devices.');
      } else {
        canvas.style.width = "1920px";
        canvas.style.height = "1080px";
      }
      loadingBar.style.display = "block";

      var script = document.createElement("script");
      script.src = loaderUrl;
      script.onload = () => {
        createUnityInstance(canvas, config, (progress) => {
          progressBarFull.style.width = 100 * progress + "%";
        }).then((unityInstance) => {
          loadingBar.style.display = "none";
          fullscreenButton.onclick = () => {
            unityInstance.SetFullscreen(1);
          };
          unityInstanceV = unityInstance;
        }).catch((message) => {
          alert(message);
        });
      };
      document.body.appendChild(script);

  var unityInstanceV;
  function ReportReady() {
    window.parent.postMessage({guid:"",event:"ReportReady"}, "*");
  }
  function SendPhone(_type,_phone)
  {
    // alert(s);
    if (_type == "tellphone"){
      window.parent.postMessage({guid:"",event:_type,phone:_phone}, "*");
    }else {
      window.parent.postMessage({guid:_type,event:"guid"}, "*");
    }
  }

  function sendMsgToUnity(obj) {
    unityInstanceV.SendMessage('UIPanel','receiveMsgFromVue',JSON.stringify(obj))
  }

    </script>
  </body>
</html>

2.6 修改Container.vue脚本

增加和Unity交互的方法

把原先显示的界面代码删除掉<div class="container"> </div>

style 部分也删掉

对Vue不熟悉,我的理解是这样的(理解不对请留言指正)

其中

javascript 复制代码
onMounted(()=>{
        window.addEventListener('message', unityWatch,true)
    }) 

是事件,对Unity发送来的消息进行监听。

javascript 复制代码
function  vueSendToUnity(){
      console.log(statusMap[status.value])
      unityIframe.value.contentWindow.sendMsgToUnity({userId:'****',状态:status.value|| '加载中...'})  
}

是vue把消息发送到Unity端。

javascript 复制代码
<template>
  <div >
<iframe id="iframe" ref="unityIframe" src="/src/unity/index.html" style="width:100%;height:100vh" frameborder="0" scrolling="auto" /> 
  </div>
</template>

是Unity部分进行显示(其中stytle的height:100% 不起作用,有知道的请留言,谢谢,所以我改为了height:100vh)。

Container.vue修改后代码如下:

javascript 复制代码
<script setup>
import { ref, onMounted } from 'vue'
const wechatGroupImg = 'https://tccc.qcloud.com/assets/wechatGroup.png';
const arrowImg = 'https://tccc.qcloud.com/assets/arrow.png';

const seat = ref('')
const status = ref('')
const number = ref('')
const loading = ref(false)
const isError = ref(false)
const errorField = ref('')

const statusMap = {
  offline: '已下线',
  disconnect: '网络断开,重连中',
  free: '空闲中',
  busy: '忙碌中',
  rest: '小休中',
  countdown: '话后倒计时',
  arrange: '话后整理中',
  notReady: '示忙中',
}

const errorFieldMap = {
  'InvalidParameterValue.InstanceNotExist': 'sdkAppId',
  'InvalidParameterValue.AccountNotExist': 'userId',
  'AuthFailure.SignatureFailure': 'secretKey或secretId',
  'AuthFailure.SecretIdNotFound': 'secretId',
};

const injectTCCC = ({ token, sdkAppId, userId, sdkUrl }) => {
  const scriptDom = document.createElement('script')
  scriptDom.setAttribute('crossorigin', 'anonymous')
  scriptDom.dataset.token = token
  scriptDom.dataset.sdkAppId = sdkAppId
  scriptDom.dataset.userid = userId
  scriptDom.src = sdkUrl
  document.body.appendChild(scriptDom)
  scriptDom.addEventListener('load', () => {
    // ready事件必须监听,否则容易发生tccc不存在的错误,所有呼入呼出的逻辑必须在ready事件触发后才可以调用
    window.tccc.on('ready', () => {
      // 以下为Demo逻辑,非业务必须。业务代码主要实现都在这个部分
      const statusVal = window.tccc.Agent.getStatus()
      status.value = statusVal;
      seat.value = userId;
    })
    // 以下为Demo逻辑,非接入必须
    setInterval(() => {
      const statusVal = window.tccc.Agent.getStatus()
      status.value = statusVal;
    }, 200)
  })
}

onMounted(() => {
  // 获取Token的方法必须在页面初始化时第一优先级调用
  fetch('/loginTCCC')
    .then((res) => res.json())
    .then((res) => {
      // 以下为Demo逻辑,需要替换为业务逻辑
      if (res.code) {
        if (res.type) {
          isError.value = true;
          errorField.value = errorFieldMap[res.code]
        } else {
          isError.value = true;
          if (errorFieldMap[res.code]) {
            errorField.value = errorFieldMap[res.code]
          } else {
            alert(res.errMsg);
          }
          return;
        }
      }
      // 调用成功后才可以开始执行TCCC的注入
      injectTCCC({
        token: res.token,
        userId: res.userId,
        sdkUrl: res.sdkUrl,
        sdkAppId: res.sdkAppId,
      })
    })
    .catch((error) => {
      console.error(`获取Token失败:${error.message}`)
    })
})

const handleCallout = async () => {
  if (loading.value) {
    return
  }
  loading.value = true
  // 调用呼出方法的核心代码
  try {
    await window.tccc.Call.startOutboundCall({ phoneNumber: number.value })
  } catch (error) {
    console.error(`呼出失败:${error.message}`)
  } finally {
    loading.value = false
  }
}


 onMounted(()=>{
        window.addEventListener('message', unityWatch,true)
    }) 
  function unityWatch(e){
      console.log('unityWatch方法调用 e==' + e.data.guid + '    event=' + e.data.event)
      if(e.data.event=='tellphone'){
            handleCalloutByUnity(e.data.phone)  
            vueSendToUnity()  
      }
} 
  //Unity端调用vue里的打电话功能
const handleCalloutByUnity = async (phone) => {
  if (loading.value) {
    return
  }
  loading.value = true
  // 调用呼出方法的核心代码
  try {
    await window.tccc.Call.startOutboundCall({ phoneNumber: phone })
  } catch (error) {
    console.error(`呼出失败:${error.message}`)
  } finally {
    loading.value = false
  }
}

const unityIframe = ref('unityIframe')
function  vueSendToUnity(){
      console.log(statusMap[status.value])
      unityIframe.value.contentWindow.sendMsgToUnity({userId:'****',状态:status.value|| '加载中...'})      
}

</script>

<template>
  <div >
<iframe id="iframe" ref="unityIframe" src="/src/unity/index.html" style="width:100%;height:100vh" frameborder="0" scrolling="auto" /> 
  </div>
</template>

2.7 测试运行

测试运行时得保证终端npm run dev在运行中

在Unity 的界面上输入手机号点击拨打,电话打了出去,同时Unity端收到了vue发送过来的消息。

2.8 网页内全屏

这时候如果需要Unity在网页内全屏,且不显示滚动条,需要打开Unity的index.html进行再次修改:

index.html的修改后如下:

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 | Web731</title>
    <link rel="shortcut icon" href="TemplateData/favicon.ico">
    <link rel="stylesheet" href="TemplateData/style.css">
  </head>
  <body>
    <div id="unity-container" style="width: 100%;height:100%">
      <canvas id="unity-canvas" width=auto height=auto></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-warning"> </div>
     
    </div>
    <script>
      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 warningBanner = document.querySelector("#unity-warning");

      // Shows a temporary message banner/ribbon for a few seconds, or
      // a permanent error message on top of the canvas if type=='error'.
      // If type=='warning', a yellow highlight color is used.
      // Modify or remove this function to customize the visually presented
      // way that non-critical warnings and error messages are presented to the
      // user.
      function unityShowBanner(msg, type) {
        function updateBannerVisibility() {
          warningBanner.style.display = warningBanner.children.length ? 'block' : 'none';
        }
        var div = document.createElement('div');
        div.innerHTML = msg;
        warningBanner.appendChild(div);
        if (type == 'error') div.style = 'background: red; padding: 10px;';
        else {
          if (type == 'warning') div.style = 'background: yellow; padding: 10px;';
          setTimeout(function() {
            warningBanner.removeChild(div);
            updateBannerVisibility();
          }, 5000);
        }
        updateBannerVisibility();
      }

      var buildUrl = "Build";
      var loaderUrl = buildUrl + "/web0803.loader.js";
      var config = {
        dataUrl: buildUrl + "/web0803.data.unityweb",
        frameworkUrl: buildUrl + "/web0803.framework.js.unityweb",
        codeUrl: buildUrl + "/web0803.wasm.unityweb",
        streamingAssetsUrl: "StreamingAssets",
        companyName: "DefaultCompany",
        productName: "Web731",
        productVersion: "0.1",
        showBanner: unityShowBanner,
      };

      // 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)) {
        var meta = document.createElement('meta');
           meta.name = 'viewport';
           meta.content = 'width=device-width, height=device-height, initial-scale=1.0, user-scalable=no, shrink-to-fit=yes';
           document.getElementsByTagName('head')[0].appendChild(meta);
           container.className = "unity-mobile";
         
           canvas.style.width = window.innerWidth + 'px';
           canvas.style.height = window.innerHeight + 'px';


       unityShowBanner('暂不支持移动端...');
     } else {	  
      canvas.style.height= document.documentElement.clientHeight+"px";
      canvas.style.width = document.documentElement.clientWidth+"px"; 	  
     }
      loadingBar.style.display = "block";

      var script = document.createElement("script");
      script.src = loaderUrl;
      script.onload = () => {
        createUnityInstance(canvas, config, (progress) => {
          progressBarFull.style.width = 100 * progress + "%";
        }).then((unityInstance) => {
          loadingBar.style.display = "none";
          //fullscreenButton.onclick = () => {
          //  unityInstance.SetFullscreen(1);
          //};
          unityInstanceV = unityInstance;
        }).catch((message) => {
          alert(message);
        });
      };
      document.body.appendChild(script);

  var unityInstanceV;
  function ReportReady() {
    window.parent.postMessage({guid:"",event:"ReportReady"}, "*");
  }
  function SendPhone(_type,_phone)
  {
    // alert(s);
    if (_type == "tellphone"){
      window.parent.postMessage({guid:"",event:_type,phone:_phone}, "*");
    }else {
      window.parent.postMessage({guid:_type,event:"guid"}, "*");
    }
  }

  function sendMsgToUnity(obj) {
    unityInstanceV.SendMessage('UIPanel','receiveMsgFromVue',JSON.stringify(obj))
  }

    </script>
  </body>
</html>

打开Unity\TemplateData路径下的style.css增加:

css 复制代码
html,body{width:100%;height:100%;margin:0;padding:0;overflow:hidden;}
.webgl-content{width: 100%; height: 100%;}
.unityContainer{width: 100%; height: 100%;}

style.css完整脚本如下:

css 复制代码
body { padding: 0; margin: 0 }
#unity-container { position: absolute }
#unity-container.unity-desktop { left: 50%; top: 50%; transform: translate(-50%, -50%) }
#unity-container.unity-mobile { width: 100%; height: 100% }
#unity-canvas { background: #231F20 }
.unity-mobile #unity-canvas { width: 100%; height: 100% }
#unity-loading-bar { position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); display: none }
#unity-logo { width: 154px; height: 130px; background: url('unity-logo-dark.png') no-repeat center }
#unity-progress-bar-empty { width: 141px; height: 18px; margin-top: 10px; background: url('progress-bar-empty-dark.png') no-repeat center }
#unity-progress-bar-full { width: 0%; height: 18px; margin-top: 10px; background: url('progress-bar-full-dark.png') no-repeat center }
#unity-footer { position: relative }
.unity-mobile #unity-footer { display: none }
#unity-webgl-logo { float:left; width: 204px; height: 38px; background: url('webgl-logo.png') no-repeat center }
#unity-build-title { float: right; margin-right: 10px; line-height: 38px; font-family: arial; font-size: 18px }
#unity-fullscreen-button { float: right; width: 38px; height: 38px; background: url('fullscreen-button.png') no-repeat center }
#unity-warning { position: absolute; left: 50%; top: 5%; transform: translate(-50%); background: white; padding: 10px; display: none }


html,body{width:100%;height:100%;margin:0;padding:0;overflow:hidden;}
.webgl-content{width: 100%; height: 100%;}
.unityContainer{width: 100%; height: 100%;}

但是vue的这个右侧还是有滚动条,还没找到隐藏的方法,有知道的同学请留言,谢谢。

相关推荐
90后小陈老师12 小时前
Unity教学 项目2 2D闯关游戏
游戏·unity·游戏引擎
噗噗夹的TA之旅13 小时前
Unity Shader 学习20:URP LitForwardPass PBR 解析
学习·unity·游戏引擎·图形渲染·技术美术
nnsix13 小时前
Unity ReferenceFinder插件 多选资源查找bug解决
unity·游戏引擎·bug
gzroy14 小时前
Unity Shader Graph实现全息瞄准器
unity·游戏引擎
90后小陈老师17 小时前
Unity教学 基础介绍
unity·游戏引擎
90后小陈老师17 小时前
Unity教学 项目3 3D坦克大战
3d·unity·游戏引擎
秦奈19 小时前
Unity复习学习随笔(五):Unity基础
学习·unity·游戏引擎
nnsix20 小时前
Unity ReferenceFinder插件 窗口中选择资源时 同步选择Assets下的资源
java·unity·游戏引擎
二狗哈21 小时前
Cesium快速入门21:Primitive材质类型与设置
3d·webgl·材质·cesium·地图可视化
麷飞花21 小时前
unity3d scene窗口选中物体, 在 hierarchy高光显示
unity·editor·unity3d·u3d·hierarchy