概要
- UI 部分,由
HTML+CSS
实现 - 图形绘制部分,由
Unity
实现 - 【通信方向:
Web 应用
=>WebGL项目(iframe组件)
=>Unity
】下拉框切换方块颜色 - 【通信方向:
Unity
=>WebGL项目(iframe组件)
=>Web 应用
】场景加载完成通知、物体聚焦/失焦
Demo 预览
初始化 Unity 项目
新建项目
替换案例场景
添加物体至场景,并附上 tag
脚本绑定
相机
- 拖拽场景,相机基于方块物体,进行环绕运动
using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using UnityEngine;
public class MouseDragger : MonoBehaviour
{
public Transform target; // 获取跟随目标(围绕中心)
public float speed = 10; // 自动环绕速度
private void Update()
{
cameraRotate();
cameraZoom();
}
// 左键:手动环绕
private void cameraRotate() //摄像机围绕目标旋转操作
{
transform.RotateAround(target.position, Vector3.up, speed * Time.deltaTime); // 自动环绕目标
var mouse_x = Input.GetAxis("Mouse X"); // 获取鼠标 X 轴移动
var mouse_y = -Input.GetAxis("Mouse Y"); // 获取鼠标 Y 轴移动
if (Input.GetKey(KeyCode.Mouse0))
{
transform.RotateAround(target.transform.position, Vector3.up, mouse_x * 5);
transform.RotateAround(target.transform.position, transform.right, mouse_y * 5);
}
}
// 滚轮:缩放
private void cameraZoom()
{
if (Input.GetAxis("Mouse ScrollWheel") > 0)
{
transform.Translate(Vector3.forward * 0.5f);
}
if (Input.GetAxis("Mouse ScrollWheel") < 0)
{
transform.Translate(Vector3.forward * -0.5f);
}
}
}
- 光线投射,判断物体是否获取焦点
using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using UnityEngine;
public class RayDetection : MonoBehaviour
{
// 事件:聚焦,射线命中对应 tag 的物体
[DllImport("__Internal")]
private static extern void FocusEventUnity(string objectName, string mousePosition);
// 事件:失焦
[DllImport("__Internal")]
private static extern void UnfocusEventUnity();
private UnityEngine.Ray ray;
private RaycastHit hit;
private string nameObject; // 用于节流,减少事件触发次数
private bool isFocus = false; // 用于节流,减少事件触发次数
private float widthScreen;
private float heightScreen;
private void Start()
{
getSizeScreen();
}
private void Update()
{
rayJudgment();
}
// 获取视窗尺寸
private void getSizeScreen()
{
widthScreen = UnityEngine.Screen.width;
heightScreen = UnityEngine.Screen.height;
}
// 获取鼠标位置
private string getMousePosition()
{
Vector3 mousePosition = Input.mousePosition;
string res = mousePosition.x + "," + (heightScreen - mousePosition.y);
return res;
}
// 鼠标悬浮:光线投射到场景,判断是否穿过物体
private void rayJudgment()
{
ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out hit, 100) && hit.collider.tag == "target")
{
if (nameObject != hit.collider.name)
{
isFocus = true;
nameObject = hit.collider.name;
string mousePosition = getMousePosition();
Debug.Log(mousePosition);
#if UNITY_WEBGL
FocusEventUnity(hit.collider.name, mousePosition);
#endif
}
}
else
{
if (isFocus)
{
isFocus = false;
nameObject = null;
#if UNITY_WEBGL
UnfocusEventUnity();
#endif
}
}
}
}
- 判断 Unity 是否加载完成
using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using UnityEngine;
public class LoadDetection : MonoBehaviour
{
// 事件:场景加载完成
[DllImport("__Internal")]
private static extern void LoadedEventUnity();
void Start()
{
#if UNITY_WEBGL
LoadedEventUnity();
#endif
}
}
方块(切换颜色)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ChangeMaterial : MonoBehaviour
{
private void ChangeMaterialColor(string colorKey)
{
Dictionary<string, Color> colorDictionary = new Dictionary<string, Color>();
colorDictionary.Add("red", Color.red);
colorDictionary.Add("green", Color.green);
colorDictionary.Add("blue", Color.blue);
gameObject.GetComponent<Renderer>().material.color = colorDictionary[colorKey];
}
}
新建 Plugins 目录,定义 jslib
作用:提供内部接口,便于 Unity 调用 JavaScript 接口
var param = {
LoadedEventUnity: function () {
window.LoadedEventUnity();
},
FocusEventUnity: function (objectName, mousePosition) {
window.FocusEventUnity(Pointer_stringify(objectName), Pointer_stringify(mousePosition));
},
UnfocusEventUnity: function () {
window.UnfocusEventUnity();
}
}
mergeInto(LibraryManager.library, param);
构建 WebGL 项目
创建构建目录
- Dist:Unity 构建输出
- index.css:
Web 应用
样式 - index.html:
Web 应用
入口 - JSBridge.js:
Unity
与WebGL项目(iframe组件)
的通信接口定义 - unityWebGL.css:
WebGL项目(iframe组件)
样式
输出构建内容
清理无用文件(TemplateData 目录)
编辑代码 WebGL项目(iframe组件)
入口(Dist/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 | VueUnityCommunication</title>
<link rel="stylesheet" href="../unityWebGL.css">
<script src="./Build/UnityLoader.js"></script>
<script src="../JSBridge.js"></script>
</head>
<body>
<div class="webgl-content">
<div id="unityContainer"></div>
</div>
</body>
</html>
样式(unityWebGL.css)
body {
margin: 0;
}
#unityContainer {
background: none !important;
visibility: hidden;
}
JSBridge.js
const unityInstance = UnityLoader.instantiate("unityContainer", "Build/Dist.json")
const parentWindow = window.parent
// 隐藏 Unity 个人版欢迎 Brand(也可以在加载期间,显示自定义的 html loading 组件)
window.LoadedEventUnity = () => {
document.querySelector('#unityContainer').style.visibility = 'visible'
}
// 调用父级页面 showObjectInfo 函数,显示物体信息
window.FocusEventUnity = (objectName, mousePosition) => {
parentWindow.showObjectInfoCard(objectName, mousePosition)
}
// 调用父级页面 hideObjectInfo 函数,隐藏物体信息
window.UnfocusEventUnity = () => {
parentWindow.hideObjectInfoCard()
}
window.ChangeMaterialColor = colorKey => {
unityInstance.SendMessage('Cube', 'ChangeMaterialColor', colorKey)
}
编辑代码 Web 应用
入口(index.html)
<iframe> src 替换为对应的域
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="./index.css">
</head>
<body>
<iframe
id="iframeUnity"
src="http://192.168.123.218:8000/Dist/"
width="960"
scrolling="no"
frameborder="0"
onload="this.height = this.contentWindow.document.querySelector('canvas').scrollHeight"
></iframe>
<select id="colorPicker">
<option value="">默认</option>
<option value="red">红</option>
<option value="green">绿</option>
<option value="blue">蓝</option>
</select>
<div id="infoCard"></div>
<script>
window.showObjectInfoCard = (objectName, mousePosition) => {
const nodeInfoCard = document.querySelector('#infoCard')
const listPos = mousePosition.split(',')
const x = listPos[0]
const y = listPos[1]
nodeInfoCard.style.top = `${y}px`
nodeInfoCard.style.left = `${x}px`
console.log(x, y)
nodeInfoCard.style.visibility = 'visible'
nodeInfoCard.innerText = objectName
}
window.hideObjectInfoCard = () => {
const nodeInfoCard = document.querySelector('#infoCard')
nodeInfoCard.style.visibility = 'hidden'
nodeInfoCard.innerText = ''
}
// 调用子级页面 ChangeMaterialColor 函数,更改方块颜色
document.querySelector("#colorPicker").addEventListener("change", function () {
window.frames[0].ChangeMaterialColor(this.value)
})
</script>
</body>
</html>
样式(index.css)
body {
position: relative;
}
#iframeUnity {
background-color: #ccc;
}
#colorPicker {
margin: 16px;
position: absolute;
top: 0;
left: 0;
}
#infoCard {
padding: 16px;
position: absolute;
background-color: #ccc;
visibility: hidden;
}
运行 Demo
此处使用基于 nodejs 的静态服务器启动工具
anywhere
cd VueUnityCommunication/Build
anywhere