OpenZI Vue 概述
OpenZI是一套高性能的三维可视化引擎,专为Web端打造。openzi-vue是其Vue集成包,提供了在Vue应用中快速集成OpenZI功能的能力,支持高性能WebRTC实时渲染、丰富的场景API和便捷的Vue组件集成。
高性能WebRTC
支持低延迟实时渲染,提供流畅的三维交互体验
丰富的场景API
关卡加载、坐标转换、相机控制等全方位场景操作能力
便捷的Vue集成
专为Vue 3设计,提供简洁的组件接口和完整的类型支持
安装指南
安装依赖
npm install openzi-vue
yarn add openzi-vue
快速开始
在您的Vue项目中引入并使用openzi-vue组件:
import { createApp } from 'vue'
import App from './App.vue'
import OpenZIVue from 'openzi-vue'
const app = createApp(App)
app.use(OpenZIVue)
app.mount('#app')
核心组件
OpenZIViewer
OpenZIViewer 是整个库的核心组件,负责与OpenZI引擎建立连接并渲染3D场景。
组件属性
| 属性名 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| ueIp | String | 无 | UE服务器IP地址,必填项 |
| uePort | Number | 8080 | UE服务器端口 |
| peerConnectionOptions | Object | {} | WebRTC连接配置选项 |
| startVideoMuted | Boolean | false | 是否静音启动视频 |
| autoPlayAudio | Boolean | true | 是否自动播放音频 |
组件事件
| 事件名 | 参数 | 说明 |
|---|---|---|
| mapLoaded | data: { OpenZIAPI } | 地图加载完成时触发,返回OpenZIAPI实例 |
| onConnected | 无 | 连接成功时触发 |
| onDisconnected | 无 | 断开连接时触发 |
| onError | error: Error | 发生错误时触发 |
API 参考
场景API (sceneApi)
sceneApi 提供了一系列操作3D场景的方法,用于控制场景、相机、关卡等元素。
初始化与基础操作
init(api)
初始化场景API,必须在使用其他API前调用。
参数
- api - OpenZIAPI实例
返回值
无
import { sceneApi } from 'openzi-vue'
// 地图加载完成后初始化
function onMapLoaded(data) {
const OpenZIAPI = data?.OpenZIAPI
if (OpenZIAPI) {
sceneApi.init(OpenZIAPI)
}
}
changeCoodinate(jsondata)
切换场景坐标系。
参数
- jsondata - 坐标系配置对象
返回值
Promise
await sceneApi.changeCoodinate({
"coordinateOrigin": "117.4572,39.021101,140.402447",
"projectionCoordinateSystem": "EPSG:4326",
"planetShape": 1,
"GISType": 0,
"originOffset": "0,0,0",
"scale": 100
})
loadLevel(levelName, removeOthersLevel, bHidden)
加载指定关卡。
参数
- levelName - 关卡名称
- removeOthersLevel - 是否移除其他关卡
- bHidden - 是否隐藏关卡
返回值
Promise
// 加载主要关卡并移除其他关卡
await sceneApi.loadLevel('TJ01_46', true)
// 加载天空场景但不移除其他关卡
await sceneApi.loadLevel('LV_skycreator', false)
相机控制
getCameraInfo()
获取当前相机信息。
参数
无
返回值
Promise,解析为相机信息对象
sceneApi.getCameraInfo().then(cameraInfo => {
console.log('当前相机信息:', cameraInfo)
})
changeCamera(cameraInfo)
设置相机位置和角度。
参数
- cameraInfo - 相机信息对象
返回值
Promise
sceneApi.changeCamera({
"cameraMode": "rts",
"redirectionOrigin": false,
"gisType": 0,
"coordinates": {"X": 117.203776, "Y": 39.136764, "Z": 48.320563},
"pitch": -79.25,
"yaw": 180,
"moveSpeed": 100,
"rotationSpeed": 180,
"zoomSpeed": 100
})
视频站与POI管理
addVideos()
添加视频站。
参数
无
返回值
Promise
sceneApi.addVideos().then(result => {
console.log('添加视频站成功:', result)
})
clearPOI()
清除所有POI点。
参数
无
返回值
Promise
sceneApi.clearPOI().then(result => {
console.log('清除POI成功:', result)
})
镜头漫游
addCameraPlay()
添加镜头漫游。
参数
无
返回值
Promise
sceneApi.addCameraPlay().then(result => {
console.log('添加镜头漫游成功:', result)
})
startCameraPlay()
开始镜头漫游。
参数
无
返回值
Promise
sceneApi.startCameraPlay().then(result => {
console.log('开始漫游成功:', result)
})
pauseCameraPlay()
暂停镜头漫游。
参数
无
返回值
Promise
sceneApi.pauseCameraPlay().then(result => {
console.log('暂停漫游成功:', result)
})
levelUtils 工具
levelUtils 提供了一系列工具方法,用于处理关卡数据和相机配置。
fetchLevelData()
获取关卡配置数据。
参数
无
返回值
Promise<Array> - 关卡配置数据数组
import { levelUtils } from 'openzi-vue'
const { fetchLevelData } = levelUtils
async function loadLevelData() {
const data = await fetchLevelData()
console.log('关卡数据:', data)
}
findLevelByName(levelData, name)
根据名称查找关卡配置。
参数
- levelData - 关卡数据数组
- name - 关卡名称
返回值
Object|null - 找到的关卡配置对象或null
import { levelUtils } from 'openzi-vue'
const { fetchLevelData, findLevelByName } = levelUtils
async function getLevelConfig() {
const data = await fetchLevelData()
const level = findLevelByName(data, '耳闸')
console.log('找到关卡:', level)
}
loadLevelWithChildren(sceneApi, levelConfig)
加载关卡及其子节点。
参数
- sceneApi - sceneApi实例
- levelConfig - 关卡配置对象
返回值
Promise<void>
import { sceneApi, levelUtils } from 'openzi-vue'
const { fetchLevelData, findLevelByName, loadLevelWithChildren } = levelUtils
async function loadLevel() {
const data = await fetchLevelData()
const level = findLevelByName(data, '耳闸')
if (level) {
await loadLevelWithChildren(level)
console.log('关卡及其子节点加载完成')
}
}
fetchCameraData()
获取相机配置数据。
参数
无
返回值
Promise<Array> - 相机配置数据数组
import { levelUtils } from 'openzi-vue'
const { fetchCameraData } = levelUtils
async function getCameraConfigs() {
const data = await fetchCameraData()
console.log('相机配置:', data)
}
findCameraConfig(cameraData, levelName, cameraName)
根据关卡名称和相机名称查找相机配置。
参数
- cameraData - 相机数据数组
- levelName - 关卡名称
- cameraName - 相机名称
返回值
Object|null - 找到的相机配置对象或null
import { sceneApi, levelUtils } from 'openzi-vue'
const { fetchCameraData, findCameraConfig } = levelUtils
async function setCameraView() {
const data = await fetchCameraData()
const cameraConfig = findCameraConfig(data, '耳闸', '耳闸-入口-中景')
if (cameraConfig) {
await sceneApi.changeCamera(cameraConfig)
console.log('相机视角设置完成')
}
}
OpenZIAPI 类
OpenZIAPI 类是与OpenZI引擎通信的核心类,负责WebSocket连接和消息传递。
构造函数
import { OpenZILab } from 'openzi-vue'
// 创建OpenZILab实例
const openZILab = new OpenZILab('ws://192.168.18.224:8080')
Call(cName, fName, data, callback)
调用OpenZI引擎中的方法。
参数
- cName - 类名
- fName - 方法名
- data - 参数数据
- callback - 回调函数
openZILab.Call(
'SceneManager',
'StartPlay',
{ "playbackSpeed": 1.0 },
(data) => {
console.log('播放开始:', data)
}
)
destroy()
销毁连接。
参数
无
// 组件卸载时销毁连接
openZILab.destroy()
使用示例
以下是一个完整的使用示例,展示了如何集成和使用openzi-vue,包含组件初始化、场景配置、关卡加载和API操作等功能:
<template>
<div class="container">
<div class="config-panel">
<div class="config-item">
<label for="ueIp">UE服务器IP:</label>
<input id="ueIp" v-model="config.ueIp" type="text" placeholder="输入UE服务器IP">
</div>
<button class="tech-btn primary" @click="applyConfig">应用配置</button>
</div>
<div class="component-container" v-if="showComponent">
<open-z-i-viewer @mapLoaded="onMapLoaded" />
</div>
<div class="api-panel">
<div class="api-section">
<h3>关卡管理</h3>
<div class="button-group">
<button class="tech-btn primary" @click="loadSingleLevel('耳闸', '耳闸-入口-中景')" :disabled="!apiReady">加载耳闸关卡</button>
<button class="tech-btn secondary" @click="loadSingleLevel('二道闸','二道闸-上游-近景')" :disabled="!apiReady">加载二道闸关卡</button>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, reactive } from 'vue';
// 从openzi-vue包导入OpenZIViewer sceneApi和levelUtils
import { sceneApi, levelUtils, OpenZIViewer } from 'openzi-vue';
// 解构levelUtils工具方法
const { fetchCameraData, fetchLevelData, findLevelByName, findCameraConfig, loadLevelWithChildren } = levelUtils;
// 组件配置
const config = reactive({
ueIp: '192.168.18.224',
levelConfigUrl: '/configs/map/levelData.json'
});
// 状态变量
const showComponent = ref(false);
const apiReady = ref(false);
let OpenZIAPI = null;
// 应用配置
function applyConfig() {
if (!config.ueIp) {
console.error('请输入有效的UE服务器IP地址');
return;
}
window.ueIp = config.ueIp; // 将UE服务器IP赋值给全局变量
showComponent.value = false;
setTimeout(() => {
showComponent.value = true;
}, 100);
}
// 地图加载完成回调
async function onMapLoaded(data) {
console.log('地图加载完成');
// 获取OpenZIAPI实例
OpenZIAPI = data?.OpenZIAPI;
if (OpenZIAPI) {
apiReady.value = true;
// 初始化场景通用工具API
sceneApi.init(OpenZIAPI);
try {
// 1 设置默认坐标系
await sceneApi.changeCoodinate({
"coordinateOrigin": "117.4572,39.021101,140.402447",
"projectionCoordinateSystem": "EPSG:4326",
"planetShape": 1,
"GISType": 0,
"originOffset": "0,0,0",
"scale": 100
});
// 2 设置默认帧率
sceneApi.changeFps(30);
// 3 加载基础关卡
await loadLevels();
// 4 设置默认视角
setDefaultCameraView();
} catch (error) {
console.error(`初始化设置出错: ${error.message}`);
}
}
// 加载基础关卡函数
async function loadLevels() {
try {
// 加载主要关卡
await sceneApi.loadLevel('TJ01_46', true);
// 加载天空场景
await sceneApi.loadLevel('LV_skycreator', false);
// 加载特效
sceneApi.loadLevel('BP_TEXIAO', false);
} catch (error) {
console.error(`加载关卡时出错: ${error.message}`);
}
}
// 设置默认视角函数
function setDefaultCameraView() {
try {
// 设置RTS模式的默认视角
const cameraInfo = {
"cameraMode": "rts",
"redirectionOrigin": false,
"gisType": 0,
"coordinates": {"X": 117.20377616700722, "Y": 39.136763510147595, "Z": 48.32056263554841},
"pitch": -79.25000000000016,
"pitchRange": {"X": -90, "Y": -5},
"yaw": 179.99999999999966,
"yawRange": {"X": -180, "Y": 180},
"moveSpeed": 100,
"rotationSpeed": 180,
"zoomSpeed": 100
};
// 调用设置相机信息的API
sceneApi.changeCamera(cameraInfo)
.then(() => {
console.log('默认视角设置完成');
})
.catch(error => {
console.error(`默认视角设置失败: ${error.message}`);
});
} catch (error) {
console.error(`设置默认视角时出错: ${error.message}`);
}
}
}
// 加载单关卡并设置指定视角
async function loadSingleLevel(name, cameraName = '默认', weaterName = 'Weather_Clear_02', time = 17) {
try {
// 0 检查参数是否有效
if (!name) {
console.error('加载单关卡时缺少必要参数');
return;
}
console.log(`开始加载关卡: ${name}`);
// 1 修改天气
sceneApi.changeWeather(weaterName);
console.log(`天气已设置为: ${weaterName}`);
// 2 修改时间
sceneApi.changeHour(time);
console.log(`时间已设置为: ${time}时`);
// 3 动态获取关卡配置数据
const levelData = await fetchLevelData();
const targetLevel = findLevelByName(levelData, name);
if (!targetLevel) {
console.warn(`未找到关卡${name}的配置信息`);
// 尝试直接使用提供的名称加载
try {
await sceneApi.loadLevel(name, false);
console.log(`关卡${name}加载完成`);
} catch (error) {
console.error(`加载关卡${name}失败: ${error.message}`);
}
return;
}
// 4 加载指定关卡及其子节点
try {
await loadLevelWithChildren(targetLevel);
console.log(`关卡${targetLevel.title}加载完成`);
} catch (error) {
console.error(`加载关卡${targetLevel.title}失败: ${error.message}`);
return;
}
// 5 动态获取相机配置数据并应用
if (cameraName) {
const cameraData = await fetchCameraData();
const targetCameraInfo = findCameraConfig(cameraData, name, cameraName);
if (!targetCameraInfo) {
console.warn(`未找到关卡${name}对应的相机配置${cameraName || ''},使用默认视角`);
} else {
// 应用相机视角设置
try {
await sceneApi.changeCamera(targetCameraInfo);
console.log(`视角${cameraName}设置完成`);
} catch (error) {
console.error(`设置视角失败: ${error.message}`);
}
}
}
} catch (error) {
console.error(`加载关卡${name}时出错: ${error.message}`);
}
}
</script>
<style scoped>
.container {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
.component-container {
width: 100%;
height: 600px;
border: 1px solid #ddd;
overflow: hidden;
background-color: #000;
}
/* 确保open-z-i-viewer组件适应容器尺寸 */
.component-container >>> open-z-i-viewer {
width: 100%;
height: 100%;
display: block;
}
</style>
兼容性
浏览器支持
- Chrome 80+
- Firefox 78+
- Safari 14+
- Edge 80+
系统要求
- 操作系统:Windows 10+, macOS 11+, Linux (主流发行版)
- 处理器:双核以上CPU
- 内存:4GB以上
- 网络:推荐稳定的网络连接,带宽不低于2Mbps