项目中有将UE程序发布为像素流供前端调用的需求,写篇推文记录一下。
-
UE准备工作
按照官方教程中的步骤一步步操作
其中有几个坑点
1️⃣ 如果我们是测试的话可以不用打包UE程序
2️⃣ 克隆信令服务器代码和第一次运行信令服务器的时候需要科学上网
3️⃣ 信令服务器代码不能放到非ntfs格式硬盘中,否则会报错???(别问我怎么知道的🌚)
4️⃣Windows系统下运行,cmd管理员权限进入
SignallingWebServer\platform_scripts\cmd 运行run_local.bat即可
-
Vue准备工作
npm install @epicgames-ps/lib-pixelstreamingfrontend-ue5.3 npm install @epicgames-ps/lib-pixelstreamingfrontend-ui-ue5.3封装一个拉取UE像素流的组件ue.vue
javascript<template> <div id="container"> <!-- 点击进入 --> <div id="play"> <div className="flex flex-col"> <!-- 加载框 --> <span className="loader"></span> <span className="mt-4 ml-3">点击进入</span> </div> </div> </div> </template> <script setup> import { Config, ControlSchemeType, Flags, Logger, PixelStreaming, TextParameters, } from "@epicgames-ps/lib-pixelstreamingfrontend-ue5.3"; import { Application, PixelStreamingApplicationStyle, UIElementCreationMode, } from "@epicgames-ps/lib-pixelstreamingfrontend-ui-ue5.3"; import {onMounted} from "vue"; // 定义emit方法 const emit = defineEmits(["ready"]); // 创建像素流样式 const style = new PixelStreamingApplicationStyle(); style.applyStyleSheet(); // 设置日志等级 Logger.SetLoggerVerbosity(1); // 创建配置对象 const config = new Config({useUrlParams: false}); config.setFlagEnabled(Flags.AutoPlayVideo, true); config.setFlagEnabled(Flags.AutoConnect, true); // 配置像素流地址 config.setTextSetting( TextParameters.SignallingServerUrl, "ws://127.0.0.1:8090" ); // 配置鼠标控制方式 config.setFlagEnabled(Flags.HoveringMouseMode, ControlSchemeType.HoveringMouse); // 配置开启鼠标事件 config.setFlagEnabled(Flags.MouseInput, true); // 配置开启键盘事件 config.setFlagEnabled(Flags.KeyboardInput, true); // 配置开启触屏事件 config.setFlagEnabled(Flags.TouchInput, true); // 配置关闭手柄事件 config.setFlagEnabled(Flags.GamepadInput, false); // 配置关闭XR事件 config.setFlagEnabled(Flags.XRControllerInput, false); config.setFlagEnabled(Flags.BrowserSendOffer, false); config.setFlagEnabled(Flags.MatchViewportResolution, true); // 创建像素流对象 const stream = new PixelStreaming(config); onMounted(() => { // 设置浏览器拒绝自动播放时触发的事件 stream.addEventListener("playStreamRejected", (data) => { const play = document.getElementById("play"); play.className = "visible"; play.onclick = () => { stream.play(); play.className = ""; play.onclick = undefined; // 通知父组件准备就绪 emit("ready"); }; }); // 设置UE端消息回调 stream.addResponseEventListener("handle_responses", (response) => { console.log(response); }); // 设置store里的流对象 window.streamsss = stream; // 创建应用程序 const application = new Application({ stream, // 关闭设置面板 settingsPanelConfig: { isEnabled: false, visibilityButtonConfig: { creationMode: UIElementCreationMode.Disable, }, }, // 关闭状态面板 statsPanelConfig: { isEnabled: false, visibilityButtonConfig: { creationMode: UIElementCreationMode.Disable, }, }, // 关闭全屏按钮 fullScreenControlsConfig: { creationMode: UIElementCreationMode.Disable, }, // 关闭XR控制按钮 xrControlsConfig: { creationMode: UIElementCreationMode.Disable, }, // 关闭信号指示 videoQpIndicatorConfig: { disableIndicator: true, }, }); // 挂载到指定元素 document.getElementById("container").appendChild(application.rootElement); }); </script> <style lang="scss" scoped> #container { width: 100vw; height: 100vh; } /* 播放按钮 */ #play { position: absolute; top: 0; left: 0; width: 100%; height: 100%; display: none; font-size: 24px; z-index: 999; /* 低于30则会展示像素流组件的默认播放按钮 */ } #ue-btn { position: absolute; right: 20px; bottom: 20px; z-index: 1001; padding: 8px 12px; background: rgba(0, 0, 0, 0.6); color: #fff; border: none; border-radius: 4px; cursor: pointer; } #ue-btn:hover { background: rgba(0, 0, 0, 0.8); } #play.visible { display: flex; align-items: center; justify-content: center; background-color: black; color: white; letter-spacing: 8px; cursor: pointer; font-size: 14px; } /* 加载动画 */ .loader { width: 48px; height: 78px; position: relative; box-sizing: border-box; border: 2px solid #fff; margin: auto; border-radius: 50% 50% 50% 50% / 25% 25% 25% 25%; } .loader::before { content: ""; position: absolute; left: 50%; top: 20px; transform: translateX(-50%); width: 4px; height: 4px; background: #fff; border-radius: 10px; animation: scrollDown 1.5s linear infinite; } @keyframes scrollDown { 0% { top: 15px; height: 4px; opacity: 1; } 33% { top: 15px; height: 40px; } 66% { top: 50px; height: 10px; opacity: 1; } 100% { top: 56px; height: 4px; opacity: 0; } } </style>然后是index页面 也就是首页
javascript<template> <div class="container"> <!-- UE像素流核心容器 --> <div class="ues-container"> <ues></ues> </div> <!-- 发送UI交互给UE的按钮 --> <button class="ueBtn" @click="sendUIInteraction">发送 UI 交互</button> </div> </template> <script setup> // 仅引入UE组件(确认路径正确,若在src/components下则用@/components/ue.vue) import ues from "@/components/ue.vue"; // 发送UI交互给UE的方法(仅保留信令服务器配置,移除MQTT) const sendUIInteraction = () => { // UE像素流信令服务器配置, const payload = { server: "http://127.0.0.1:8888" }; // 调用UE流对象发送交互 if (window.streamsss) { window.streamsss.emitUIInteraction(payload); console.log("已发送UI交互消息给UE(仅信令服务器配置):", payload); } else { console.error("UE流对象未加载!请检查像素流连接"); } }; </script> <style scoped> .container { width: 100vw; height: 100vh; position: relative; margin: 0; padding: 0; overflow: hidden; } .ues-container { width: 100%; height: 100%; position: absolute; top: 0; left: 0; z-index: 1; } .ueBtn { position: absolute; right: 20px; bottom: 20px; background: #ffffff; padding: 10px 20px; border: 1px solid #cccccc; border-radius: 4px; cursor: pointer; z-index: 20; } .ueBtn:hover { background: #f5f5f5; } </style>